feat(agent): automatically write user configuration after app started (#516)

* auto write userconfig

* fix load success action

* exit from app

* add electron:auto-write-config script
This commit is contained in:
Róbert Kiss
2017-12-14 23:36:43 +01:00
committed by László Monda
parent e8a1814d89
commit b32c93f0f8
15 changed files with 90 additions and 38 deletions

View File

@@ -86,6 +86,7 @@
"server:web": "lerna exec --scope uhk-web npm start", "server:web": "lerna exec --scope uhk-web npm start",
"server:electron": "lerna exec --scope uhk-web npm run server:renderer", "server:electron": "lerna exec --scope uhk-web npm run server:renderer",
"electron": "lerna exec --scope uhk-agent npm start", "electron": "lerna exec --scope uhk-agent npm start",
"electron:auto-write-config": "lerna exec --scope uhk-agent npm run auto-write-config",
"standard-version": "standard-version", "standard-version": "standard-version",
"pack": "node ./scripts/release.js", "pack": "node ./scripts/release.js",
"sprites": "node ./scripts/generate-svg-sprites", "sprites": "node ./scripts/generate-svg-sprites",

View File

@@ -36,6 +36,7 @@
}, },
"scripts": { "scripts": {
"start": "electron ./dist/electron-main.js", "start": "electron ./dist/electron-main.js",
"auto-write-config": "electron ./dist/electron-main.js --auto-write-config",
"build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-blhost", "build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-blhost",
"build:usb": "electron-rebuild -w node-hid -p -m ./dist", "build:usb": "electron-rebuild -w node-hid -p -m ./dist",
"install:build-deps": "cd ./dist && npm i", "install:build-deps": "cd ./dist && npm i",

View File

@@ -10,7 +10,7 @@ import * as url from 'url';
import * as commandLineArgs from 'command-line-args'; import * as commandLineArgs from 'command-line-args';
import { UhkHidDevice, UhkOperations } from 'uhk-usb'; import { UhkHidDevice, UhkOperations } from 'uhk-usb';
// import { ElectronDataStorageRepositoryService } from './services/electron-datastorage-repository.service'; // import { ElectronDataStorageRepositoryService } from './services/electron-datastorage-repository.service';
import { CommandLineArgs, LogRegExps } from 'uhk-common'; import { LogRegExps } from 'uhk-common';
import { DeviceService } from './services/device.service'; import { DeviceService } from './services/device.service';
import { logger } from './services/logger.service'; import { logger } from './services/logger.service';
import { AppUpdateService } from './services/app-update.service'; import { AppUpdateService } from './services/app-update.service';
@@ -18,12 +18,14 @@ import { AppService } from './services/app.service';
import { SudoService } from './services/sudo.service'; import { SudoService } from './services/sudo.service';
import { UhkBlhost } from '../../uhk-usb/src'; import { UhkBlhost } from '../../uhk-usb/src';
import * as isDev from 'electron-is-dev'; import * as isDev from 'electron-is-dev';
import { CommandLineInputs } from './models/command-line-inputs';
const optionDefinitions = [ const optionDefinitions = [
{name: 'addons', type: Boolean, defaultOption: false} {name: 'addons', type: Boolean},
{name: 'auto-write-config', type: Boolean}
]; ];
const options: CommandLineArgs = commandLineArgs(optionDefinitions); const options: CommandLineInputs = commandLineArgs(optionDefinitions);
// import './dev-extension'; // import './dev-extension';
// require('electron-debug')({ showDevTools: true, enabled: true }); // require('electron-debug')({ showDevTools: true, enabled: true });

View File

@@ -0,0 +1,4 @@
export interface CommandLineInputs {
addons?: boolean;
'auto-write-config'?: boolean;
}

View File

@@ -3,19 +3,21 @@ import { UhkHidDevice } from 'uhk-usb';
import { readFile } from 'fs'; import { readFile } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { AppStartInfo, CommandLineArgs, IpcEvents, LogService } from 'uhk-common'; import { AppStartInfo, IpcEvents, LogService } from 'uhk-common';
import { MainServiceBase } from './main-service-base'; import { MainServiceBase } from './main-service-base';
import { DeviceService } from './device.service'; import { DeviceService } from './device.service';
import { CommandLineInputs } from '../models/command-line-inputs';
export class AppService extends MainServiceBase { export class AppService extends MainServiceBase {
constructor(protected logService: LogService, constructor(protected logService: LogService,
protected win: Electron.BrowserWindow, protected win: Electron.BrowserWindow,
private deviceService: DeviceService, private deviceService: DeviceService,
private options: CommandLineArgs, private options: CommandLineInputs,
private uhkHidDeviceService: UhkHidDevice) { private uhkHidDeviceService: UhkHidDevice) {
super(logService, win); super(logService, win);
ipcMain.on(IpcEvents.app.getAppStartInfo, this.handleAppStartInfo.bind(this)); ipcMain.on(IpcEvents.app.getAppStartInfo, this.handleAppStartInfo.bind(this));
ipcMain.on(IpcEvents.app.exit, this.exit.bind(this));
logService.info('[AppService] init success'); logService.info('[AppService] init success');
} }
@@ -25,7 +27,10 @@ export class AppService extends MainServiceBase {
const packageJson = await this.getPackageJson(); const packageJson = await this.getPackageJson();
const response: AppStartInfo = { const response: AppStartInfo = {
commandLineArgs: this.options, commandLineArgs: {
addons: this.options.addons || false,
autoWriteConfig: this.options['auto-write-config'] || false
},
deviceConnected: this.deviceService.isConnected, deviceConnected: this.deviceService.isConnected,
hasPermission: this.uhkHidDeviceService.hasPermission(), hasPermission: this.uhkHidDeviceService.hasPermission(),
agentVersionInfo: { agentVersionInfo: {
@@ -57,4 +62,9 @@ export class AppService extends MainServiceBase {
}); });
}); });
} }
private exit() {
this.logService.info('[AppService] exit');
this.win.close();
}
} }

View File

@@ -89,7 +89,6 @@ export class DeviceService {
success: true, success: true,
...result ...result
}; };
event.sender.send(IpcEvents.device.loadConfigurationReply, JSON.stringify(response));
} catch (error) { } catch (error) {
response = { response = {
success: false, success: false,

View File

@@ -1,3 +1,4 @@
export interface CommandLineArgs { export interface CommandLineArgs {
addons: boolean; addons: boolean;
autoWriteConfig: boolean;
} }

View File

@@ -2,6 +2,7 @@ class App {
public static readonly appStarted = 'app-started'; public static readonly appStarted = 'app-started';
public static readonly getAppStartInfo = 'app-get-start-info'; public static readonly getAppStartInfo = 'app-get-start-info';
public static readonly getAppStartInfoReply = 'app-get-start-info-reply'; public static readonly getAppStartInfoReply = 'app-get-start-info-reply';
public static readonly exit = 'app-exit';
} }
class AutoUpdate { class AutoUpdate {

View File

@@ -21,6 +21,11 @@ export class AppRendererService {
this.ipcRenderer.send(IpcEvents.app.getAppStartInfo); this.ipcRenderer.send(IpcEvents.app.getAppStartInfo);
} }
exit() {
this.logService.info('[AppRendererService] exit');
this.ipcRenderer.send(IpcEvents.app.exit);
}
private registerEvents() { private registerEvents() {
this.ipcRenderer.on(IpcEvents.app.getAppStartInfoReply, (event: string, arg: AppStartInfo) => { this.ipcRenderer.on(IpcEvents.app.getAppStartInfoReply, (event: string, arg: AppStartInfo) => {
this.dispachStoreAction(new ProcessAppStartInfoAction(arg)); this.dispachStoreAction(new ProcessAppStartInfoAction(arg));

View File

@@ -1,6 +1,6 @@
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { AppStartInfo, HardwareConfiguration, Notification, type, VersionInformation } from 'uhk-common'; import { AppStartInfo, CommandLineArgs, HardwareConfiguration, Notification, type, VersionInformation } from 'uhk-common';
import { ElectronLogEntry } from '../../models/xterm-log'; import { ElectronLogEntry } from '../../models/xterm-log';
const PREFIX = '[app] '; const PREFIX = '[app] ';
@@ -10,7 +10,7 @@ export const ActionTypes = {
APP_BOOTSRAPPED: type(PREFIX + 'bootstrapped'), APP_BOOTSRAPPED: type(PREFIX + 'bootstrapped'),
APP_STARTED: type(PREFIX + 'started'), APP_STARTED: type(PREFIX + 'started'),
APP_SHOW_NOTIFICATION: type(PREFIX + 'show notification'), APP_SHOW_NOTIFICATION: type(PREFIX + 'show notification'),
APP_TOGGLE_ADDON_MENU: type(PREFIX + 'toggle add-on menu'), APPLY_COMMAND_LINE_ARGS: type(PREFIX + 'apply command line args'),
APP_PROCESS_START_INFO: type(PREFIX + 'process start info'), APP_PROCESS_START_INFO: type(PREFIX + 'process start info'),
UNDO_LAST: type(PREFIX + 'undo last action'), UNDO_LAST: type(PREFIX + 'undo last action'),
UNDO_LAST_SUCCESS: type(PREFIX + 'undo last action success'), UNDO_LAST_SUCCESS: type(PREFIX + 'undo last action success'),
@@ -34,10 +34,10 @@ export class ShowNotificationAction implements Action {
constructor(public payload: Notification) { } constructor(public payload: Notification) { }
} }
export class ToggleAddonMenuAction implements Action { export class ApplyCommandLineArgsAction implements Action {
type = ActionTypes.APP_TOGGLE_ADDON_MENU; type = ActionTypes.APPLY_COMMAND_LINE_ARGS;
constructor(public payload: boolean) { } constructor(public payload: CommandLineArgs) { }
} }
export class ProcessAppStartInfoAction implements Action { export class ProcessAppStartInfoAction implements Action {
@@ -82,7 +82,7 @@ export type Actions
= AppStartedAction = AppStartedAction
| AppBootsrappedAction | AppBootsrappedAction
| ShowNotificationAction | ShowNotificationAction
| ToggleAddonMenuAction | ApplyCommandLineArgsAction
| ProcessAppStartInfoAction | ProcessAppStartInfoAction
| UndoLastAction | UndoLastAction
| UndoLastSuccessAction | UndoLastSuccessAction

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Action } from '@ngrx/store'; import { Action, Store } from '@ngrx/store';
import { Actions, Effect } from '@ngrx/effects'; import { Actions, Effect } from '@ngrx/effects';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
import { NotifierService } from 'angular-notifier'; import { NotifierService } from 'angular-notifier';
@@ -13,16 +13,23 @@ import 'rxjs/add/operator/catch';
import { AppStartInfo, LogService, Notification, NotificationType } from 'uhk-common'; import { AppStartInfo, LogService, Notification, NotificationType } from 'uhk-common';
import { import {
ActionTypes, ActionTypes,
ApplyCommandLineArgsAction,
AppStartedAction, AppStartedAction,
DismissUndoNotificationAction, DismissUndoNotificationAction,
ProcessAppStartInfoAction, ProcessAppStartInfoAction,
ShowNotificationAction, ShowNotificationAction,
ToggleAddonMenuAction, UndoLastAction,
UndoLastAction, UpdateAgentVersionInformationAction UpdateAgentVersionInformationAction
} from '../actions/app'; } from '../actions/app';
import { AppRendererService } from '../../services/app-renderer.service'; import { AppRendererService } from '../../services/app-renderer.service';
import { AppUpdateRendererService } from '../../services/app-update-renderer.service'; import { AppUpdateRendererService } from '../../services/app-update-renderer.service';
import { ConnectionStateChangedAction, PermissionStateChangedAction } from '../actions/device'; import {
ActionTypes as DeviceActions,
ConnectionStateChangedAction,
PermissionStateChangedAction,
SaveToKeyboardSuccessAction
} from '../actions/device';
import { AppState, autoWriteUserConfiguration } from '../index';
@Injectable() @Injectable()
export class ApplicationEffects { export class ApplicationEffects {
@@ -56,7 +63,7 @@ export class ApplicationEffects {
.mergeMap((appInfo: AppStartInfo) => { .mergeMap((appInfo: AppStartInfo) => {
this.logService.debug('[AppEffect][processStartInfo] payload:', appInfo); this.logService.debug('[AppEffect][processStartInfo] payload:', appInfo);
return [ return [
new ToggleAddonMenuAction(appInfo.commandLineArgs.addons), new ApplyCommandLineArgsAction(appInfo.commandLineArgs),
new ConnectionStateChangedAction(appInfo.deviceConnected), new ConnectionStateChangedAction(appInfo.deviceConnected),
new PermissionStateChangedAction(appInfo.hasPermission), new PermissionStateChangedAction(appInfo.hasPermission),
new UpdateAgentVersionInformationAction(appInfo.agentVersionInfo) new UpdateAgentVersionInformationAction(appInfo.agentVersionInfo)
@@ -68,10 +75,20 @@ export class ApplicationEffects {
.map(action => action.payload) .map(action => action.payload)
.mergeMap((action: Action) => [action, new DismissUndoNotificationAction()]); .mergeMap((action: Action) => [action, new DismissUndoNotificationAction()]);
@Effect({dispatch: false}) saveToKeyboardSuccess$ = this.actions$
.ofType<SaveToKeyboardSuccessAction>(DeviceActions.SAVE_TO_KEYBOARD_SUCCESS)
.withLatestFrom(this.store.select(autoWriteUserConfiguration))
.do(([action, autoWriteUserConfig]) => {
if (autoWriteUserConfig) {
this.appRendererService.exit();
}
});
constructor(private actions$: Actions, constructor(private actions$: Actions,
private notifierService: NotifierService, private notifierService: NotifierService,
private appUpdateRendererService: AppUpdateRendererService, private appUpdateRendererService: AppUpdateRendererService,
private appRendererService: AppRendererService, private appRendererService: AppRendererService,
private logService: LogService) { private logService: LogService,
private store: Store<AppState>) {
} }
} }

View File

@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Action, Store } from '@ngrx/store'; import { Action, Store } from '@ngrx/store';
import { Actions, Effect, toPayload } from '@ngrx/effects'; import { Actions, Effect } from '@ngrx/effects';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of'; import 'rxjs/add/observable/of';
@@ -20,6 +20,7 @@ import {
HideSaveToKeyboardButton, HideSaveToKeyboardButton,
PermissionStateChangedAction, PermissionStateChangedAction,
SaveConfigurationAction, SaveConfigurationAction,
SaveConfigurationReplyAction,
SaveToKeyboardSuccessAction, SaveToKeyboardSuccessAction,
SaveToKeyboardSuccessFailed, SaveToKeyboardSuccessFailed,
SetPrivilegeOnLinuxReplyAction, SetPrivilegeOnLinuxReplyAction,
@@ -113,8 +114,8 @@ export class DeviceEffects {
@Effect() @Effect()
saveConfigurationReply$: Observable<Action> = this.actions$ saveConfigurationReply$: Observable<Action> = this.actions$
.ofType(ActionTypes.SAVE_CONFIGURATION_REPLY) .ofType<SaveConfigurationReplyAction>(ActionTypes.SAVE_CONFIGURATION_REPLY)
.map(toPayload) .map(action => action.payload)
.mergeMap((response: IpcResponse) => { .mergeMap((response: IpcResponse) => {
if (response.success) { if (response.success) {
return [ return [

View File

@@ -12,35 +12,27 @@ import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/withLatestFrom'; import 'rxjs/add/operator/withLatestFrom';
import 'rxjs/add/operator/mergeMap'; import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/observable/of'; import 'rxjs/add/observable/of';
import 'rxjs/add/observable/empty';
import { import {
ConfigurationReply, ConfigurationReply, HardwareConfiguration, LogService, NotificationType, UhkBuffer,
HardwareConfiguration,
LogService,
NotificationType,
UhkBuffer,
UserConfiguration UserConfiguration
} from 'uhk-common'; } from 'uhk-common';
import { import {
ActionTypes, ActionTypes, LoadConfigFromDeviceReplyAction, LoadUserConfigSuccessAction, RenameUserConfigurationAction,
LoadConfigFromDeviceReplyAction,
LoadUserConfigSuccessAction,
RenameUserConfigurationAction,
SaveUserConfigSuccessAction SaveUserConfigSuccessAction
} from '../actions/user-config'; } from '../actions/user-config';
import { DataStorageRepositoryService } from '../../services/datastorage-repository.service'; import { DataStorageRepositoryService } from '../../services/datastorage-repository.service';
import { DefaultUserConfigurationService } from '../../services/default-user-configuration.service'; import { DefaultUserConfigurationService } from '../../services/default-user-configuration.service';
import { AppState, getPrevUserConfiguration, getUserConfiguration } from '../index'; import { AppState, autoWriteUserConfiguration, getPrevUserConfiguration, getUserConfiguration } from '../index';
import { KeymapAction, KeymapActions, MacroAction, MacroActions } from '../actions'; import { KeymapAction, KeymapActions, MacroAction, MacroActions } from '../actions';
import { import {
DismissUndoNotificationAction, DismissUndoNotificationAction, LoadHardwareConfigurationSuccessAction, ShowNotificationAction,
LoadHardwareConfigurationSuccessAction,
ShowNotificationAction,
UndoLastAction UndoLastAction
} from '../actions/app'; } from '../actions/app';
import { ShowSaveToKeyboardButtonAction } from '../actions/device'; import { SaveConfigurationAction, ShowSaveToKeyboardButtonAction } from '../actions/device';
import { DeviceRendererService } from '../../services/device-renderer.service'; import { DeviceRendererService } from '../../services/device-renderer.service';
import { UndoUserConfigData } from '../../models/undo-user-config-data'; import { UndoUserConfigData } from '../../models/undo-user-config-data';
@@ -193,6 +185,19 @@ export class UserConfigEffects {
saveAs(blob, 'UserConfiguration.bin'); saveAs(blob, 'UserConfiguration.bin');
}); });
@Effect() loadUserConfigurationSuccess$ = this.actions$
.ofType(ActionTypes.LOAD_USER_CONFIG_SUCCESS)
.withLatestFrom(this.store.select(autoWriteUserConfiguration))
.switchMap(([action, autoWriteUserConfig]) => {
this.logService.debug('[UserConfigEffect] LOAD_USER_CONFIG_SUCCESS', {autoWriteUserConfig});
if (autoWriteUserConfig) {
return Observable.of(new SaveConfigurationAction());
}
else {
return Observable.empty();
}
});
constructor(private actions$: Actions, constructor(private actions$: Actions,
private dataStorageRepository: DataStorageRepositoryService, private dataStorageRepository: DataStorageRepositoryService,
private store: Store<AppState>, private store: Store<AppState>,

View File

@@ -43,6 +43,7 @@ export const getDeviceName = (state: AppState) => state.userConfiguration.device
export const appState = (state: AppState) => state.app; export const appState = (state: AppState) => state.app;
export const showAddonMenu = createSelector(appState, fromApp.showAddonMenu); export const showAddonMenu = createSelector(appState, fromApp.showAddonMenu);
export const autoWriteUserConfiguration = createSelector(appState, fromApp.autoWriteUserConfiguration);
export const getUndoableNotification = createSelector(appState, fromApp.getUndoableNotification); export const getUndoableNotification = createSelector(appState, fromApp.getUndoableNotification);
export const getPrevUserConfiguration = createSelector(appState, fromApp.getPrevUserConfiguration); export const getPrevUserConfiguration = createSelector(appState, fromApp.getPrevUserConfiguration);
export const runningInElectron = createSelector(appState, fromApp.runningInElectron); export const runningInElectron = createSelector(appState, fromApp.runningInElectron);

View File

@@ -11,6 +11,7 @@ import { KeyboardLayout } from '../../keyboard/keyboard-layout.enum';
export interface State { export interface State {
started: boolean; started: boolean;
showAddonMenu: boolean; showAddonMenu: boolean;
autoWriteUserConfiguration: boolean;
undoableNotification?: Notification; undoableNotification?: Notification;
navigationCountAfterNotification: number; navigationCountAfterNotification: number;
prevUserConfig?: UserConfiguration; prevUserConfig?: UserConfiguration;
@@ -23,6 +24,7 @@ export interface State {
export const initialState: State = { export const initialState: State = {
started: false, started: false,
showAddonMenu: false, showAddonMenu: false,
autoWriteUserConfiguration: false,
navigationCountAfterNotification: 0, navigationCountAfterNotification: 0,
runningInElectron: runInElectron(), runningInElectron: runInElectron(),
configLoading: true configLoading: true
@@ -37,10 +39,11 @@ export function reducer(state = initialState, action: Action & { payload: any })
}; };
} }
case ActionTypes.APP_TOGGLE_ADDON_MENU: { case ActionTypes.APPLY_COMMAND_LINE_ARGS: {
return { return {
...state, ...state,
showAddonMenu: action.payload showAddonMenu: action.payload.addons,
autoWriteUserConfiguration: action.payload.autoWriteConfig
}; };
} }
@@ -124,6 +127,7 @@ export function reducer(state = initialState, action: Action & { payload: any })
} }
export const showAddonMenu = (state: State) => state.showAddonMenu; export const showAddonMenu = (state: State) => state.showAddonMenu;
export const autoWriteUserConfiguration = (state: State) => state.autoWriteUserConfiguration;
export const getUndoableNotification = (state: State) => state.undoableNotification; export const getUndoableNotification = (state: State) => state.undoableNotification;
export const getPrevUserConfiguration = (state: State) => state.prevUserConfig; export const getPrevUserConfiguration = (state: State) => state.prevUserConfig;
export const runningInElectron = (state: State) => state.runningInElectron; export const runningInElectron = (state: State) => state.runningInElectron;