Add 'New update available' dialog to the electron version (#299)
* build(tsconfig): Rename root tsconfig.json -> tsconfig.base.json * feat(auto-update): Add update dialog When new update available than new message will visible of the top of the screen with 2 buttons 'Update' and 'Close'. - Update button: Update the application (close and restart) - Close button: Hide the updatePanel * fix(auto-update): Add types to the event methods * style: Add comma after SafeStylePipe import I forgot add the comma when I rebased the branch * fix(auto-update): Use electron-is-dev package to detect dev build I removed the isDev() function from the shared util library because it is electron specific code. * ci: Change osx_image: xcode8.3 Recommended after the last travis upgrade * feat(auto-update): Add auto update settings page and save config save on electron platform * ci: Fix osx image * ci: Upgrade the electron builder -> 19.6.1 The builder now use the 2 package.json structure and build only the necessary dependencies.
This commit is contained in:
committed by
László Monda
parent
2598109f8c
commit
121807a65a
@@ -11,7 +11,7 @@ matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- os: osx
|
||||
|
||||
osx_image: xcode8.3
|
||||
- os: linux
|
||||
env: CC=clang CXX=clang++ npm_config_clang=1
|
||||
compiler: clang
|
||||
|
||||
@@ -67,6 +67,7 @@ import { SvgKeyboardWrapComponent } from './shared/components/svg/wrap';
|
||||
import { appRoutingProviders, routing } from './app/app.routes';
|
||||
import { AppComponent } from './app/app.component';
|
||||
import { MainAppComponent } from './main-app';
|
||||
import { UpdateAvailableComponent } from './components/update-available/update-available.component';
|
||||
|
||||
import { CancelableDirective } from './shared/directives';
|
||||
import { SafeStylePipe } from './shared/pipes';
|
||||
@@ -76,7 +77,8 @@ import { MapperService } from './shared/services/mapper.service';
|
||||
import { SvgModuleProviderService } from './shared/services/svg-module-provider.service';
|
||||
import { UhkDeviceService } from './services/uhk-device.service';
|
||||
|
||||
import { KeymapEffects, MacroEffects, UserConfigEffects} from './shared/store/effects';
|
||||
import { AutoUpdateSettingsEffects, KeymapEffects, MacroEffects, UserConfigEffects } from './shared/store/effects';
|
||||
import { ApplicationEffect, AppUpdateEffect } from './store/effects';
|
||||
|
||||
import { KeymapEditGuard } from './shared/components/keymap/edit';
|
||||
import { MacroNotFoundGuard } from './shared/components/macro/not-found';
|
||||
@@ -88,7 +90,9 @@ import { UhkDeviceUninitializedGuard } from './services/uhk-device-uninitialized
|
||||
import { DATA_STORAGE_REPOSITORY } from './shared/services/datastorage-repository.service';
|
||||
import { ElectronDataStorageRepositoryService } from './services/electron-datastorage-repository.service';
|
||||
import { DefaultUserConfigurationService } from './shared/services/default-user-configuration.service';
|
||||
import { reducer } from '../../shared/src/store/reducers/index';
|
||||
import { AppUpdateRendererService } from './services/app-update-renderer.service';
|
||||
import { reducer } from './store';
|
||||
import { AutoUpdateSettings } from './shared/components/auto-update-settings/auto-update-settings';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -143,7 +147,9 @@ import { reducer } from '../../shared/src/store/reducers/index';
|
||||
PrivilegeCheckerComponent,
|
||||
UhkMessageComponent,
|
||||
CancelableDirective,
|
||||
SafeStylePipe
|
||||
SafeStylePipe,
|
||||
UpdateAvailableComponent,
|
||||
AutoUpdateSettings
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@@ -163,7 +169,10 @@ import { reducer } from '../../shared/src/store/reducers/index';
|
||||
Select2Module,
|
||||
EffectsModule.runAfterBootstrap(KeymapEffects),
|
||||
EffectsModule.runAfterBootstrap(MacroEffects),
|
||||
EffectsModule.runAfterBootstrap(UserConfigEffects)
|
||||
EffectsModule.runAfterBootstrap(UserConfigEffects),
|
||||
EffectsModule.runAfterBootstrap(AutoUpdateSettingsEffects),
|
||||
EffectsModule.run(ApplicationEffect),
|
||||
EffectsModule.run(AppUpdateEffect)
|
||||
],
|
||||
providers: [
|
||||
UhkDeviceConnectedGuard,
|
||||
@@ -177,9 +186,11 @@ import { reducer } from '../../shared/src/store/reducers/index';
|
||||
MacroNotFoundGuard,
|
||||
CaptureService,
|
||||
UhkDeviceService,
|
||||
{provide: DATA_STORAGE_REPOSITORY, useClass: ElectronDataStorageRepositoryService},
|
||||
DefaultUserConfigurationService
|
||||
{ provide: DATA_STORAGE_REPOSITORY, useClass: ElectronDataStorageRepositoryService },
|
||||
DefaultUserConfigurationService,
|
||||
AppUpdateRendererService
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
export class AppModule {
|
||||
}
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
<app-update-available
|
||||
[showUpdateAvailable]="showUpdateAvailable$ | async"
|
||||
(updateApp)="updateApp()"
|
||||
(doNotUpdateApp)="doNotUpdateApp()">
|
||||
</app-update-available>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { Component, ViewEncapsulation } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
|
||||
import { AppState, getShowAppUpdateAvailable } from '../store';
|
||||
import { DoNotUpdateAppAction, UpdateAppAction } from '../store/actions/app-update.action';
|
||||
|
||||
@Component({
|
||||
selector: 'app',
|
||||
@@ -6,4 +11,18 @@ import { Component, ViewEncapsulation } from '@angular/core';
|
||||
styleUrls: ['app.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class AppComponent { }
|
||||
export class AppComponent {
|
||||
showUpdateAvailable$: Observable<boolean>;
|
||||
|
||||
constructor(private store: Store<AppState>) {
|
||||
this.showUpdateAvailable$ = store.select(getShowAppUpdateAvailable);
|
||||
}
|
||||
|
||||
updateApp() {
|
||||
this.store.dispatch(new UpdateAppAction());
|
||||
}
|
||||
|
||||
doNotUpdateApp() {
|
||||
this.store.dispatch(new DoNotUpdateAppAction());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/// <reference path="../../custom_types/sudo-prompt.d.ts"/>
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@@ -13,7 +14,7 @@ import { remote } from 'electron';
|
||||
import * as path from 'path';
|
||||
import * as sudo from 'sudo-prompt';
|
||||
|
||||
import { UhkDeviceService } from './../../services/uhk-device.service';
|
||||
import { UhkDeviceService } from '../../services/uhk-device.service';
|
||||
|
||||
@Component({
|
||||
selector: 'privilege-checker',
|
||||
|
||||
1
electron/src/components/update-available/index.ts
Normal file
1
electron/src/components/update-available/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { UpdateAvailableComponent } from './update-available.component';
|
||||
@@ -0,0 +1,5 @@
|
||||
<div *ngIf="showUpdateAvailable">
|
||||
New version available.
|
||||
<button type="button" (click)="updateApp.emit()" class="btn btn-primary">Update</button>
|
||||
<button type="button" (click)="doNotUpdateApp.emit()" class="btn btn-default">Close</button>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
:host {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-update-available',
|
||||
templateUrl: './update-available.component.html',
|
||||
styleUrls: ['./update-available.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class UpdateAvailableComponent {
|
||||
@Input() showUpdateAvailable: boolean = false;
|
||||
@Output() updateApp = new EventEmitter<null>();
|
||||
@Output() doNotUpdateApp = new EventEmitter<null>();
|
||||
}
|
||||
1
electron/src/custom_types/electron-is-dev.d.ts
vendored
Normal file
1
electron/src/custom_types/electron-is-dev.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare module 'electron-is-dev';
|
||||
22
electron/src/dev-extension.ts
Normal file
22
electron/src/dev-extension.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/// <reference path="./custom_types/electron-is-dev.d.ts"/>
|
||||
|
||||
/*
|
||||
* Install DevTool extensions when Electron is in development mode
|
||||
*/
|
||||
import { app } from 'electron';
|
||||
import * as isDev from 'electron-is-dev';
|
||||
|
||||
if (isDev) {
|
||||
|
||||
app.once('ready', () => {
|
||||
|
||||
const { default: installExtension, REDUX_DEVTOOLS } = require('electron-devtools-installer');
|
||||
|
||||
installExtension(REDUX_DEVTOOLS)
|
||||
.then((name: string) => console.log(`Added Extension: ${name}`))
|
||||
.catch((err: any) => console.log('An error occurred: ', err));
|
||||
|
||||
require('electron-debug')({ showDevTools: true });
|
||||
});
|
||||
|
||||
}
|
||||
@@ -1,10 +1,27 @@
|
||||
import { BrowserWindow, app } from 'electron';
|
||||
/// <reference path="./custom_types/electron-is-dev.d.ts"/>
|
||||
|
||||
import { app, BrowserWindow, ipcMain } from 'electron';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
import * as log from 'electron-log';
|
||||
import * as path from 'path';
|
||||
import { ProgressInfo } from 'electron-builder-http/out/ProgressCallbackTransform';
|
||||
import { VersionInfo } from 'electron-builder-http/out/publishOptions';
|
||||
import * as settings from 'electron-settings';
|
||||
import * as isDev from 'electron-is-dev';
|
||||
|
||||
import { IpcEvents } from './shared/util';
|
||||
import { ElectronDataStorageRepositoryService } from './services/electron-datastorage-repository.service';
|
||||
|
||||
// import './dev-extension';
|
||||
// require('electron-debug')({ showDevTools: true, enabled: true });
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let win: Electron.BrowserWindow;
|
||||
|
||||
log.transports.file.level = 'debug';
|
||||
autoUpdater.logger = log;
|
||||
|
||||
function createWindow() {
|
||||
// Create the browser window.
|
||||
win = new BrowserWindow({
|
||||
@@ -35,6 +52,9 @@ function createWindow() {
|
||||
// when you should delete the corresponding element.
|
||||
win = null;
|
||||
});
|
||||
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
});
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
@@ -51,6 +71,10 @@ app.on('window-all-closed', () => {
|
||||
}
|
||||
});
|
||||
|
||||
app.on('will-quit', () => {
|
||||
saveFirtsRun();
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
@@ -61,3 +85,101 @@ app.on('activate', () => {
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here
|
||||
|
||||
// =========================================================================
|
||||
// Auto update events
|
||||
// =========================================================================
|
||||
function checkForUpdate() {
|
||||
if (isDev) {
|
||||
const msg = 'Application update is not working in dev mode.';
|
||||
log.info(msg);
|
||||
sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFirstRun()) {
|
||||
const msg = 'Application update is skipping at first run.';
|
||||
log.info(msg);
|
||||
sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
autoUpdater.allowPrerelease = allowPreRelease();
|
||||
autoUpdater.checkForUpdates();
|
||||
}
|
||||
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
sendIpcToWindow(IpcEvents.autoUpdater.checkingForUpdate);
|
||||
});
|
||||
|
||||
autoUpdater.on('update-available', (ev: any, info: VersionInfo) => {
|
||||
autoUpdater.downloadUpdate();
|
||||
sendIpcToWindow(IpcEvents.autoUpdater.updateAvailable, info);
|
||||
});
|
||||
|
||||
autoUpdater.on('update-not-available', (ev: any, info: VersionInfo) => {
|
||||
sendIpcToWindow(IpcEvents.autoUpdater.updateNotAvailable, info);
|
||||
});
|
||||
|
||||
autoUpdater.on('error', (ev: any, err: Error) => {
|
||||
sendIpcToWindow(IpcEvents.autoUpdater.autoUpdateError, err);
|
||||
});
|
||||
|
||||
autoUpdater.on('download-progress', (progressObj: ProgressInfo) => {
|
||||
sendIpcToWindow(IpcEvents.autoUpdater.autoUpdateDownloadProgress, progressObj);
|
||||
});
|
||||
|
||||
autoUpdater.on('update-downloaded', (ev: any, info: VersionInfo) => {
|
||||
sendIpcToWindow(IpcEvents.autoUpdater.autoUpdateDownloaded, info);
|
||||
});
|
||||
|
||||
ipcMain.on(IpcEvents.autoUpdater.updateAndRestart, () => autoUpdater.quitAndInstall(true));
|
||||
|
||||
ipcMain.on(IpcEvents.app.appStarted, () => {
|
||||
if (checkForUpdateAtStartup()) {
|
||||
checkForUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(IpcEvents.autoUpdater.checkForUpdate, () => checkForUpdate());
|
||||
|
||||
function isFirstRun() {
|
||||
if (!settings.has('firstRunVersion')) {
|
||||
return true;
|
||||
}
|
||||
const firstRunVersion = settings.get('firstRunVersion');
|
||||
log.info(`firstRunVersion: ${firstRunVersion}`);
|
||||
log.info(`package.version: ${app.getVersion()}`);
|
||||
|
||||
return firstRunVersion !== app.getVersion();
|
||||
}
|
||||
|
||||
function saveFirtsRun() {
|
||||
settings.set('firstRunVersion', app.getVersion());
|
||||
}
|
||||
|
||||
function sendIpcToWindow(message: string, arg?: any) {
|
||||
log.info('sendIpcToWindow:', message, arg);
|
||||
if (!win || win.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
win.webContents.send(message, arg);
|
||||
}
|
||||
|
||||
function allowPreRelease() {
|
||||
const settings = getAutoUpdateSettings();
|
||||
|
||||
return settings && settings.usePreReleaseUpdate;
|
||||
}
|
||||
|
||||
function checkForUpdateAtStartup() {
|
||||
const settings = getAutoUpdateSettings();
|
||||
|
||||
return settings && settings.checkForUpdateOnStartUp;
|
||||
}
|
||||
|
||||
function getAutoUpdateSettings() {
|
||||
const storageService = new ElectronDataStorageRepositoryService();
|
||||
return storageService.getAutoUpdateSettings();
|
||||
}
|
||||
|
||||
20
electron/src/package.json
Normal file
20
electron/src/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "uhk-agent",
|
||||
"main": "electron-main.js",
|
||||
"version": "0.0.0",
|
||||
"description": "Agent is the configuration application of the Ultimate Hacking Keyboard.",
|
||||
"author": "Ultimate Gadget Laboratories",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:UltimateHackingKeyboard/agent.git"
|
||||
},
|
||||
"license": "GPL-3.0",
|
||||
"engines": {
|
||||
"node": ">=6.9.5 <7.0.0",
|
||||
"npm": ">=3.10.7 <4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"usb": "git+https://github.com/aktary/node-usb.git"
|
||||
}
|
||||
}
|
||||
|
||||
75
electron/src/services/app-update-renderer.service.ts
Normal file
75
electron/src/services/app-update-renderer.service.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { Injectable, NgZone } from '@angular/core';
|
||||
import { Action, Store } from '@ngrx/store';
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
import { IpcEvents } from '../shared/util';
|
||||
import { AppState } from '../store';
|
||||
import { UpdateDownloadedAction } from '../store/actions/app-update.action';
|
||||
import { CheckForUpdateFailedAction, CheckForUpdateSuccessAction } from '../shared/store/actions/auto-update-settings';
|
||||
|
||||
/**
|
||||
* This service handle the application update events in the electron renderer process.
|
||||
*
|
||||
* The class contains parameters with 'any' type, because the relevant type definitions in
|
||||
* import { ProgressInfo } from 'electron-builder-http/out/ProgressCallbackTransform';
|
||||
* import { VersionInfo } from 'electron-builder-http/out/publishOptions';
|
||||
* but, typescript allow import these if import 'electron-updater' too, but I i don't want to import
|
||||
* the updater in renderer process.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AppUpdateRendererService {
|
||||
constructor(private store: Store<AppState>,
|
||||
private zone: NgZone) {
|
||||
this.registerEvents();
|
||||
}
|
||||
|
||||
sendAppStarted() {
|
||||
ipcRenderer.send(IpcEvents.app.appStarted);
|
||||
}
|
||||
|
||||
sendUpdateAndRestartApp() {
|
||||
ipcRenderer.send(IpcEvents.autoUpdater.updateAndRestart);
|
||||
}
|
||||
|
||||
checkForUpdate() {
|
||||
ipcRenderer.send(IpcEvents.autoUpdater.checkForUpdate);
|
||||
}
|
||||
|
||||
private registerEvents() {
|
||||
ipcRenderer.on(IpcEvents.autoUpdater.updateAvailable, (event: string, arg: any) => {
|
||||
this.writeUpdateState(IpcEvents.autoUpdater.updateAvailable, arg);
|
||||
});
|
||||
|
||||
ipcRenderer.on(IpcEvents.autoUpdater.updateNotAvailable, () => {
|
||||
this.writeUpdateState(IpcEvents.autoUpdater.updateNotAvailable);
|
||||
this.dispachStoreAction(new CheckForUpdateSuccessAction('No update available'));
|
||||
});
|
||||
|
||||
ipcRenderer.on(IpcEvents.autoUpdater.autoUpdateError, (event: string, arg: any) => {
|
||||
this.writeUpdateState(IpcEvents.autoUpdater.autoUpdateError, arg);
|
||||
this.dispachStoreAction(new CheckForUpdateFailedAction(arg));
|
||||
});
|
||||
|
||||
ipcRenderer.on(IpcEvents.autoUpdater.autoUpdateDownloadProgress, (event: string, arg: any) => {
|
||||
this.writeUpdateState(IpcEvents.autoUpdater.autoUpdateDownloadProgress, arg);
|
||||
});
|
||||
|
||||
ipcRenderer.on(IpcEvents.autoUpdater.autoUpdateDownloaded, (event: string, arg: any) => {
|
||||
this.writeUpdateState(IpcEvents.autoUpdater.autoUpdateDownloaded, arg);
|
||||
this.dispachStoreAction(new UpdateDownloadedAction());
|
||||
});
|
||||
|
||||
ipcRenderer.on(IpcEvents.autoUpdater.checkForUpdateNotAvailable, (event: string, arg: any) => {
|
||||
this.writeUpdateState(IpcEvents.autoUpdater.checkForUpdateNotAvailable, arg);
|
||||
this.dispachStoreAction(new CheckForUpdateFailedAction(arg));
|
||||
});
|
||||
}
|
||||
|
||||
private dispachStoreAction(action: Action) {
|
||||
this.zone.run(() => this.store.dispatch(action));
|
||||
}
|
||||
|
||||
private writeUpdateState(event: any, arg?: any) {
|
||||
console.log({ event, arg });
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,36 @@
|
||||
import { UserConfiguration } from '../shared/config-serializer/config-items/UserConfiguration';
|
||||
import * as storage from 'electron-settings';
|
||||
|
||||
export class ElectronDataStorageRepositoryService {
|
||||
getConfig(): UserConfiguration {
|
||||
// TODO implement load logic
|
||||
return;
|
||||
import { UserConfiguration } from '../shared/config-serializer/config-items/UserConfiguration';
|
||||
import { DataStorageRepositoryService } from '../shared/services/datastorage-repository.service';
|
||||
import { AutoUpdateSettings } from '../shared/models/auto-update-settings';
|
||||
|
||||
export class ElectronDataStorageRepositoryService implements DataStorageRepositoryService {
|
||||
static getValue(key: string): any {
|
||||
const value = storage.get(key);
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(<string>value);
|
||||
}
|
||||
|
||||
static saveValue(key: string, value: any) {
|
||||
storage.set(key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
getConfig(): UserConfiguration {
|
||||
return ElectronDataStorageRepositoryService.getValue('user-config');
|
||||
}
|
||||
|
||||
/* tslint:disable:no-unused-variable */
|
||||
saveConfig(config: UserConfiguration): void {
|
||||
// TODO implement save logic
|
||||
ElectronDataStorageRepositoryService.saveValue('user-config', config.toJsonObject());
|
||||
}
|
||||
|
||||
getAutoUpdateSettings(): AutoUpdateSettings {
|
||||
return ElectronDataStorageRepositoryService.getValue('auto-update-settings');
|
||||
}
|
||||
|
||||
saveAutoUpdateSettings(settings: AutoUpdateSettings): void {
|
||||
ElectronDataStorageRepositoryService.saveValue('auto-update-settings', settings);
|
||||
}
|
||||
}
|
||||
|
||||
40
electron/src/store/actions/app-update.action.ts
Normal file
40
electron/src/store/actions/app-update.action.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { type } from '../../shared/util/';
|
||||
|
||||
const PREFIX = '[app-update] ';
|
||||
|
||||
// tslint:disable-next-line:variable-name
|
||||
export const ActionTypes = {
|
||||
UPDATE_AVAILABLE: type(PREFIX + 'update available'),
|
||||
UPDATE_APP: type(PREFIX + 'update app'),
|
||||
DO_NOT_UPDATE_APP: type(PREFIX + 'do not update app'),
|
||||
UPDATE_DOWNLOADED: type(PREFIX + 'update downloaded'),
|
||||
UPDATING: type(PREFIX + 'updating')
|
||||
};
|
||||
|
||||
export class UpdateAvailableAction implements Action {
|
||||
type = ActionTypes.UPDATE_AVAILABLE;
|
||||
}
|
||||
|
||||
export class UpdateAppAction implements Action {
|
||||
type = ActionTypes.UPDATE_APP;
|
||||
}
|
||||
|
||||
export class DoNotUpdateAppAction implements Action {
|
||||
type = ActionTypes.DO_NOT_UPDATE_APP;
|
||||
}
|
||||
|
||||
export class UpdateDownloadedAction implements Action {
|
||||
type = ActionTypes.UPDATE_DOWNLOADED;
|
||||
}
|
||||
|
||||
export class UpdatingAction implements Action {
|
||||
type = ActionTypes.UPDATING;
|
||||
}
|
||||
|
||||
export type Actions
|
||||
= UpdateAvailableAction
|
||||
| UpdateAppAction
|
||||
| DoNotUpdateAppAction
|
||||
| UpdateDownloadedAction
|
||||
| UpdatingAction;
|
||||
22
electron/src/store/actions/app.action.ts
Normal file
22
electron/src/store/actions/app.action.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { type } from '../../shared/util/';
|
||||
|
||||
const PREFIX = '[app] ';
|
||||
|
||||
// tslint:disable-next-line:variable-name
|
||||
export const ActionTypes = {
|
||||
APP_BOOTSRAPPED: type(PREFIX + 'bootstrapped'),
|
||||
APP_STARTED: type(PREFIX + 'started')
|
||||
};
|
||||
|
||||
export class AppBootsrappedAction implements Action {
|
||||
type = ActionTypes.APP_BOOTSRAPPED;
|
||||
}
|
||||
|
||||
export class AppStartedAction implements Action {
|
||||
type = ActionTypes.APP_STARTED;
|
||||
}
|
||||
|
||||
export type Actions
|
||||
= AppStartedAction
|
||||
| AppBootsrappedAction;
|
||||
32
electron/src/store/effects/app-update.effect.ts
Normal file
32
electron/src/store/effects/app-update.effect.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Action } from '@ngrx/store';
|
||||
import { Actions, Effect } from '@ngrx/effects';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/first';
|
||||
|
||||
import { ActionTypes } from '../actions/app-update.action';
|
||||
import { ActionTypes as AutoUpdateActionTypes } from '../../shared/store/actions/auto-update-settings';
|
||||
import { AppUpdateRendererService } from '../../services/app-update-renderer.service';
|
||||
|
||||
@Injectable()
|
||||
export class AppUpdateEffect {
|
||||
@Effect({ dispatch: false })
|
||||
appStart$: Observable<Action> = this.actions$
|
||||
.ofType(ActionTypes.UPDATE_APP)
|
||||
.first()
|
||||
.do(() => {
|
||||
this.appUpdateRendererService.sendUpdateAndRestartApp();
|
||||
});
|
||||
|
||||
@Effect({ dispatch: false }) checkForUpdate$: Observable<Action> = this.actions$
|
||||
.ofType(AutoUpdateActionTypes.CHECK_FOR_UPDATE_NOW)
|
||||
.do(() => {
|
||||
this.appUpdateRendererService.checkForUpdate();
|
||||
});
|
||||
|
||||
constructor(private actions$: Actions,
|
||||
private appUpdateRendererService: AppUpdateRendererService) {
|
||||
}
|
||||
|
||||
}
|
||||
24
electron/src/store/effects/app.effect.ts
Normal file
24
electron/src/store/effects/app.effect.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Action } from '@ngrx/store';
|
||||
import { Effect, Actions } from '@ngrx/effects';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/do';
|
||||
|
||||
import * as app from '../actions/app.action';
|
||||
import { AppUpdateRendererService } from '../../services/app-update-renderer.service';
|
||||
|
||||
@Injectable()
|
||||
export class ApplicationEffect {
|
||||
@Effect()
|
||||
appStart$: Observable<Action> = this.actions$
|
||||
.ofType(app.ActionTypes.APP_BOOTSRAPPED)
|
||||
.startWith(new app.AppStartedAction())
|
||||
.delay(3000) // wait 3 sec to mainRenderer subscribe all events
|
||||
.do(() => {
|
||||
this.appUpdateRendererService.sendAppStarted();
|
||||
});
|
||||
|
||||
constructor(
|
||||
private actions$: Actions,
|
||||
private appUpdateRendererService: AppUpdateRendererService) { }
|
||||
}
|
||||
2
electron/src/store/effects/index.ts
Normal file
2
electron/src/store/effects/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { AppUpdateEffect } from './app-update.effect';
|
||||
export { ApplicationEffect } from './app.effect';
|
||||
41
electron/src/store/index.ts
Normal file
41
electron/src/store/index.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/// <reference path="../custom_types/electron-is-dev.d.ts"/>
|
||||
import { createSelector } from 'reselect';
|
||||
import { compose } from '@ngrx/core/compose';
|
||||
import { storeFreeze } from 'ngrx-store-freeze';
|
||||
import { ActionReducer, combineReducers } from '@ngrx/store';
|
||||
import { routerReducer } from '@ngrx/router-store';
|
||||
import * as isDev from 'electron-is-dev';
|
||||
|
||||
import { AppState as CommonState } from '../shared/store';
|
||||
import * as fromApp from './reducers/app.reducer';
|
||||
import * as fromAppUpdate from './reducers/app-update.reducer';
|
||||
import { autoUpdateReducer, presetReducer, userConfigurationReducer } from '../shared/store/reducers';
|
||||
|
||||
export interface AppState extends CommonState {
|
||||
app: fromApp.State;
|
||||
appUpdate: fromAppUpdate.State;
|
||||
}
|
||||
|
||||
const reducers = {
|
||||
userConfiguration: userConfigurationReducer,
|
||||
presetKeymaps: presetReducer,
|
||||
router: routerReducer,
|
||||
app: fromApp.reducer,
|
||||
appUpdate: fromAppUpdate.reducer,
|
||||
autoUpdateSettings: autoUpdateReducer
|
||||
};
|
||||
|
||||
const developmentReducer: ActionReducer<AppState> = compose(storeFreeze, combineReducers)(reducers);
|
||||
const productionReducer: ActionReducer<AppState> = combineReducers(reducers);
|
||||
|
||||
export function reducer(state: any, action: any) {
|
||||
if (isDev) {
|
||||
return developmentReducer(state, action);
|
||||
} else {
|
||||
return productionReducer(state, action);
|
||||
}
|
||||
}
|
||||
|
||||
export const appUpdateState = (state: AppState) => state.appUpdate;
|
||||
|
||||
export const getShowAppUpdateAvailable = createSelector(appUpdateState, fromAppUpdate.getShowAppUpdateAvailable);
|
||||
37
electron/src/store/reducers/app-update.reducer.ts
Normal file
37
electron/src/store/reducers/app-update.reducer.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Actions, ActionTypes } from '../actions/app-update.action';
|
||||
|
||||
export interface State {
|
||||
updateAvailable: boolean;
|
||||
updateDownloaded: boolean;
|
||||
doNotUpdateApp: boolean;
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
updateAvailable: false,
|
||||
updateDownloaded: false,
|
||||
doNotUpdateApp: false
|
||||
};
|
||||
|
||||
export function reducer(state = initialState, action: Actions) {
|
||||
switch (action.type) {
|
||||
case ActionTypes.UPDATE_AVAILABLE: {
|
||||
const newState = Object.assign({}, state);
|
||||
newState.updateAvailable = true;
|
||||
return newState;
|
||||
}
|
||||
case ActionTypes.UPDATE_DOWNLOADED: {
|
||||
const newState = Object.assign({}, state);
|
||||
newState.updateDownloaded = true;
|
||||
return newState;
|
||||
}
|
||||
case ActionTypes.DO_NOT_UPDATE_APP: {
|
||||
const newState = Object.assign({}, state);
|
||||
newState.doNotUpdateApp = true;
|
||||
return newState;
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export const getShowAppUpdateAvailable = (state: State) => state.updateDownloaded && !state.doNotUpdateApp;
|
||||
21
electron/src/store/reducers/app.reducer.ts
Normal file
21
electron/src/store/reducers/app.reducer.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Actions, ActionTypes } from '../actions/app.action';
|
||||
|
||||
export interface State {
|
||||
started: boolean;
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
started: false
|
||||
};
|
||||
|
||||
export function reducer(state = initialState, action: Actions) {
|
||||
switch (action.type) {
|
||||
case ActionTypes.APP_STARTED: {
|
||||
const newState = Object.assign({}, state);
|
||||
newState.started = true;
|
||||
return newState;
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [
|
||||
"electron-main.ts"
|
||||
]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"exclude": [
|
||||
"../dist",
|
||||
"electron-main.ts",
|
||||
"webpack.config.*"
|
||||
]
|
||||
|
||||
@@ -80,6 +80,10 @@ module.exports = {
|
||||
{
|
||||
from: 'node_modules/usb',
|
||||
to: 'vendor/usb'
|
||||
},
|
||||
{
|
||||
from: 'electron/src/package.json',
|
||||
to: 'package.json'
|
||||
}
|
||||
]
|
||||
),
|
||||
|
||||
19
package.json
19
package.json
@@ -16,17 +16,23 @@
|
||||
"@ngrx/store-devtools": "3.2.4",
|
||||
"@ngrx/store-log-monitor": "3.0.2",
|
||||
"@types/core-js": "0.9.35",
|
||||
"@types/electron-devtools-installer": "^2.0.2",
|
||||
"@types/electron-settings": "^3.0.0",
|
||||
"@types/file-saver": "0.0.1",
|
||||
"@types/jquery": "3.2.1",
|
||||
"@types/node": "^6.0.78",
|
||||
"@types/usb": "^1.1.3",
|
||||
"angular2-template-loader": "0.6.2",
|
||||
"copy-webpack-plugin": "^4.0.1",
|
||||
"devtron": "^1.4.0",
|
||||
"electron": "1.6.11",
|
||||
"electron-builder": "^19.4.2",
|
||||
"electron-builder": "19.6.1",
|
||||
"electron-debug": "^1.1.0",
|
||||
"electron-devtools-installer": "^2.2.0",
|
||||
"electron-rebuild": "^1.5.7",
|
||||
"expose-loader": "^0.7.1",
|
||||
"html-loader": "0.4.5",
|
||||
"jsonfile": "3.0.0",
|
||||
"node-sass": "^4.5.2",
|
||||
"npm-run-all": "4.0.2",
|
||||
"path": "^0.12.7",
|
||||
@@ -58,12 +64,18 @@
|
||||
"buffer": "^5.0.6",
|
||||
"core-js": "2.4.1",
|
||||
"dragula": "^3.7.2",
|
||||
"electron-is-dev": "0.1.2",
|
||||
"electron-log": "2.2.6",
|
||||
"electron-settings": "3.0.14",
|
||||
"electron-updater": "2.2.0",
|
||||
"filesaver.js": "^0.2.0",
|
||||
"font-awesome": "^4.6.3",
|
||||
"jquery": "3.2.1",
|
||||
"json-loader": "^0.5.4",
|
||||
"ng2-dragula": "1.5.0",
|
||||
"ng2-select2": "1.0.0-beta.10",
|
||||
"ngrx-store-freeze": "^0.1.9",
|
||||
"reselect": "3.0.1",
|
||||
"rxjs": "^5.4.1",
|
||||
"select2": "^4.0.3",
|
||||
"sudo-prompt": "^7.0.0",
|
||||
@@ -76,7 +88,7 @@
|
||||
"postinstall": "run-p build:usb \"symlink -- -i\" ",
|
||||
"test": "cd ./test-serializer && node ./test-serializer.js",
|
||||
"lint": "run-s -scn lint:ts lint:style",
|
||||
"lint:ts": "tslint \"electron/**/*.ts\" \"web/**/*.ts\" \"shared/**/*.ts\" \"test-serializer/**/*.ts\"",
|
||||
"lint:ts": "tslint \"electron/src/**/*.ts\" \"web/src/**/*.ts\" \"shared/**/*.ts\" \"test-serializer/**/*.ts\"",
|
||||
"lint:style": "stylelint \"electron/**/*.scss\" \"web/**/*.scss\" \"shared/**/*.scss\" --syntax scss",
|
||||
"build": "run-p build:web build:electron",
|
||||
"build:web": "webpack --config \"web/src/webpack.config.js\"",
|
||||
@@ -91,6 +103,7 @@
|
||||
"symlink": "node ./tools/symlinker",
|
||||
"standard-version": "standard-version",
|
||||
"pack": "node ./scripts/release.js",
|
||||
"release": "node ./scripts/release.js"
|
||||
"install:build-deps": "cd electron/dist && npm i",
|
||||
"release": "npm run install:build-deps && node ./scripts/release.js"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +1,129 @@
|
||||
'use strict';
|
||||
const jsonfile = require('jsonfile');
|
||||
|
||||
const TEST_BUILD = false; // set true if you would like to test on your local machince
|
||||
|
||||
if (!process.env.CI && !TEST_BUILD) {
|
||||
console.error('Create release only on CI server')
|
||||
process.exit(1)
|
||||
console.error('Create release only on CI server');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let branchName = ''
|
||||
let pullRequestNr = ''
|
||||
let gitTag = ''
|
||||
let repoName = ''
|
||||
let branchName = '';
|
||||
let pullRequestNr = '';
|
||||
let gitTag = '';
|
||||
let repoName = '';
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
branchName = process.env.TRAVIS_BRANCH
|
||||
pullRequestNr = process.env.TRAVIS_PULL_REQUEST
|
||||
gitTag = process.env.TRAVIS_TAG
|
||||
repoName = process.env.TRAVIS_REPO_SLUG
|
||||
branchName = process.env.TRAVIS_BRANCH;
|
||||
pullRequestNr = process.env.TRAVIS_PULL_REQUEST;
|
||||
gitTag = process.env.TRAVIS_TAG;
|
||||
repoName = process.env.TRAVIS_REPO_SLUG;
|
||||
} else if (process.env.APPVEYOR) {
|
||||
branchName = process.env.APPVEYOR_REPO_BRANCH
|
||||
pullRequestNr = process.env.APPVEYOR_PULL_REQUEST_NUMBER
|
||||
gitTag = process.env.APPVEYOR_REPO_TAG_NAME
|
||||
repoName = process.env.APPVEYOR_REPO_NAME
|
||||
branchName = process.env.APPVEYOR_REPO_BRANCH;
|
||||
pullRequestNr = process.env.APPVEYOR_PULL_REQUEST_NUMBER;
|
||||
gitTag = process.env.APPVEYOR_REPO_TAG_NAME;
|
||||
repoName = process.env.APPVEYOR_REPO_NAME;
|
||||
}
|
||||
|
||||
console.log({ branchName, pullRequestNr, gitTag, repoName })
|
||||
console.log({branchName, pullRequestNr, gitTag, repoName});
|
||||
|
||||
// TODO(Robi): Remove the comment after success tests
|
||||
const isReleaseCommit = TEST_BUILD || branchName === gitTag && repoName === 'UltimateHackingKeyboard/agent'
|
||||
const isReleaseCommit = TEST_BUILD || branchName === gitTag && repoName === 'UltimateHackingKeyboard/agent';
|
||||
|
||||
if (!isReleaseCommit) {
|
||||
console.log('It is not a release task. Skipping publish.')
|
||||
console.log('It is not a release task. Skipping publish.');
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
|
||||
const fs = require('fs-extra')
|
||||
const cp = require('child_process')
|
||||
const path = require('path')
|
||||
const builder = require("electron-builder")
|
||||
const Platform = builder.Platform
|
||||
const fs = require('fs-extra');
|
||||
const cp = require('child_process');
|
||||
const path = require('path');
|
||||
const builder = require("electron-builder");
|
||||
const Platform = builder.Platform;
|
||||
const electron_build_folder = path.join(__dirname, '../electron/dist');
|
||||
|
||||
let sha = ''
|
||||
let sha = '';
|
||||
if (process.env.TRAVIS) {
|
||||
sha = process.env.TRAVIS_COMMIT
|
||||
sha = process.env.TRAVIS_COMMIT;
|
||||
} else if (process.env.APPVEYOR) {
|
||||
sha = process.env.APPVEYOR_REPO_COMMIT
|
||||
sha = process.env.APPVEYOR_REPO_COMMIT;
|
||||
}
|
||||
|
||||
let target = ''
|
||||
let target = '';
|
||||
let artifactName = 'UHK.Agent-${version}-${os}';
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
target = Platform.MAC.createTarget()
|
||||
target = Platform.MAC.createTarget();
|
||||
artifactName += '.${ext}';
|
||||
} else if (process.platform === 'win32') {
|
||||
target = Platform.WINDOWS.createTarget()
|
||||
target = Platform.WINDOWS.createTarget();
|
||||
artifactName += '-${arch}.${ext}';
|
||||
} else if (process.platform === 'linux') {
|
||||
target = Platform.LINUX.createTarget()
|
||||
target = Platform.LINUX.createTarget();
|
||||
artifactName += '.${ext}';
|
||||
} else {
|
||||
console.error(`I dunno how to publish a release for ${process.platform} :(`)
|
||||
process.exit(1)
|
||||
console.error(`I dunno how to publish a release for ${process.platform} :(`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
// TODO: Remove comment when macOS certificates boughted and exported
|
||||
//require('./setup-macos-keychain').registerKeyChain()
|
||||
//require('./setup-macos-keychain').registerKeyChain();
|
||||
}
|
||||
|
||||
let version = ''
|
||||
let version = '';
|
||||
if (TEST_BUILD || gitTag) {
|
||||
version = gitTag
|
||||
const jsonVersion = require('../package.json').version;
|
||||
version = gitTag;
|
||||
updateVersionNumberIn2rndPackageJson(jsonVersion);
|
||||
|
||||
builder.build({
|
||||
dir: true,
|
||||
targets: target,
|
||||
appMetadata: {
|
||||
main: 'electron/dist/electron-main.js',
|
||||
main: 'electron-main.js',
|
||||
name: 'UHK Agent',
|
||||
author: {
|
||||
name: 'Ultimate Gaget Laboratories'
|
||||
name: 'Ultimate Gadget Laboratories'
|
||||
},
|
||||
version: jsonVersion
|
||||
},
|
||||
config: {
|
||||
directories: {
|
||||
app: electron_build_folder
|
||||
},
|
||||
appId: 'com.ultimategadgetlabs.uhk.agent',
|
||||
productName: 'UHK Agent',
|
||||
mac: {
|
||||
category: 'public.app-category.utilities',
|
||||
category: 'public.app-category.utilities'
|
||||
},
|
||||
publish: 'github',
|
||||
artifactName,
|
||||
files: [
|
||||
'!**/*',
|
||||
'electron/dist/**/*',
|
||||
'node_modules/**/*'
|
||||
'**/*'
|
||||
]
|
||||
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
console.log('Packing success.')
|
||||
console.log('Packing success.');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(`${error}`)
|
||||
process.exit(1)
|
||||
console.error(`${error}`);
|
||||
process.exit(1);
|
||||
})
|
||||
}
|
||||
else {
|
||||
console.log('No git tag')
|
||||
console.log('No git tag');
|
||||
// TODO: Need it?
|
||||
version = sha.substr(0, 8)
|
||||
process.exit(1)
|
||||
version = sha.substr(0, 8);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function updateVersionNumberIn2rndPackageJson(version) {
|
||||
const jsonPath = path.join(__dirname,'../electron/dist/package.json');
|
||||
const json = require(jsonPath);
|
||||
|
||||
json.version = version;
|
||||
|
||||
jsonfile.writeFileSync(jsonPath, json, {spaces: 2})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
[checked]="settings.checkForUpdateOnStartUp"
|
||||
(change)="emitCheckForUpdateOnStartUp($event.target.checked)"> Automatically check for update on
|
||||
application start
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
[checked]="settings.usePreReleaseUpdate"
|
||||
(change)="emitUsePreReleaseUpdate($event.target.checked)"> Allow alpha / pre release
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Version:</label>
|
||||
<div class="col-sm-10">
|
||||
<p class="form-control-static">{{version}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-link" (click)="emitCheckForUpdate()">
|
||||
Check for update
|
||||
<span *ngIf="checkingForUpdate"
|
||||
class="fa fa-spinner fa-spin"></span>
|
||||
</button>
|
||||
|
||||
<div>
|
||||
{{message}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
import { State } from '../../store/reducers/auto-update-settings';
|
||||
|
||||
@Component({
|
||||
selector: 'auto-update-settings',
|
||||
templateUrl: './auto-update-settings.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class AutoUpdateSettings {
|
||||
|
||||
@Input() version: string;
|
||||
@Input() settings: State;
|
||||
@Input() checkingForUpdate: boolean;
|
||||
@Input() message: string;
|
||||
|
||||
@Output() toggleCheckForUpdateOnStartUp = new EventEmitter<boolean>();
|
||||
@Output() toggleUsePreReleaseUpdate = new EventEmitter<boolean>();
|
||||
@Output() checkForUpdate = new EventEmitter();
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
emitCheckForUpdateOnStartUp(value: boolean) {
|
||||
this.toggleCheckForUpdateOnStartUp.emit(value);
|
||||
}
|
||||
|
||||
emitUsePreReleaseUpdate(value: boolean) {
|
||||
this.toggleUsePreReleaseUpdate.emit(value);
|
||||
}
|
||||
|
||||
emitCheckForUpdate() {
|
||||
this.checkForUpdate.emit();
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,15 @@
|
||||
<span class="macro__name pane-title__name">Settings</span>
|
||||
</h1>
|
||||
</div>
|
||||
To be done...
|
||||
<div *ngIf="!runInElectron">
|
||||
To be done...
|
||||
</div>
|
||||
<auto-update-settings *ngIf="runInElectron"
|
||||
[version]="version"
|
||||
[settings]="autoUpdateSettings$ | async"
|
||||
[checkingForUpdate]="checkingForUpdate$ | async"
|
||||
[message]="autoUpdateMessage$ | async"
|
||||
(toggleCheckForUpdateOnStartUp)="toogleCheckForUpdateOnStartUp($event)"
|
||||
(toggleUsePreReleaseUpdate)="toogleUsePreReleaseUpdate($event)"
|
||||
(checkForUpdate)="checkForUpdate()">
|
||||
</auto-update-settings>
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { runInElectron } from '../../util/index';
|
||||
import { AppState, getAutoUpdateMessage, getAutoUpdateSettings, getCheckingForUpdate } from '../../store';
|
||||
import {
|
||||
CheckForUpdateNowAction,
|
||||
ToggleCheckForUpdateOnStartupAction,
|
||||
TogglePreReleaseFlagAction
|
||||
} from '../../store/actions/auto-update-settings';
|
||||
import { AutoUpdateSettings } from '../../models/auto-update-settings';
|
||||
|
||||
@Component({
|
||||
selector: 'settings',
|
||||
@@ -9,5 +20,28 @@ import { Component } from '@angular/core';
|
||||
}
|
||||
})
|
||||
export class SettingsComponent {
|
||||
constructor() { }
|
||||
runInElectron = runInElectron();
|
||||
// TODO: From where do we get the version number? The electron gives back in main process, but the web...
|
||||
version = '1.0.0';
|
||||
autoUpdateSettings$: Observable<AutoUpdateSettings>;
|
||||
checkingForUpdate$: Observable<boolean>;
|
||||
autoUpdateMessage$: Observable<string>;
|
||||
|
||||
constructor(private store: Store<AppState>) {
|
||||
this.autoUpdateSettings$ = store.select(getAutoUpdateSettings);
|
||||
this.checkingForUpdate$ = store.select(getCheckingForUpdate);
|
||||
this.autoUpdateMessage$ = store.select(getAutoUpdateMessage);
|
||||
}
|
||||
|
||||
toogleCheckForUpdateOnStartUp(value: boolean) {
|
||||
this.store.dispatch(new ToggleCheckForUpdateOnStartupAction(value));
|
||||
}
|
||||
|
||||
toogleUsePreReleaseUpdate(value: boolean) {
|
||||
this.store.dispatch(new TogglePreReleaseFlagAction(value));
|
||||
}
|
||||
|
||||
checkForUpdate() {
|
||||
this.store.dispatch(new CheckForUpdateNowAction());
|
||||
}
|
||||
}
|
||||
|
||||
4
shared/src/models/auto-update-settings.ts
Normal file
4
shared/src/models/auto-update-settings.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface AutoUpdateSettings {
|
||||
checkForUpdateOnStartUp: boolean;
|
||||
usePreReleaseUpdate: boolean;
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
import { InjectionToken } from '@angular/core';
|
||||
|
||||
import { UserConfiguration } from '../config-serializer/config-items/UserConfiguration';
|
||||
import { AutoUpdateSettings } from '../models/auto-update-settings';
|
||||
|
||||
export interface DataStorageRepositoryService {
|
||||
|
||||
getConfig(): UserConfiguration;
|
||||
|
||||
saveConfig(config: UserConfiguration): void;
|
||||
|
||||
getAutoUpdateSettings(): AutoUpdateSettings;
|
||||
|
||||
saveAutoUpdateSettings(settings: AutoUpdateSettings): void;
|
||||
}
|
||||
|
||||
export let DATA_STORAGE_REPOSITORY = new InjectionToken('dataStorage-repository');
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { UserConfiguration } from '../config-serializer/config-items/UserConfiguration';
|
||||
import { DataStorageRepositoryService } from './datastorage-repository.service';
|
||||
import { DefaultUserConfigurationService } from './default-user-configuration.service';
|
||||
import { State as AutoUpdateSettings } from '../store/reducers/auto-update-settings';
|
||||
|
||||
@Injectable()
|
||||
export class LocalDataStorageRepositoryService implements DataStorageRepositoryService {
|
||||
constructor(private defaultUserConfigurationService: DefaultUserConfigurationService) { }
|
||||
|
||||
getConfig(): UserConfiguration {
|
||||
const configJsonString = localStorage.getItem('config');
|
||||
let config: UserConfiguration;
|
||||
|
||||
if (configJsonString) {
|
||||
const configJsonObject = JSON.parse(configJsonString);
|
||||
if (configJsonObject.dataModelVersion === this.defaultUserConfigurationService.getDefault().dataModelVersion) {
|
||||
config = new UserConfiguration().fromJsonObject(configJsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
return JSON.parse(localStorage.getItem('config'));
|
||||
}
|
||||
|
||||
saveConfig(config: UserConfiguration): void {
|
||||
localStorage.setItem('config', JSON.stringify(config.toJsonObject()));
|
||||
}
|
||||
|
||||
getAutoUpdateSettings(): AutoUpdateSettings {
|
||||
return JSON.parse(localStorage.getItem('auto-update-settings'));
|
||||
}
|
||||
|
||||
saveAutoUpdateSettings(settings: AutoUpdateSettings): void {
|
||||
localStorage.setItem('auto-update-settings', JSON.stringify(settings));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
74
shared/src/store/actions/auto-update-settings.ts
Normal file
74
shared/src/store/actions/auto-update-settings.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
|
||||
import { type } from '../../util';
|
||||
import { AutoUpdateSettings } from '../../models/auto-update-settings';
|
||||
|
||||
const PREFIX = '[app-update-config] ';
|
||||
|
||||
// tslint:disable-next-line:variable-name
|
||||
export const ActionTypes = {
|
||||
TOGGLE_CHECK_FOR_UPDATE_ON_STARTUP: type(PREFIX + 'Check for update on startup'),
|
||||
CHECK_FOR_UPDATE_NOW: type(PREFIX + 'Check for update now'),
|
||||
CHECK_FOR_UPDATE_SUCCESS: type(PREFIX + 'Check for update success'),
|
||||
CHECK_FOR_UPDATE_FAILED: type(PREFIX + 'Check for update faild'),
|
||||
TOGGLE_PRE_RELEASE_FLAG: type(PREFIX + 'Toggle pre release update flag'),
|
||||
LOAD_AUTO_UPDATE_SETTINGS: type(PREFIX + 'Load auto update settings'),
|
||||
LOAD_AUTO_UPDATE_SETTINGS_SUCCESS: type(PREFIX + 'Load auto update settings success'),
|
||||
SAVE_AUTO_UPDATE_SETTINGS_SUCCESS: type(PREFIX + 'Save auto update settings success')
|
||||
};
|
||||
|
||||
export class ToggleCheckForUpdateOnStartupAction implements Action {
|
||||
type = ActionTypes.TOGGLE_CHECK_FOR_UPDATE_ON_STARTUP;
|
||||
|
||||
constructor(public payload: boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
export class CheckForUpdateNowAction implements Action {
|
||||
type = ActionTypes.CHECK_FOR_UPDATE_NOW;
|
||||
}
|
||||
|
||||
export class CheckForUpdateSuccessAction implements Action {
|
||||
type = ActionTypes.CHECK_FOR_UPDATE_SUCCESS;
|
||||
constructor(public payload?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export class CheckForUpdateFailedAction implements Action {
|
||||
type = ActionTypes.CHECK_FOR_UPDATE_FAILED;
|
||||
|
||||
constructor(public payload: any) {
|
||||
}
|
||||
}
|
||||
|
||||
export class TogglePreReleaseFlagAction implements Action {
|
||||
type = ActionTypes.TOGGLE_PRE_RELEASE_FLAG;
|
||||
|
||||
constructor(public payload: boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
export class LoadAutoUpdateSettingsAction implements Action {
|
||||
type = ActionTypes.LOAD_AUTO_UPDATE_SETTINGS_SUCCESS;
|
||||
}
|
||||
|
||||
export class LoadAutoUpdateSettingsSuccessAction implements Action {
|
||||
type = ActionTypes.LOAD_AUTO_UPDATE_SETTINGS_SUCCESS;
|
||||
|
||||
constructor(public payload: AutoUpdateSettings) {
|
||||
}
|
||||
}
|
||||
|
||||
export class SaveAutoUpdateSettingsSuccessAction implements Action {
|
||||
type = ActionTypes.SAVE_AUTO_UPDATE_SETTINGS_SUCCESS;
|
||||
}
|
||||
|
||||
export type Actions
|
||||
= ToggleCheckForUpdateOnStartupAction
|
||||
| CheckForUpdateNowAction
|
||||
| CheckForUpdateSuccessAction
|
||||
| CheckForUpdateFailedAction
|
||||
| TogglePreReleaseFlagAction
|
||||
| LoadAutoUpdateSettingsAction
|
||||
| LoadAutoUpdateSettingsSuccessAction
|
||||
| SaveAutoUpdateSettingsSuccessAction;
|
||||
42
shared/src/store/effects/auto-update-settings.ts
Normal file
42
shared/src/store/effects/auto-update-settings.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { Actions, Effect } from '@ngrx/effects';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Action, Store } from '@ngrx/store';
|
||||
|
||||
import {
|
||||
ActionTypes,
|
||||
LoadAutoUpdateSettingsAction,
|
||||
LoadAutoUpdateSettingsSuccessAction,
|
||||
SaveAutoUpdateSettingsSuccessAction
|
||||
} from '../actions/auto-update-settings';
|
||||
import { DATA_STORAGE_REPOSITORY, DataStorageRepositoryService } from '../../services/datastorage-repository.service';
|
||||
import { AppState, getAutoUpdateSettings } from '../index';
|
||||
import { initialState, State } from '../reducers/auto-update-settings';
|
||||
import { AutoUpdateSettings } from '../../models/auto-update-settings';
|
||||
|
||||
@Injectable()
|
||||
export class AutoUpdateSettingsEffects {
|
||||
@Effect() loadUserConfig$: Observable<Action> = this.actions$
|
||||
.ofType(ActionTypes.LOAD_AUTO_UPDATE_SETTINGS)
|
||||
.startWith(new LoadAutoUpdateSettingsAction())
|
||||
.switchMap(() => {
|
||||
let settings: AutoUpdateSettings = this.dataStorageRepository.getAutoUpdateSettings();
|
||||
if (!settings) {
|
||||
settings = initialState;
|
||||
}
|
||||
return Observable.of(new LoadAutoUpdateSettingsSuccessAction(settings));
|
||||
});
|
||||
|
||||
@Effect() saveAutoUpdateConfig$: Observable<Action> = this.actions$
|
||||
.ofType(ActionTypes.TOGGLE_CHECK_FOR_UPDATE_ON_STARTUP, ActionTypes.TOGGLE_PRE_RELEASE_FLAG)
|
||||
.withLatestFrom(this.store.select(getAutoUpdateSettings))
|
||||
.map(([action, config]) => {
|
||||
this.dataStorageRepository.saveAutoUpdateSettings(config);
|
||||
return new SaveAutoUpdateSettingsSuccessAction();
|
||||
});
|
||||
|
||||
constructor(private actions$: Actions,
|
||||
@Inject(DATA_STORAGE_REPOSITORY) private dataStorageRepository: DataStorageRepositoryService,
|
||||
private store: Store<AppState>) {
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './keymap';
|
||||
export * from './macro';
|
||||
export * from './user-config';
|
||||
export * from './auto-update-settings';
|
||||
|
||||
@@ -30,18 +30,28 @@ export class UserConfigEffects {
|
||||
.ofType(ActionTypes.LOAD_USER_CONFIG)
|
||||
.startWith(new LoadUserConfigAction())
|
||||
.switchMap(() => {
|
||||
let config: UserConfiguration = this.dataStorageRepository.getConfig();
|
||||
const configJsonObject = this.dataStorageRepository.getConfig();
|
||||
let config: UserConfiguration;
|
||||
|
||||
if (configJsonObject) {
|
||||
if (configJsonObject.dataModelVersion === this.defaultUserConfigurationService.getDefault().dataModelVersion) {
|
||||
config = new UserConfiguration().fromJsonObject(configJsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
config = this.defaultUserConfigurationService.getDefault();
|
||||
}
|
||||
|
||||
return Observable.of(new LoadUserConfigSuccessAction(config));
|
||||
});
|
||||
|
||||
@Effect() saveUserConfig$: Observable<Action> = this.actions$
|
||||
.ofType(KeymapActions.ADD, KeymapActions.DUPLICATE, KeymapActions.EDIT_NAME, KeymapActions.EDIT_ABBR,
|
||||
KeymapActions.SET_DEFAULT, KeymapActions.REMOVE, KeymapActions.SAVE_KEY, KeymapActions.CHECK_MACRO,
|
||||
MacroActions.ADD, MacroActions.DUPLICATE, MacroActions.EDIT_NAME, MacroActions.REMOVE, MacroActions.ADD_ACTION,
|
||||
MacroActions.SAVE_ACTION, MacroActions.DELETE_ACTION, MacroActions.REORDER_ACTION)
|
||||
.ofType(
|
||||
KeymapActions.ADD, KeymapActions.DUPLICATE, KeymapActions.EDIT_NAME, KeymapActions.EDIT_ABBR,
|
||||
KeymapActions.SET_DEFAULT, KeymapActions.REMOVE, KeymapActions.SAVE_KEY, KeymapActions.CHECK_MACRO,
|
||||
MacroActions.ADD, MacroActions.DUPLICATE, MacroActions.EDIT_NAME, MacroActions.REMOVE, MacroActions.ADD_ACTION,
|
||||
MacroActions.SAVE_ACTION, MacroActions.DELETE_ACTION, MacroActions.REORDER_ACTION)
|
||||
.withLatestFrom(this.store.select(getUserConfiguration))
|
||||
.map(([action, config]) => {
|
||||
this.dataStorageRepository.saveConfig(config);
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { Keymap } from '../config-serializer/config-items/Keymap';
|
||||
import { UserConfiguration } from '../config-serializer/config-items/UserConfiguration';
|
||||
import * as autoUpdate from './reducers/auto-update-settings';
|
||||
|
||||
// State interface for the application
|
||||
export interface AppState {
|
||||
userConfiguration: UserConfiguration;
|
||||
presetKeymaps: Keymap[];
|
||||
autoUpdateSettings: autoUpdate.State;
|
||||
}
|
||||
|
||||
export const getUserConfiguration = (state: AppState) => state.userConfiguration;
|
||||
|
||||
export const appUpdateState = (state: AppState) => state.autoUpdateSettings;
|
||||
|
||||
export const getAutoUpdateSettings = createSelector(appUpdateState, autoUpdate.getUpdateSettings);
|
||||
export const getCheckingForUpdate = createSelector(appUpdateState, autoUpdate.checkingForUpdate);
|
||||
export const getAutoUpdateMessage = createSelector(appUpdateState, autoUpdate.getMessage);
|
||||
|
||||
50
shared/src/store/reducers/auto-update-settings.ts
Normal file
50
shared/src/store/reducers/auto-update-settings.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { ActionTypes } from '../actions/auto-update-settings';
|
||||
import { AutoUpdateSettings } from '../../models/auto-update-settings';
|
||||
|
||||
export interface State extends AutoUpdateSettings {
|
||||
checkingForUpdate: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export const initialState: State = {
|
||||
checkForUpdateOnStartUp: false,
|
||||
usePreReleaseUpdate: false,
|
||||
checkingForUpdate: false
|
||||
};
|
||||
|
||||
export function reducer(state = initialState, action: Action): State {
|
||||
switch (action.type) {
|
||||
case ActionTypes.TOGGLE_CHECK_FOR_UPDATE_ON_STARTUP: {
|
||||
return Object.assign({}, state, { checkForUpdateOnStartUp: action.payload });
|
||||
}
|
||||
|
||||
case ActionTypes.TOGGLE_PRE_RELEASE_FLAG: {
|
||||
return Object.assign({}, state, { usePreReleaseUpdate: action.payload });
|
||||
}
|
||||
|
||||
case ActionTypes.LOAD_AUTO_UPDATE_SETTINGS_SUCCESS: {
|
||||
return Object.assign({}, action.payload);
|
||||
}
|
||||
|
||||
case ActionTypes.CHECK_FOR_UPDATE_NOW: {
|
||||
return Object.assign({}, state, { checkingForUpdate: true, message: null });
|
||||
}
|
||||
|
||||
case ActionTypes.CHECK_FOR_UPDATE_SUCCESS:
|
||||
case ActionTypes.CHECK_FOR_UPDATE_FAILED: {
|
||||
return Object.assign({}, state, { checkingForUpdate: false, message: action.payload });
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export const getUpdateSettings = (state: State) => ({
|
||||
checkForUpdateOnStartUp: state.checkForUpdateOnStartUp,
|
||||
usePreReleaseUpdate: state.usePreReleaseUpdate
|
||||
});
|
||||
|
||||
export const checkingForUpdate = (state: State) => state.checkingForUpdate;
|
||||
export const getMessage = (state: State) => state.message;
|
||||
@@ -2,10 +2,14 @@ import { routerReducer } from '@ngrx/router-store';
|
||||
|
||||
import userConfigurationReducer from './user-configuration';
|
||||
import presetReducer from './preset';
|
||||
import { reducer as autoUpdateReducer } from './auto-update-settings';
|
||||
|
||||
export { userConfigurationReducer, presetReducer, autoUpdateReducer };
|
||||
|
||||
// All reducers that are used in application
|
||||
export const reducer = {
|
||||
userConfiguration: userConfigurationReducer,
|
||||
presetKeymaps: presetReducer,
|
||||
router: routerReducer
|
||||
router: routerReducer,
|
||||
autoUpdateSettings: autoUpdateReducer
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ const initialState: Keymap[] = [];
|
||||
export default function(state = initialState, action: Action): Keymap[] {
|
||||
switch (action.type) {
|
||||
case KeymapActions.LOAD_KEYMAPS_SUCCESS: {
|
||||
return Object.assign(state, action.payload);
|
||||
return action.payload;
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -1,27 +1,3 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"removeComments": false,
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"../../node_modules/@types"
|
||||
],
|
||||
"types": [
|
||||
"node",
|
||||
"jquery",
|
||||
"core-js",
|
||||
"select2",
|
||||
"file-saver"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"config-serializer"
|
||||
]
|
||||
"extends": "../../tsconfig.base.json"
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ export function capitalizeFirstLetter(text: string): string {
|
||||
*/
|
||||
|
||||
const typeCache: { [label: string]: boolean } = {};
|
||||
|
||||
export function type<T>(label: T | ''): T {
|
||||
if (typeCache[<string>label]) {
|
||||
throw new Error(`Action type "${label}" is not unique"`);
|
||||
@@ -29,3 +30,9 @@ export function type<T>(label: T | ''): T {
|
||||
|
||||
return <T>label;
|
||||
}
|
||||
|
||||
export { IpcEvents } from './ipcEvents';
|
||||
|
||||
export function runInElectron() {
|
||||
return window && (<any>window).process && (<any>window).process.type;
|
||||
}
|
||||
|
||||
20
shared/src/util/ipcEvents.ts
Normal file
20
shared/src/util/ipcEvents.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
class App {
|
||||
public static readonly appStarted = 'app-started';
|
||||
}
|
||||
|
||||
class AutoUpdate {
|
||||
public static readonly checkingForUpdate = 'checking-for-update';
|
||||
public static readonly updateAvailable = 'update-available';
|
||||
public static readonly updateNotAvailable = 'update-not-available';
|
||||
public static readonly autoUpdateError = 'auto-update-error';
|
||||
public static readonly autoUpdateDownloaded = 'update-downloaded';
|
||||
public static readonly autoUpdateDownloadProgress = 'auto-update-download-progress';
|
||||
public static readonly updateAndRestart = 'update-and-restart';
|
||||
public static readonly checkForUpdate = 'check-for-update';
|
||||
public static readonly checkForUpdateNotAvailable = 'check-for-update-not-available';
|
||||
}
|
||||
|
||||
export class IpcEvents {
|
||||
public static readonly app = App;
|
||||
public static readonly autoUpdater = AutoUpdate;
|
||||
}
|
||||
@@ -11,8 +11,8 @@
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
},
|
||||
"exclude": [
|
||||
"./node_modules",
|
||||
"./dist",
|
||||
".electron/dist",
|
||||
"./dist"
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -60,7 +60,7 @@ import {
|
||||
} from './shared/components/svg/keys';
|
||||
import { SvgModuleComponent } from './shared/components/svg/module';
|
||||
import { SvgKeyboardWrapComponent } from './shared/components/svg/wrap';
|
||||
import { MainAppComponent, appRoutingProviders, routing } from './main-app';
|
||||
import { appRoutingProviders, MainAppComponent, routing } from './main-app';
|
||||
|
||||
import { CancelableDirective } from './shared/directives';
|
||||
import { SafeStylePipe } from './shared/pipes';
|
||||
@@ -69,14 +69,15 @@ import { CaptureService } from './shared/services/capture.service';
|
||||
import { MapperService } from './shared/services/mapper.service';
|
||||
import { SvgModuleProviderService } from './shared/services/svg-module-provider.service';
|
||||
|
||||
import { KeymapEffects, MacroEffects, UserConfigEffects } from './shared/store/effects';
|
||||
import { AutoUpdateSettingsEffects, KeymapEffects, MacroEffects, UserConfigEffects } from './shared/store/effects';
|
||||
|
||||
import { KeymapEditGuard } from './shared/components/keymap/edit';
|
||||
import { MacroNotFoundGuard } from './shared/components/macro/not-found';
|
||||
import { DATA_STORAGE_REPOSITORY } from './shared/services/datastorage-repository.service';
|
||||
import { LocalDataStorageRepositoryService } from './shared/services/local-datastorage-repository.service';
|
||||
import { DefaultUserConfigurationService } from './shared/services/default-user-configuration.service';
|
||||
import { reducer } from '../../shared/src/store/reducers/index';
|
||||
import { reducer } from './shared/store/reducers/index';
|
||||
import { AutoUpdateSettings } from './shared/components/auto-update-settings/auto-update-settings';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -127,7 +128,8 @@ import { reducer } from '../../shared/src/store/reducers/index';
|
||||
SettingsComponent,
|
||||
KeyboardSliderComponent,
|
||||
CancelableDirective,
|
||||
SafeStylePipe
|
||||
SafeStylePipe,
|
||||
AutoUpdateSettings
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@@ -147,7 +149,8 @@ import { reducer } from '../../shared/src/store/reducers/index';
|
||||
Select2Module,
|
||||
EffectsModule.runAfterBootstrap(KeymapEffects),
|
||||
EffectsModule.runAfterBootstrap(MacroEffects),
|
||||
EffectsModule.runAfterBootstrap(UserConfigEffects)
|
||||
EffectsModule.runAfterBootstrap(UserConfigEffects),
|
||||
EffectsModule.runAfterBootstrap(AutoUpdateSettingsEffects)
|
||||
],
|
||||
providers: [
|
||||
SvgModuleProviderService,
|
||||
@@ -156,9 +159,10 @@ import { reducer } from '../../shared/src/store/reducers/index';
|
||||
KeymapEditGuard,
|
||||
MacroNotFoundGuard,
|
||||
CaptureService,
|
||||
{provide: DATA_STORAGE_REPOSITORY, useClass: LocalDataStorageRepositoryService},
|
||||
{ provide: DATA_STORAGE_REPOSITORY, useClass: LocalDataStorageRepositoryService },
|
||||
DefaultUserConfigurationService
|
||||
],
|
||||
bootstrap: [MainAppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
export class AppModule {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"exclude": [
|
||||
"config-serializer"
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user