Make saving the configuration more robust (#594)
* feat: Make saving the configuration more robust * parse backup user config before return * fix some bug * Add write-userconfig.js and invalid-config.bin * throw exception if failed user config parsing * Merge branch 'master' into feat-467-make-save-more-robust * hide keymaps and macros if agent in restore mode * fix Device name settings
This commit is contained in:
committed by
László Monda
parent
00c5b69129
commit
13ec617d58
@@ -1,4 +1,4 @@
|
||||
import { Component, HostListener, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, ViewEncapsulation } from '@angular/core';
|
||||
import { animate, style, transition, trigger } from '@angular/animations';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Action, Store } from '@ngrx/store';
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
saveToKeyboardState
|
||||
} from './store';
|
||||
import { ProgressButtonState } from './store/reducers/progress-button-state';
|
||||
import { SaveUserConfigInBinaryFileAction, SaveUserConfigInJsonFileAction } from './store/actions/user-config';
|
||||
|
||||
@Component({
|
||||
selector: 'main-app',
|
||||
|
||||
@@ -4,6 +4,7 @@ import { DeviceConfigurationComponent } from './configuration/device-configurati
|
||||
import { DeviceFirmwareComponent } from './firmware/device-firmware.component';
|
||||
import { MouseSpeedComponent } from './mouse-speed/mouse-speed.component';
|
||||
import { LEDBrightnessComponent } from './led-brightness/led-brightness.component';
|
||||
import { RestoreConfigurationComponent } from './restore-configuration/restore-configuration.component';
|
||||
|
||||
export const deviceRoutes: Routes = [
|
||||
{
|
||||
@@ -29,6 +30,10 @@ export const deviceRoutes: Routes = [
|
||||
{
|
||||
path: 'firmware',
|
||||
component: DeviceFirmwareComponent
|
||||
},
|
||||
{
|
||||
path: 'restore-user-configuration',
|
||||
component: RestoreConfigurationComponent
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,4 +2,5 @@ export * from './configuration/device-configuration.component';
|
||||
export * from './firmware/device-firmware.component';
|
||||
export * from './mouse-speed/mouse-speed.component';
|
||||
export * from './led-brightness/led-brightness.component';
|
||||
export * from './restore-configuration/restore-configuration.component';
|
||||
export * from './device.routes';
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<h1>
|
||||
<i class="fa fa-exclamation-circle"></i>
|
||||
<span>Fix configuration</span>
|
||||
</h1>
|
||||
|
||||
<p>
|
||||
Your on-board device configuration is invalid.
|
||||
</p>
|
||||
<button class="btn btn-primary"
|
||||
*ngIf="state.hasBackupUserConfiguration"
|
||||
[disabled]="state.restoringUserConfiguration"
|
||||
(click)="restoreUserConfiguration()"> Restore the last valid device configuration
|
||||
</button>
|
||||
|
||||
<button class="btn btn-danger"
|
||||
*ngIf="!state.hasBackupUserConfiguration"
|
||||
[disabled]="state.restoringUserConfiguration"
|
||||
(click)="resetUserConfiguration()">Reset device configuration
|
||||
</button>
|
||||
@@ -0,0 +1,10 @@
|
||||
:host {
|
||||
overflow-y: auto;
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
p {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
import { AppState, getBackupUserConfigurationState } from '../../../store';
|
||||
import { ResetUserConfigurationAction, RestoreUserConfigurationFromBackupAction } from '../../../store/actions/device';
|
||||
import { RestoreConfigurationState } from '../../../models/restore-configuration-state';
|
||||
|
||||
@Component({
|
||||
selector: 'restore-configuration',
|
||||
templateUrl: './restore-configuration.component.html',
|
||||
styleUrls: ['./restore-configuration.component.scss'],
|
||||
host: {
|
||||
'class': 'container-fluid'
|
||||
}
|
||||
})
|
||||
export class RestoreConfigurationComponent implements OnInit, OnDestroy {
|
||||
state: RestoreConfigurationState;
|
||||
|
||||
private stateSubscription: Subscription;
|
||||
|
||||
constructor(private store: Store<AppState>,
|
||||
private cdRef: ChangeDetectorRef) {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.stateSubscription) {
|
||||
this.stateSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.stateSubscription = this.store
|
||||
.select(getBackupUserConfigurationState)
|
||||
.subscribe(data => {
|
||||
this.state = data;
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
resetUserConfiguration() {
|
||||
this.store.dispatch(new ResetUserConfigurationAction());
|
||||
}
|
||||
|
||||
restoreUserConfiguration(): void {
|
||||
this.store.dispatch(new RestoreUserConfigurationFromBackupAction());
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
<input #deviceName cancelable
|
||||
class="pane-title__name"
|
||||
type="text"
|
||||
[readonly]="state.restoreUserConfiguration"
|
||||
(change)="editDeviceName($event.target.value)"
|
||||
(keyup.enter)="deviceName.blur()"
|
||||
(keyup)="calculateHeaderTextWidth($event.target.value)">
|
||||
@@ -17,33 +18,43 @@
|
||||
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'configuration')"></i>
|
||||
</div>
|
||||
<ul [@toggler]="animation['configuration']">
|
||||
<li class="sidebar__level-2--item">
|
||||
<li class="sidebar__level-2--item"
|
||||
*ngIf="!state.restoreUserConfiguration">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/device/mouse-speed']"
|
||||
[class.disabled]="updatingFirmware$ | async">Mouse speed</a>
|
||||
[class.disabled]="state.updatingFirmware">Mouse speed</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar__level-2--item">
|
||||
<li class="sidebar__level-2--item"
|
||||
*ngIf="!state.restoreUserConfiguration">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/device/led-brightness']"
|
||||
[class.disabled]="updatingFirmware$ | async">LED brightness</a>
|
||||
[class.disabled]="state.updatingFirmware">LED brightness</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar__level-2--item">
|
||||
<li class="sidebar__level-2--item"
|
||||
*ngIf="!state.restoreUserConfiguration">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/device/configuration']"
|
||||
[class.disabled]="updatingFirmware$ | async">Configuration</a>
|
||||
[class.disabled]="state.updatingFirmware">Configuration</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar__level-2--item"
|
||||
*ngIf="state.restoreUserConfiguration">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/device/restore-user-configuration']">Fix configuration</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar__level-2--item">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/device/firmware']"
|
||||
[class.disabled]="updatingFirmware$ | async">Firmware</a>
|
||||
[class.disabled]="state.updatingFirmware">Firmware</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="sidebar__level-1--item">
|
||||
<li class="sidebar__level-1--item"
|
||||
*ngIf="!state.restoreUserConfiguration">
|
||||
<div class="sidebar__level-1">
|
||||
<i class="fa fa-keyboard-o"></i> Keymaps
|
||||
<!--a [routerLink]="['/keymap/add']"
|
||||
@@ -55,10 +66,10 @@
|
||||
(click)="toggleHide($event, 'keymap')"></i>
|
||||
</div>
|
||||
<ul [@toggler]="animation['keymap']">
|
||||
<li *ngFor="let keymap of keymaps$ | async" class="sidebar__level-2--item">
|
||||
<li *ngFor="let keymap of state.keymaps" class="sidebar__level-2--item">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/keymap', keymap.abbreviation]"
|
||||
[class.disabled]="updatingFirmware$ | async">{{keymap.name}}</a>
|
||||
[class.disabled]="state.updatingFirmware">{{keymap.name}}</a>
|
||||
<i *ngIf="keymap.isDefault" class="fa fa-star sidebar__fav"
|
||||
title="This is the default keymap which gets activated when powering the keyboard."
|
||||
data-toggle="tooltip" data-placement="bottom"></i>
|
||||
@@ -66,26 +77,27 @@
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="sidebar__level-1--item">
|
||||
<li class="sidebar__level-1--item"
|
||||
*ngIf="!state.restoreUserConfiguration">
|
||||
<div class="sidebar__level-1">
|
||||
<i class="fa fa-play"></i> Macros
|
||||
<a (click)="addMacro()"
|
||||
class="btn btn-default pull-right btn-sm"
|
||||
[class.disabled]="updatingFirmware$ | async">
|
||||
[class.disabled]="state.updatingFirmware">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'macro')"></i>
|
||||
</div>
|
||||
<ul [@toggler]="animation['macro']">
|
||||
<li *ngFor="let macro of macros$ | async" class="sidebar__level-2--item">
|
||||
<li *ngFor="let macro of state.macros" class="sidebar__level-2--item">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/macro', macro.id]"
|
||||
[class.disabled]="updatingFirmware$ | async">{{macro.name}}</a>
|
||||
[class.disabled]="state.updatingFirmware">{{macro.name}}</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="sidebar__level-1--item" *ngIf="showAddonMenu$ | async">
|
||||
<li class="sidebar__level-1--item" *ngIf="state.showAddonMenu">
|
||||
<div class="sidebar__level-1">
|
||||
<i class="fa fa-puzzle-piece"></i> Add-on modules
|
||||
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'addon')"></i>
|
||||
@@ -94,25 +106,25 @@
|
||||
<li class="sidebar__level-2--item" data-name="Key cluster" data-abbrev="">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/add-on', 'Key cluster']"
|
||||
[class.disabled]="updatingFirmware$ | async">Key cluster</a>
|
||||
[class.disabled]="state.updatingFirmware">Key cluster</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar__level-2--item" data-name="Trackball" data-abbrev="">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/add-on', 'Trackball']"
|
||||
[class.disabled]="updatingFirmware$ | async">Trackball</a>
|
||||
[class.disabled]="state.updatingFirmware">Trackball</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar__level-2--item" data-name="Toucpad" data-abbrev="">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/add-on', 'Touchpad']"
|
||||
[class.disabled]="updatingFirmware$ | async">Touchpad</a>
|
||||
[class.disabled]="state.updatingFirmware">Touchpad</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar__level-2--item" data-name="Trackpoint" data-abbrev="">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/add-on', 'Trackpoint']"
|
||||
[class.disabled]="updatingFirmware$ | async">Trackpoint</a>
|
||||
[class.disabled]="state.updatingFirmware">Trackpoint</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -123,19 +135,19 @@
|
||||
<div class="sidebar__level-0">
|
||||
<i class="uhk-icon uhk-icon-agent-icon"></i> Agent
|
||||
<i class="fa fa-chevron-up pull-right"
|
||||
(click)="toggleHide($event, 'agent')"></i>
|
||||
(click)="toggleHide($event, 'agent')"></i>
|
||||
</div>
|
||||
<ul [@toggler]="animation['agent']">
|
||||
<li class="sidebar__level-2--item">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/settings']"
|
||||
[class.disabled]="updatingFirmware$ | async">Settings</a>
|
||||
[class.disabled]="state.updatingFirmware">Settings</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar__level-2--item">
|
||||
<div class="sidebar__level-2" [routerLinkActive]="['active']">
|
||||
<a [routerLink]="['/about']"
|
||||
[class.disabled]="updatingFirmware$ | async">About</a>
|
||||
[class.disabled]="state.updatingFirmware">About</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
import { AfterContentInit, Component, ElementRef, OnDestroy, Renderer2, ViewChild } from '@angular/core';
|
||||
import {
|
||||
AfterContentInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
OnDestroy, OnInit,
|
||||
Renderer2,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
import { Keymap, Macro } from 'uhk-common';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/let';
|
||||
|
||||
import { AppState, getDeviceName, runningInElectron, showAddonMenu, updatingFirmware } from '../../store';
|
||||
import { AppState, getSideMenuPageState } from '../../store';
|
||||
import { MacroActions } from '../../store/actions';
|
||||
import { getKeymaps, getMacros } from '../../store/reducers/user-configuration';
|
||||
import * as util from '../../util';
|
||||
import { RenameUserConfigurationAction } from '../../store/actions/user-config';
|
||||
import { SideMenuPageState } from '../../models/side-menu-page-state';
|
||||
|
||||
@Component({
|
||||
animations: [
|
||||
@@ -30,24 +37,19 @@ import { RenameUserConfigurationAction } from '../../store/actions/user-config';
|
||||
],
|
||||
selector: 'side-menu',
|
||||
templateUrl: './side-menu.component.html',
|
||||
styleUrls: ['./side-menu.component.scss']
|
||||
styleUrls: ['./side-menu.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SideMenuComponent implements AfterContentInit, OnDestroy {
|
||||
showAddonMenu$: Observable<boolean>;
|
||||
runInElectron$: Observable<boolean>;
|
||||
updatingFirmware$: Observable<boolean>;
|
||||
|
||||
deviceName$: Observable<string>;
|
||||
deviceNameSubscription: Subscription;
|
||||
keymaps$: Observable<Keymap[]>;
|
||||
macros$: Observable<Macro[]>;
|
||||
export class SideMenuComponent implements AfterContentInit, OnInit, OnDestroy {
|
||||
state: SideMenuPageState;
|
||||
animation: { [key: string]: 'active' | 'inactive' };
|
||||
deviceNameValue: string;
|
||||
updatingFirmware = false;
|
||||
updatingFirmwareSubscription: Subscription;
|
||||
@ViewChild('deviceName') deviceName: ElementRef;
|
||||
|
||||
constructor(private store: Store<AppState>, private renderer: Renderer2) {
|
||||
private stateSubscription: Subscription;
|
||||
|
||||
constructor(private store: Store<AppState>,
|
||||
private renderer: Renderer2,
|
||||
private cdRef: ChangeDetectorRef) {
|
||||
this.animation = {
|
||||
device: 'active',
|
||||
configuration: 'active',
|
||||
@@ -55,20 +57,13 @@ export class SideMenuComponent implements AfterContentInit, OnDestroy {
|
||||
macro: 'active',
|
||||
addon: 'active'
|
||||
};
|
||||
}
|
||||
|
||||
this.keymaps$ = store.let(getKeymaps());
|
||||
this.macros$ = store.let(getMacros());
|
||||
|
||||
this.showAddonMenu$ = this.store.select(showAddonMenu);
|
||||
this.runInElectron$ = this.store.select(runningInElectron);
|
||||
this.deviceName$ = store.select(getDeviceName);
|
||||
this.deviceNameSubscription = this.deviceName$.subscribe(name => {
|
||||
this.deviceNameValue = name;
|
||||
ngOnInit(): void {
|
||||
this.stateSubscription = this.store.select(getSideMenuPageState).subscribe(data => {
|
||||
this.state = data;
|
||||
this.setDeviceName();
|
||||
});
|
||||
this.updatingFirmware$ = store.select(updatingFirmware);
|
||||
this.updatingFirmwareSubscription = this.updatingFirmware$.subscribe(updating => {
|
||||
this.updatingFirmware = updating;
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -77,12 +72,13 @@ export class SideMenuComponent implements AfterContentInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.deviceNameSubscription.unsubscribe();
|
||||
this.updatingFirmwareSubscription.unsubscribe();
|
||||
if (this.stateSubscription) {
|
||||
this.stateSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
toggleHide(event: Event, type: string) {
|
||||
if (this.updatingFirmware) {
|
||||
if (this.state.updatingFirmware) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -110,7 +106,7 @@ export class SideMenuComponent implements AfterContentInit, OnDestroy {
|
||||
}
|
||||
|
||||
editDeviceName(name: string): void {
|
||||
if (!util.isValidName(name) || name.trim() === this.deviceNameValue) {
|
||||
if (!util.isValidName(name) || name.trim() === this.state.deviceName) {
|
||||
this.setDeviceName();
|
||||
return;
|
||||
}
|
||||
@@ -126,7 +122,7 @@ export class SideMenuComponent implements AfterContentInit, OnDestroy {
|
||||
|
||||
private setDeviceName(): void {
|
||||
if (this.deviceName) {
|
||||
this.renderer.setProperty(this.deviceName.nativeElement, 'value', this.deviceNameValue);
|
||||
this.renderer.setProperty(this.deviceName.nativeElement, 'value', this.state.deviceName);
|
||||
this.calculateHeaderTextWidth(this.deviceName.nativeElement.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface RestoreConfigurationState {
|
||||
restoringUserConfiguration: boolean;
|
||||
hasBackupUserConfiguration: boolean;
|
||||
}
|
||||
11
packages/uhk-web/src/app/models/side-menu-page-state.ts
Normal file
11
packages/uhk-web/src/app/models/side-menu-page-state.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Keymap, Macro } from 'uhk-common';
|
||||
|
||||
export interface SideMenuPageState {
|
||||
showAddonMenu: boolean;
|
||||
runInElectron: boolean;
|
||||
updatingFirmware: boolean;
|
||||
deviceName: string;
|
||||
keymaps: Keymap[];
|
||||
macros: Macro[];
|
||||
restoreUserConfiguration: boolean;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable, NgZone } from '@angular/core';
|
||||
import { Action, Store } from '@ngrx/store';
|
||||
|
||||
import { DeviceConnectionState, IpcEvents, IpcResponse, LogService } from 'uhk-common';
|
||||
import { DeviceConnectionState, IpcEvents, IpcResponse, LogService, SaveUserConfigurationData } from 'uhk-common';
|
||||
import { AppState } from '../store';
|
||||
import { IpcCommonRenderer } from './ipc-common-renderer';
|
||||
import {
|
||||
@@ -26,8 +26,8 @@ export class DeviceRendererService {
|
||||
this.ipcRenderer.send(IpcEvents.device.setPrivilegeOnLinux);
|
||||
}
|
||||
|
||||
saveUserConfiguration(buffer: Buffer): void {
|
||||
this.ipcRenderer.send(IpcEvents.device.saveUserConfiguration, JSON.stringify(buffer));
|
||||
saveUserConfiguration(data: SaveUserConfigurationData): void {
|
||||
this.ipcRenderer.send(IpcEvents.device.saveUserConfiguration, JSON.stringify(data));
|
||||
}
|
||||
|
||||
loadConfigurationFromKeyboard(): void {
|
||||
|
||||
@@ -16,7 +16,8 @@ import {
|
||||
DeviceConfigurationComponent,
|
||||
DeviceFirmwareComponent,
|
||||
MouseSpeedComponent,
|
||||
LEDBrightnessComponent
|
||||
LEDBrightnessComponent,
|
||||
RestoreConfigurationComponent
|
||||
} from './components/device';
|
||||
import { KeymapAddComponent, KeymapEditComponent, KeymapHeaderComponent } from './components/keymap';
|
||||
import { LayersComponent } from './components/layers';
|
||||
@@ -174,7 +175,8 @@ import { Autofocus } from './directives/autofocus/autofocus.directive';
|
||||
XtermComponent,
|
||||
SliderWrapperComponent,
|
||||
EditableTextComponent,
|
||||
Autofocus
|
||||
Autofocus,
|
||||
RestoreConfigurationComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { DeviceConnectionState, IpcResponse, type } from 'uhk-common';
|
||||
import { HardwareModules } from '../../../../../uhk-common/src/models';
|
||||
import { DeviceConnectionState, HardwareModules, IpcResponse, type } from 'uhk-common';
|
||||
|
||||
const PREFIX = '[device] ';
|
||||
|
||||
@@ -24,7 +23,10 @@ export const ActionTypes = {
|
||||
UPDATE_FIRMWARE_SUCCESS: type(PREFIX + 'update firmware success'),
|
||||
UPDATE_FIRMWARE_FAILED: type(PREFIX + 'update firmware failed'),
|
||||
UPDATE_FIRMWARE_OK_BUTTON: type(PREFIX + 'update firmware ok button click'),
|
||||
MODULES_INFO_LOADED: type(PREFIX + 'module info loaded')
|
||||
MODULES_INFO_LOADED: type(PREFIX + 'module info loaded'),
|
||||
HAS_BACKUP_USER_CONFIGURATION: type(PREFIX + 'Store backup user configuration'),
|
||||
RESTORE_CONFIGURATION_FROM_BACKUP: type(PREFIX + 'Restore configuration from backup'),
|
||||
RESTORE_CONFIGURATION_FROM_BACKUP_SUCCESS: type(PREFIX + 'Restore configuration from backup success')
|
||||
};
|
||||
|
||||
export class SetPrivilegeOnLinuxAction implements Action {
|
||||
@@ -123,6 +125,21 @@ export class HardwareModulesLoadedAction implements Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class RestoreUserConfigurationFromBackupAction implements Action {
|
||||
type = ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP;
|
||||
}
|
||||
|
||||
export class HasBackupUserConfigurationAction implements Action {
|
||||
type = ActionTypes.HAS_BACKUP_USER_CONFIGURATION;
|
||||
|
||||
constructor(public payload: boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
export class RestoreUserConfigurationFromBackupSuccessAction implements Action {
|
||||
type = ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP_SUCCESS;
|
||||
}
|
||||
|
||||
export type Actions
|
||||
= SetPrivilegeOnLinuxAction
|
||||
| SetPrivilegeOnLinuxReplyAction
|
||||
@@ -142,4 +159,7 @@ export type Actions
|
||||
| UpdateFirmwareFailedAction
|
||||
| UpdateFirmwareOkButtonAction
|
||||
| HardwareModulesLoadedAction
|
||||
| RestoreUserConfigurationFromBackupAction
|
||||
| HasBackupUserConfigurationAction
|
||||
| RestoreUserConfigurationFromBackupSuccessAction
|
||||
;
|
||||
|
||||
@@ -13,11 +13,19 @@ import 'rxjs/add/operator/mergeMap';
|
||||
import 'rxjs/add/operator/withLatestFrom';
|
||||
import 'rxjs/add/operator/switchMap';
|
||||
|
||||
import { DeviceConnectionState, IpcResponse, NotificationType, UhkBuffer, UserConfiguration } from 'uhk-common';
|
||||
import {
|
||||
DeviceConnectionState,
|
||||
HardwareConfiguration,
|
||||
IpcResponse,
|
||||
NotificationType,
|
||||
UserConfiguration
|
||||
} from 'uhk-common';
|
||||
import {
|
||||
ActionTypes,
|
||||
ConnectionStateChangedAction,
|
||||
HideSaveToKeyboardButton,
|
||||
ResetUserConfigurationAction,
|
||||
RestoreUserConfigurationFromBackupSuccessAction,
|
||||
SaveConfigurationAction,
|
||||
SaveConfigurationReplyAction,
|
||||
SaveToKeyboardSuccessAction,
|
||||
@@ -93,9 +101,8 @@ export class DeviceEffects {
|
||||
saveConfiguration$: Observable<Action> = this.actions$
|
||||
.ofType(ActionTypes.SAVE_CONFIGURATION)
|
||||
.withLatestFrom(this.store)
|
||||
.map(([action, state]) => state.userConfiguration)
|
||||
.do((userConfiguration: UserConfiguration) => {
|
||||
setTimeout(() => this.sendUserConfigToKeyboard(userConfiguration), 100);
|
||||
.do(([action, state]) => {
|
||||
setTimeout(() => this.sendUserConfigToKeyboard(state.userConfiguration, state.app.hardwareConfig), 100);
|
||||
})
|
||||
.switchMap(() => Observable.empty());
|
||||
|
||||
@@ -122,8 +129,18 @@ export class DeviceEffects {
|
||||
@Effect()
|
||||
autoHideSaveToKeyboardButton$: Observable<Action> = this.actions$
|
||||
.ofType(ActionTypes.SAVE_TO_KEYBOARD_SUCCESS)
|
||||
.switchMap(() => Observable.timer(1000)
|
||||
.switchMap(() => Observable.of(new HideSaveToKeyboardButton()))
|
||||
.withLatestFrom(this.store)
|
||||
.switchMap(([action, state]) => Observable.timer(1000)
|
||||
.mergeMap(() => {
|
||||
const actions = [new HideSaveToKeyboardButton()];
|
||||
|
||||
if (state.device.hasBackupUserConfiguration) {
|
||||
actions.push(new RestoreUserConfigurationFromBackupSuccessAction());
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
|
||||
return actions;
|
||||
})
|
||||
);
|
||||
|
||||
@Effect()
|
||||
@@ -193,6 +210,10 @@ export class DeviceEffects {
|
||||
.ofType<UpdateFirmwareOkButtonAction>(ActionTypes.UPDATE_FIRMWARE_OK_BUTTON)
|
||||
.do(() => this.deviceRendererService.startConnectionPoller());
|
||||
|
||||
@Effect() restoreUserConfiguration$ = this.actions$
|
||||
.ofType<ResetUserConfigurationAction>(ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP)
|
||||
.map(() => new SaveConfigurationAction());
|
||||
|
||||
constructor(private actions$: Actions,
|
||||
private router: Router,
|
||||
private deviceRendererService: DeviceRendererService,
|
||||
@@ -201,9 +222,10 @@ export class DeviceEffects {
|
||||
private defaultUserConfigurationService: DefaultUserConfigurationService) {
|
||||
}
|
||||
|
||||
private sendUserConfigToKeyboard(userConfiguration: UserConfiguration): void {
|
||||
const uhkBuffer = new UhkBuffer();
|
||||
userConfiguration.toBinary(uhkBuffer);
|
||||
this.deviceRendererService.saveUserConfiguration(uhkBuffer.getBufferContent());
|
||||
private sendUserConfigToKeyboard(userConfiguration: UserConfiguration, hardwareConfig: HardwareConfiguration): void {
|
||||
this.deviceRendererService.saveUserConfiguration({
|
||||
uniqueId: hardwareConfig && hardwareConfig.uniqueId,
|
||||
configuration: userConfiguration.toJsonObject()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@ import 'rxjs/add/observable/of';
|
||||
import 'rxjs/add/observable/empty';
|
||||
|
||||
import {
|
||||
getHardwareConfigFromDeviceResponse,
|
||||
getUserConfigFromDeviceResponse,
|
||||
ConfigurationReply,
|
||||
HardwareConfiguration,
|
||||
LogService,
|
||||
NotificationType,
|
||||
UhkBuffer,
|
||||
@@ -43,7 +44,11 @@ import {
|
||||
ShowNotificationAction,
|
||||
UndoLastAction
|
||||
} from '../actions/app';
|
||||
import { HardwareModulesLoadedAction, ShowSaveToKeyboardButtonAction } from '../actions/device';
|
||||
import {
|
||||
HardwareModulesLoadedAction,
|
||||
ShowSaveToKeyboardButtonAction,
|
||||
HasBackupUserConfigurationAction
|
||||
} from '../actions/device';
|
||||
import { DeviceRendererService } from '../../services/device-renderer.service';
|
||||
import { UndoUserConfigData } from '../../models/undo-user-config-data';
|
||||
import { UploadFileData } from '../../models/upload-file-data';
|
||||
@@ -51,29 +56,6 @@ import { UploadFileData } from '../../models/upload-file-data';
|
||||
@Injectable()
|
||||
export class UserConfigEffects {
|
||||
|
||||
private static getUserConfigFromDeviceResponse(json: string): UserConfiguration {
|
||||
const data = JSON.parse(json);
|
||||
const userConfig = new UserConfiguration();
|
||||
userConfig.fromBinary(UhkBuffer.fromArray(data));
|
||||
|
||||
if (userConfig.userConfigMajorVersion > 0) {
|
||||
return userConfig;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static getHardwareConfigFromDeviceResponse(json: string): HardwareConfiguration {
|
||||
const data = JSON.parse(json);
|
||||
const hardwareConfig = new HardwareConfiguration();
|
||||
hardwareConfig.fromBinary(UhkBuffer.fromArray(data));
|
||||
|
||||
if (hardwareConfig.uniqueId > 0) {
|
||||
return hardwareConfig;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Effect() loadUserConfig$: Observable<Action> = defer(() => {
|
||||
return Observable.of(new LoadUserConfigSuccessAction(this.getUserConfiguration()));
|
||||
});
|
||||
@@ -146,23 +128,24 @@ export class UserConfigEffects {
|
||||
}
|
||||
|
||||
const result = [];
|
||||
let newPageDestination = ['/'];
|
||||
|
||||
try {
|
||||
const userConfig = UserConfigEffects.getUserConfigFromDeviceResponse(data.userConfiguration);
|
||||
const userConfig = getUserConfigFromDeviceResponse(data.userConfiguration);
|
||||
result.push(new LoadUserConfigSuccessAction(userConfig));
|
||||
|
||||
} catch (err) {
|
||||
this.logService.error('Eeprom user-config parse error:', err);
|
||||
result.push(
|
||||
new ShowNotificationAction({
|
||||
type: NotificationType.Error,
|
||||
message: err
|
||||
}));
|
||||
const userConfig = new UserConfiguration().fromJsonObject(data.backupConfiguration);
|
||||
|
||||
result.push(new LoadUserConfigSuccessAction(this.getUserConfiguration()));
|
||||
result.push(new HasBackupUserConfigurationAction(!!data.backupConfiguration));
|
||||
result.push(new LoadUserConfigSuccessAction(userConfig));
|
||||
|
||||
newPageDestination = ['/device/restore-user-configuration'];
|
||||
}
|
||||
|
||||
try {
|
||||
const hardwareConfig = UserConfigEffects.getHardwareConfigFromDeviceResponse(data.hardwareConfiguration);
|
||||
const hardwareConfig = getHardwareConfigFromDeviceResponse(data.hardwareConfiguration);
|
||||
result.push(new LoadHardwareConfigurationSuccessAction(hardwareConfig));
|
||||
} catch (err) {
|
||||
this.logService.error('Eeprom hardware-config parse error:', err);
|
||||
@@ -175,7 +158,7 @@ export class UserConfigEffects {
|
||||
|
||||
result.push(new HardwareModulesLoadedAction(data.modules));
|
||||
|
||||
this.router.navigate(['/']);
|
||||
this.router.navigate(newPageDestination);
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
@@ -39,7 +39,6 @@ export const metaReducers: MetaReducer<AppState>[] = environment.production
|
||||
: [storeFreeze];
|
||||
|
||||
export const getUserConfiguration = (state: AppState) => state.userConfiguration;
|
||||
export const getDeviceName = createSelector(getUserConfiguration, fromUserConfig.getDeviceName);
|
||||
|
||||
export const appState = (state: AppState) => state.app;
|
||||
|
||||
@@ -47,7 +46,6 @@ export const showAddonMenu = createSelector(appState, fromApp.showAddonMenu);
|
||||
export const getUndoableNotification = createSelector(appState, fromApp.getUndoableNotification);
|
||||
export const getPrevUserConfiguration = createSelector(appState, fromApp.getPrevUserConfiguration);
|
||||
export const runningInElectron = createSelector(appState, fromApp.runningInElectron);
|
||||
export const getHardwareConfiguration = createSelector(appState, fromApp.getHardwareConfiguration);
|
||||
export const getKeyboardLayout = createSelector(appState, fromApp.getKeyboardLayout);
|
||||
export const deviceConfigurationLoaded = createSelector(appState, fromApp.deviceConfigurationLoaded);
|
||||
export const getAgentVersionInfo = createSelector(appState, fromApp.getAgentVersionInfo);
|
||||
@@ -79,3 +77,28 @@ export const firmwareOkButtonDisabled = createSelector(deviceState, fromDevice.f
|
||||
// tslint:disable-next-line: max-line-length
|
||||
export const flashFirmwareButtonDisbabled = createSelector(runningInElectron, deviceState, (electron, state: fromDevice.State) => !electron || state.updatingFirmware);
|
||||
export const getHardwareModules = createSelector(deviceState, fromDevice.getHardwareModules);
|
||||
export const getBackupUserConfigurationState = createSelector(deviceState, fromDevice.getBackupUserConfigurationState);
|
||||
export const getRestoreUserConfiguration = createSelector(deviceState, fromDevice.getHasBackupUserConfiguration);
|
||||
|
||||
export const getSideMenuPageState = createSelector(
|
||||
showAddonMenu,
|
||||
runningInElectron,
|
||||
updatingFirmware,
|
||||
getUserConfiguration,
|
||||
getRestoreUserConfiguration,
|
||||
(showAddonMenuValue: boolean,
|
||||
runningInElectronValue: boolean,
|
||||
updatingFirmwareValue: boolean,
|
||||
userConfiguration: UserConfiguration,
|
||||
restoreUserConfiguration: boolean) => {
|
||||
return {
|
||||
showAddonMenu: showAddonMenuValue,
|
||||
runInElectron: runningInElectronValue,
|
||||
updatingFirmware: updatingFirmwareValue,
|
||||
deviceName: userConfiguration.deviceName,
|
||||
keymaps: userConfiguration.keymaps,
|
||||
macros: userConfiguration.macros,
|
||||
restoreUserConfiguration
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -152,7 +152,6 @@ export const showAddonMenu = (state: State) => state.showAddonMenu;
|
||||
export const getUndoableNotification = (state: State) => state.undoableNotification;
|
||||
export const getPrevUserConfiguration = (state: State) => state.prevUserConfig;
|
||||
export const runningInElectron = (state: State) => state.runningInElectron;
|
||||
export const getHardwareConfiguration = (state: State) => state.hardwareConfig;
|
||||
export const getKeyboardLayout = (state: State): KeyboardLayout => {
|
||||
if (state.hardwareConfig && state.hardwareConfig.isIso) {
|
||||
return KeyboardLayout.ISO;
|
||||
|
||||
@@ -6,11 +6,13 @@ import {
|
||||
ConnectionStateChangedAction,
|
||||
HardwareModulesLoadedAction,
|
||||
SaveConfigurationAction,
|
||||
HasBackupUserConfigurationAction,
|
||||
UpdateFirmwareFailedAction
|
||||
} from '../actions/device';
|
||||
import { ActionTypes as AppActions, ElectronMainLogReceivedAction } from '../actions/app';
|
||||
import { initProgressButtonState, ProgressButtonState } from './progress-button-state';
|
||||
import { XtermCssClass, XtermLog } from '../../models/xterm-log';
|
||||
import { RestoreConfigurationState } from '../../models/restore-configuration-state';
|
||||
|
||||
export interface State {
|
||||
connected: boolean;
|
||||
@@ -20,6 +22,8 @@ export interface State {
|
||||
firmwareUpdateFinished: boolean;
|
||||
modules: HardwareModules;
|
||||
log: Array<XtermLog>;
|
||||
restoringUserConfiguration: boolean;
|
||||
hasBackupUserConfiguration: boolean;
|
||||
}
|
||||
|
||||
export const initialState: State = {
|
||||
@@ -37,7 +41,9 @@ export const initialState: State = {
|
||||
firmwareVersion: ''
|
||||
}
|
||||
},
|
||||
log: [{message: '', cssClass: XtermCssClass.standard}]
|
||||
log: [{message: '', cssClass: XtermCssClass.standard}],
|
||||
restoringUserConfiguration: false,
|
||||
hasBackupUserConfiguration: false
|
||||
};
|
||||
|
||||
export function reducer(state = initialState, action: Action) {
|
||||
@@ -87,7 +93,8 @@ export function reducer(state = initialState, action: Action) {
|
||||
showButton: true,
|
||||
text: 'Saved!',
|
||||
action: null
|
||||
}
|
||||
},
|
||||
restoringUserConfiguration: false
|
||||
};
|
||||
}
|
||||
|
||||
@@ -167,6 +174,25 @@ export function reducer(state = initialState, action: Action) {
|
||||
modules: (action as HardwareModulesLoadedAction).payload
|
||||
};
|
||||
|
||||
case ActionTypes.RESET_USER_CONFIGURATION:
|
||||
case ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP:
|
||||
return {
|
||||
...state,
|
||||
restoringUserConfiguration: true
|
||||
};
|
||||
|
||||
case ActionTypes.HAS_BACKUP_USER_CONFIGURATION:
|
||||
return {
|
||||
...state,
|
||||
hasBackupUserConfiguration: (action as HasBackupUserConfigurationAction).payload
|
||||
};
|
||||
|
||||
case ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
hasBackupUserConfiguration: false
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
@@ -179,3 +205,10 @@ export const getSaveToKeyboardState = (state: State) => state.saveToKeyboard;
|
||||
export const xtermLog = (state: State) => state.log;
|
||||
export const firmwareOkButtonDisabled = (state: State) => !state.firmwareUpdateFinished;
|
||||
export const getHardwareModules = (state: State) => state.modules;
|
||||
export const getHasBackupUserConfiguration = (state: State) => state.hasBackupUserConfiguration;
|
||||
export const getBackupUserConfigurationState = (state: State): RestoreConfigurationState => {
|
||||
return {
|
||||
restoringUserConfiguration: state.restoringUserConfiguration,
|
||||
hasBackupUserConfiguration: state.hasBackupUserConfiguration
|
||||
};
|
||||
};
|
||||
|
||||
@@ -494,5 +494,3 @@ function setKeyActionToLayer(newLayer: Layer, moduleIndex: number, keyIndex: num
|
||||
newModule.keyActions = newModule.keyActions.slice();
|
||||
newModule.keyActions[keyIndex] = newKeyAction;
|
||||
}
|
||||
|
||||
export const getDeviceName = (state: UserConfiguration) => state.deviceName;
|
||||
|
||||
Reference in New Issue
Block a user