refactor: use rxjs pipe syntax (#900)

The `let` operator was not migrated because earlier two reducer needed be refactored
- user-configuration reducer
- present reducer

This commit is prerequisite of the angular upgrade.
This commit is contained in:
Róbert Kiss
2019-01-20 23:23:01 +01:00
committed by László Monda
parent e18a98d8bb
commit bb31c2cefa
27 changed files with 683 additions and 599 deletions

View File

@@ -14,18 +14,13 @@ import {
UpdateFirmwareData
} from 'uhk-common';
import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { interval } from 'rxjs/observable/interval';
import { fromPromise } from 'rxjs/observable/fromPromise';
import { distinctUntilChanged, startWith, switchMap, tap } from 'rxjs/operators';
import { emptyDir } from 'fs-extra';
import * as path from 'path';
import 'rxjs/add/observable/interval';
import 'rxjs/add/observable/fromPromise';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/distinctUntilChanged';
import { TmpFirmware } from '../models/tmp-firmware';
import { QueueManager } from './queue-manager';
import {
@@ -254,14 +249,16 @@ export class DeviceService {
return;
}
this.pollTimer$ = Observable.interval(1000)
.startWith(0)
.switchMap(() => Observable.fromPromise(this.device.getDeviceConnectionStateAsync()))
.distinctUntilChanged<DeviceConnectionState>(isEqual)
.do((state: DeviceConnectionState) => {
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
this.logService.info('[DeviceService] Device connection state changed to:', state);
})
this.pollTimer$ = interval(1000)
.pipe(
startWith(0),
switchMap(() => fromPromise(this.device.getDeviceConnectionStateAsync())),
distinctUntilChanged<DeviceConnectionState>(isEqual),
tap((state: DeviceConnectionState) => {
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
this.logService.info('[DeviceService] Device connection state changed to:', state);
})
)
.subscribe();
}

View File

@@ -4,8 +4,6 @@ import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { Action, Store } from '@ngrx/store';
import 'rxjs/add/operator/last';
import { DoNotUpdateAppAction, UpdateAppAction } from './store/actions/app-update.action';
import { EnableUsbStackTestAction } from './store/actions/device';
import {

View File

@@ -2,7 +2,7 @@ import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/pluck';
import { pluck } from 'rxjs/operators';
@Component({
selector: 'add-on',
@@ -18,6 +18,8 @@ export class AddOnComponent {
constructor(route: ActivatedRoute) {
this.name$ = route
.params
.pluck<{}, string>('name');
.pipe(
pluck<{}, string>('name')
);
}
}

View File

@@ -4,8 +4,7 @@ import { Keymap } from 'uhk-common';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/combineLatest';
import 'rxjs/add/operator/publishReplay';
import { combineLatest, publishReplay, refCount } from 'rxjs/operators';
import { AppState } from '../../../store';
import { KeymapActions } from '../../../store/actions';
@@ -29,11 +28,13 @@ export class KeymapAddComponent {
this.filterExpression$ = new BehaviorSubject('');
this.presets$ = this.presetsAll$
.combineLatest(this.filterExpression$, (keymaps: Keymap[], filterExpression: string) => {
return keymaps.filter((keymap: Keymap) => keymap.name.toLocaleLowerCase().includes(filterExpression));
})
.publishReplay(1)
.refCount();
.pipe(
combineLatest(this.filterExpression$, (keymaps: Keymap[], filterExpression: string) => {
return keymaps.filter((keymap: Keymap) => keymap.name.toLocaleLowerCase().includes(filterExpression));
}),
publishReplay(1),
refCount()
);
}
filterKeyboards(filterExpression: string) {

View File

@@ -3,15 +3,14 @@ import { CanActivate, Router } from '@angular/router';
import { Keymap } from 'uhk-common';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { switchMap, tap } from 'rxjs/operators';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/let';
import 'rxjs/add/operator/switchMap';
import { Store } from '@ngrx/store';
import { AppState } from '../../../store/index';
import { AppState } from '../../../store';
import { getKeymaps } from '../../../store/reducers/user-configuration';
@Injectable()
@@ -22,12 +21,14 @@ export class KeymapEditGuard implements CanActivate {
canActivate(): Observable<boolean> {
return this.store
.let(getKeymaps())
.do((keymaps: Keymap[]) => {
const defaultKeymap = keymaps.find(keymap => keymap.isDefault);
if (defaultKeymap) {
this.router.navigate(['/keymap', defaultKeymap.abbreviation]);
}
})
.switchMap(() => Observable.of(false));
.pipe(
tap((keymaps: Keymap[]) => {
const defaultKeymap = keymaps.find(keymap => keymap.isDefault);
if (defaultKeymap) {
this.router.navigate(['/keymap', defaultKeymap.abbreviation]);
}
}),
switchMap(() => of(false))
);
}
}

View File

@@ -4,13 +4,8 @@ import { Store } from '@ngrx/store';
import { Keymap } from 'uhk-common';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/first';
import { combineLatest, first, map, pluck, publishReplay, refCount, switchMap } from 'rxjs/operators';
import 'rxjs/add/operator/let';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/publishReplay';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/pluck';
import 'rxjs/add/operator/combineLatest';
import { saveAs } from 'file-saver';
@@ -42,13 +37,17 @@ export class KeymapEditComponent {
route: ActivatedRoute) {
this.keymap$ = route
.params
.pluck<{}, string>('abbr')
.switchMap((abbr: string) => store.let(getKeymap(abbr)))
.publishReplay(1)
.refCount();
.pipe(
pluck<{}, string>('abbr'),
switchMap((abbr: string) => store.let(getKeymap(abbr))),
publishReplay(1),
refCount()
);
this.deletable$ = store.let(getKeymaps())
.map((keymaps: Keymap[]) => keymaps.length > 1);
.pipe(
map((keymaps: Keymap[]) => keymaps.length > 1)
);
this.keyboardLayout$ = store.select(getKeyboardLayout);
this.allowLayerDoubleTap$ = store.select(layerDoubleTapSupported);
@@ -56,17 +55,21 @@ export class KeymapEditComponent {
downloadKeymap() {
const exportableJSON$: Observable<string> = this.keymap$
.switchMap(keymap => this.toExportableJSON(keymap))
.map(exportableJSON => JSON.stringify(exportableJSON));
.pipe(
switchMap(keymap => this.toExportableJSON(keymap)),
map(exportableJSON => JSON.stringify(exportableJSON))
);
this.keymap$
.combineLatest(exportableJSON$)
.first()
.pipe(
combineLatest(exportableJSON$),
first()
)
.subscribe(latest => {
const keymap = latest[0];
const exportableJSON = latest[1];
const fileName = keymap.name + '_keymap.json';
saveAs(new Blob([exportableJSON], {type: 'application/json'}), fileName);
saveAs(new Blob([exportableJSON], { type: 'application/json' }), fileName);
});
}
@@ -82,18 +85,20 @@ export class KeymapEditComponent {
private toExportableJSON(keymap: Keymap): Observable<any> {
return this.store
.let(getUserConfiguration())
.first()
.map(userConfiguration => {
return {
site: 'https://ultimatehackingkeyboard.com',
description: 'Ultimate Hacking Keyboard keymap',
keyboardModel: 'UHK60',
userConfigMajorVersion: userConfiguration.userConfigMajorVersion,
userConfigMinorVersion: userConfiguration.userConfigMinorVersion,
userConfigPatchVersion: userConfiguration.userConfigPatchVersion,
objectType: 'keymap',
objectValue: keymap.toJsonObject()
};
});
.pipe(
first(),
map(userConfiguration => {
return {
site: 'https://ultimatehackingkeyboard.com',
description: 'Ultimate Hacking Keyboard keymap',
keyboardModel: 'UHK60',
userConfigMajorVersion: userConfiguration.userConfigMajorVersion,
userConfigMinorVersion: userConfiguration.userConfigMinorVersion,
userConfigPatchVersion: userConfiguration.userConfigPatchVersion,
objectType: 'keymap',
objectValue: keymap.toJsonObject()
};
})
);
}
}

View File

@@ -5,7 +5,7 @@ import { Macro, MacroAction } from 'uhk-common';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/pluck';
import { pluck, switchMap } from 'rxjs/operators';
import { MacroActions } from '../../../store/actions';
import { AppState, macroPlaybackSupported } from '../../../store';
@@ -26,14 +26,17 @@ export class MacroEditComponent implements OnDestroy {
macroPlaybackSupported$: Observable<boolean>;
private subscription: Subscription;
constructor(private store: Store<AppState>, public route: ActivatedRoute) {
this.subscription = route
.params
.pluck<{}, string>('id')
.switchMap((id: string) => {
this.macroId = +id;
return store.let(getMacro(this.macroId));
})
.pipe(
pluck<{}, string>('id'),
switchMap((id: string) => {
this.macroId = +id;
return store.let(getMacro(this.macroId));
})
)
.subscribe((macro: Macro) => {
this.macro = macro;
});

View File

@@ -3,9 +3,8 @@ import { CanActivate, Router } from '@angular/router';
import { Macro } from 'uhk-common';
import { Observable } from 'rxjs/Observable';
import { map } from 'rxjs/operators';
import 'rxjs/add/operator/let';
import 'rxjs/add/operator/map';
import { Store } from '@ngrx/store';
@@ -20,12 +19,14 @@ export class MacroNotFoundGuard implements CanActivate {
canActivate(): Observable<boolean> {
return this.store
.let(getMacros())
.map((macros: Macro[]) => {
const hasMacros = macros.length > 0;
if (hasMacros) {
this.router.navigate(['/macro', macros[0].id]);
}
return !hasMacros;
});
.pipe(
map((macros: Macro[]) => {
const hasMacros = macros.length > 0;
if (hasMacros) {
this.router.navigate(['/macro', macros[0].id]);
}
return !hasMacros;
})
);
}
}

View File

@@ -16,8 +16,7 @@ import { animate, keyframes, state, style, transition, trigger } from '@angular/
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import 'rxjs/add/operator/combineLatest';
import 'rxjs/add/operator/map';
import { combineLatest, map } from 'rxjs/operators';
import {
KeyAction,
@@ -155,9 +154,10 @@ export class PopoverComponent implements OnChanges {
private cdRef: ChangeDetectorRef) {
this.animationState = 'closed';
this.keymaps$ = store.let(getKeymaps())
.combineLatest(this.currentKeymap$)
.map(([keymaps, currentKeymap]: [Keymap[], Keymap]) =>
keymaps.filter((keymap: Keymap) => currentKeymap.abbreviation !== keymap.abbreviation)
.pipe(
combineLatest(this.currentKeymap$),
map(([keymaps, currentKeymap]: [Keymap[], Keymap]) =>
keymaps.filter((keymap: Keymap) => currentKeymap.abbreviation !== keymap.abbreviation))
);
this.macroPlaybackSupported$ = store.select(macroPlaybackSupported);
}

View File

@@ -12,9 +12,6 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
import { Store } from '@ngrx/store';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/let';
import { AppState, getSideMenuPageState } from '../../store';
import { MacroActions } from '../../store/actions';

View File

@@ -3,8 +3,7 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { NouisliderComponent } from 'ng2-nouislider';
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
export interface SliderPips {
mode: string;
@@ -80,9 +79,10 @@ export class SliderWrapperComponent implements AfterViewInit, ControlValueAccess
if (!this.changeObserver$) {
Observable.create(observer => {
this.changeObserver$ = observer;
}).debounceTime(this.changeDebounceTime)
.distinctUntilChanged()
.subscribe(this.propagateChange);
}).pipe(
debounceTime(this.changeDebounceTime),
distinctUntilChanged()
).subscribe(this.propagateChange);
return; // No change event on first change as the value is just being set
}

View File

@@ -14,8 +14,8 @@ import {
} from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
import { of } from 'rxjs/observable/of';
import { map } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import {
@@ -319,40 +319,44 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
const playMacroAction: PlayMacroAction = keyAction;
return this.store
.select(appState => appState.userConfiguration.macros)
.map(macroState => macroState.find(macro => {
return macro.id === playMacroAction.macroId;
}).name)
.map(macroName => {
const content: NameValuePair[] = [
{
name: 'Action type',
value: 'Play macro'
},
{
name: 'Macro name',
value: macroName
}
];
return content;
});
.pipe(
map(macroState => macroState.find(macro => {
return macro.id === playMacroAction.macroId;
}).name),
map(macroName => {
const content: NameValuePair[] = [
{
name: 'Action type',
value: 'Play macro'
},
{
name: 'Macro name',
value: macroName
}
];
return content;
})
);
} else if (keyAction instanceof SwitchKeymapAction) {
const switchKeymapAction: SwitchKeymapAction = keyAction;
return this.store
.select(appState => appState.userConfiguration.keymaps)
.map(keymaps => keymaps.find(keymap => keymap.abbreviation === switchKeymapAction.keymapAbbreviation).name)
.map(keymapName => {
const content: NameValuePair[] = [
{
name: 'Action type',
value: 'Switch keymap'
},
{
name: 'Keymap',
value: keymapName
}
];
return content;
});
.pipe(
map(keymaps => keymaps.find(keymap => keymap.abbreviation === switchKeymapAction.keymapAbbreviation).name),
map(keymapName => {
const content: NameValuePair[] = [
{
name: 'Action type',
value: 'Switch keymap'
},
{
name: 'Keymap',
value: keymapName
}
];
return content;
})
);
} else if (keyAction instanceof SwitchLayerAction) {
const switchLayerAction: SwitchLayerAction = keyAction;
const content: NameValuePair[] =
@@ -370,9 +374,9 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
value: switchLayerAction.switchLayerMode === SwitchLayerMode.toggle ? 'On' : 'Off'
}
];
return Observable.of(content);
return of(content);
}
return Observable.of([]);
return of([]);
}
}

View File

@@ -3,8 +3,7 @@ import { Store } from '@ngrx/store';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import { tap } from 'rxjs/operators';
import { AppState, bootloaderActive } from '../store';
@@ -15,10 +14,12 @@ export class UhkDeviceBootloaderNotActiveGuard implements CanActivate {
canActivate(): Observable<boolean> {
return this.store.select(bootloaderActive)
.do(active => {
if (!active) {
this.router.navigate(['/']);
}
});
.pipe(
tap(active => {
if (!active) {
this.router.navigate(['/']);
}
})
);
}
}

View File

@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import { map, tap } from 'rxjs/operators';
import { AppState, deviceConnected } from '../store/index';
@@ -14,11 +14,13 @@ export class UhkDeviceConnectedGuard implements CanActivate {
canActivate(): Observable<boolean> {
return this.store.select(deviceConnected)
.do(connected => {
if (connected) {
this.router.navigate(['/']);
}
})
.map(connected => !connected);
.pipe(
tap(connected => {
if (connected) {
this.router.navigate(['/']);
}
}),
map(connected => !connected)
);
}
}

View File

@@ -2,10 +2,9 @@ import { CanActivate, Router } from '@angular/router';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import { tap } from 'rxjs/operators';
import { AppState, deviceConnected } from '../store/index';
import { AppState, deviceConnected } from '../store';
import { Store } from '@ngrx/store';
@Injectable()
@@ -15,10 +14,12 @@ export class UhkDeviceDisconnectedGuard implements CanActivate {
canActivate(): Observable<boolean> {
return this.store.select(deviceConnected)
.do(connected => {
if (!connected) {
this.router.navigate(['/detection']);
}
});
.pipe(
tap(connected => {
if (!connected) {
this.router.navigate(['/detection']);
}
})
);
}
}

View File

@@ -2,9 +2,7 @@ import { CanActivate, Router } from '@angular/router';
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import { map, tap } from 'rxjs/operators';
import { AppState, hasDevicePermission } from '../store/index';
@@ -15,11 +13,13 @@ export class UhkDeviceInitializedGuard implements CanActivate {
canActivate(): Observable<boolean> {
return this.store.select(hasDevicePermission)
.do(hasPermission => {
if (hasPermission) {
this.router.navigate(['/detection']);
}
})
.map(hasPermission => !hasPermission);
.pipe(
tap(hasPermission => {
if (hasPermission) {
this.router.navigate(['/detection']);
}
}),
map(hasPermission => !hasPermission)
);
}
}

View File

@@ -3,8 +3,7 @@ import { Store } from '@ngrx/store';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import { map, tap } from 'rxjs/operators';
import { AppState, deviceConfigurationLoaded } from '../store';
@@ -15,11 +14,13 @@ export class UhkDeviceLoadedGuard implements CanActivate {
canActivate(): Observable<boolean> {
return this.store.select(deviceConfigurationLoaded)
.do(loaded => {
if (loaded) {
this.router.navigate(['/']);
}
})
.map(loaded => !loaded);
.pipe(
tap(loaded => {
if (loaded) {
this.router.navigate(['/']);
}
}),
map(loaded => !loaded)
);
}
}

View File

@@ -3,7 +3,7 @@ import { Store } from '@ngrx/store';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import { tap } from 'rxjs/operators';
import { AppState, deviceConfigurationLoaded } from '../store';
@@ -14,10 +14,12 @@ export class UhkDeviceLoadingGuard implements CanActivate {
canActivate(): Observable<boolean> {
return this.store.select(deviceConfigurationLoaded)
.do(loaded => {
if (!loaded) {
this.router.navigate(['/loading']);
}
});
.pipe(
tap(loaded => {
if (!loaded) {
this.router.navigate(['/loading']);
}
})
);
}
}

View File

@@ -2,23 +2,24 @@ import { CanActivate, Router } from '@angular/router';
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { tap } from 'rxjs/operators';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import { AppState, hasDevicePermission } from '../store/index';
import { AppState, hasDevicePermission } from '../store';
@Injectable()
export class UhkDeviceUninitializedGuard implements CanActivate {
constructor(private store: Store<AppState>, private router: Router) { }
constructor(private store: Store<AppState>, private router: Router) {
}
canActivate(): Observable<boolean> {
return this.store.select(hasDevicePermission)
.do(hasPermission => {
if (!hasPermission) {
this.router.navigate(['/privilege']);
}
});
.pipe(
tap(hasPermission => {
if (!hasPermission) {
this.router.navigate(['/privilege']);
}
})
);
}
}

View File

@@ -2,10 +2,7 @@ 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/map';
import 'rxjs/add/operator/first';
import { first, map, tap } from 'rxjs/operators';
import { LogService, NotificationType } from 'uhk-common';
@@ -19,27 +16,33 @@ export class AppUpdateEffect {
@Effect({ dispatch: false })
appStart$: Observable<Action> = this.actions$
.ofType(ActionTypes.UPDATE_APP)
.first()
.do(() => {
this.appUpdateRendererService.sendUpdateAndRestartApp();
});
.pipe(
first(),
tap(() => {
this.appUpdateRendererService.sendUpdateAndRestartApp();
})
);
@Effect({ dispatch: false }) checkForUpdate$: Observable<Action> = this.actions$
.ofType(AutoUpdateActionTypes.CHECK_FOR_UPDATE_NOW)
.do(() => {
this.logService.debug('[AppUpdateEffect] call checkForUpdate');
this.appUpdateRendererService.checkForUpdate();
});
.pipe(
tap(() => {
this.logService.debug('[AppUpdateEffect] call checkForUpdate');
this.appUpdateRendererService.checkForUpdate();
})
);
@Effect() handleError$: Observable<Action> = this.actions$
.ofType<UpdateErrorAction>(ActionTypes.UPDATE_ERROR)
.map(action => action.payload)
.map((message: string) => {
return new ShowNotificationAction({
type: NotificationType.Error,
message
});
});
.pipe(
map(action => action.payload),
map((message: string) => {
return new ShowNotificationAction({
type: NotificationType.Error,
message
});
})
);
constructor(private actions$: Actions,
private appUpdateRendererService: AppUpdateRendererService,

View File

@@ -2,14 +2,9 @@ import { Injectable } from '@angular/core';
import { Action, Store } from '@ngrx/store';
import { Actions, Effect } from '@ngrx/effects';
import { Observable } from 'rxjs/Observable';
import { map, startWith, tap, withLatestFrom } from 'rxjs/operators';
import { NotifierService } from 'angular-notifier';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/catch';
import { AppStartInfo, LogService, Notification, NotificationType } from 'uhk-common';
import {
ActionTypes,
@@ -32,13 +27,15 @@ export class ApplicationEffects {
@Effect()
appStart$: Observable<Action> = this.actions$
.ofType(ActionTypes.APP_BOOTSRAPPED)
.startWith(new AppStartedAction())
.do(() => {
this.logService.info('Renderer appStart effect start');
this.appUpdateRendererService.sendAppStarted();
this.appRendererService.getAppStartInfo();
this.logService.info('Renderer appStart effect end');
});
.pipe(
startWith(new AppStartedAction()),
tap(() => {
this.logService.info('Renderer appStart effect start');
this.appUpdateRendererService.sendAppStarted();
this.appRendererService.getAppStartInfo();
this.logService.info('Renderer appStart effect end');
})
);
@Effect({dispatch: false})
appStartInfo$: Observable<Action> = this.actions$
@@ -50,13 +47,15 @@ export class ApplicationEffects {
@Effect({dispatch: false})
showNotification$: Observable<Action> = this.actions$
.ofType<ShowNotificationAction>(ActionTypes.APP_SHOW_NOTIFICATION)
.map(action => action.payload)
.do((notification: Notification) => {
if (notification.type === NotificationType.Undoable) {
return;
}
this.notifierService.notify(notification.type, notification.message);
});
.pipe(
map(action => action.payload),
tap((notification: Notification) => {
if (notification.type === NotificationType.Undoable) {
return;
}
this.notifierService.notify(notification.type, notification.message);
})
);
@Effect()
processStartInfo$: Observable<Action> = this.actions$
@@ -77,16 +76,18 @@ export class ApplicationEffects {
@Effect({dispatch: false}) openUrlInNewWindow$ = this.actions$
.ofType<OpenUrlInNewWindowAction>(ActionTypes.OPEN_URL_IN_NEW_WINDOW)
.withLatestFrom(this.store.select(runningInElectron))
.do(([action, inElectron]) => {
const url = action.payload;
.pipe(
withLatestFrom(this.store.select(runningInElectron)),
tap(([action, inElectron]) => {
const url = action.payload;
if (inElectron) {
this.appRendererService.openUrl(url);
} else {
window.open(url, '_blank');
}
});
if (inElectron) {
this.appRendererService.openUrl(url);
} else {
window.open(url, '_blank');
}
})
);
constructor(private actions$: Actions,
private notifierService: NotifierService,

View File

@@ -2,11 +2,7 @@ import { Injectable } from '@angular/core';
import { Actions, Effect, toPayload } from '@ngrx/effects';
import { Observable } from 'rxjs/Observable';
import { Action, Store } from '@ngrx/store';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/withLatestFrom';
import 'rxjs/add/operator/map';
import { map, startWith, switchMap, withLatestFrom } from 'rxjs/operators';
import { NotificationType } from 'uhk-common';
@@ -27,32 +23,38 @@ import { ShowNotificationAction } from '../actions/app';
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));
});
.pipe(
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();
});
.pipe(
withLatestFrom(this.store.select(getAutoUpdateSettings)),
map(([action, config]) => {
this.dataStorageRepository.saveAutoUpdateSettings(config);
return new SaveAutoUpdateSettingsSuccessAction();
})
);
@Effect() sendNotification$: Observable<Action> = this.actions$
.ofType(ActionTypes.CHECK_FOR_UPDATE_FAILED, ActionTypes.CHECK_FOR_UPDATE_SUCCESS)
.map(toPayload)
.map((message: string) => {
return new ShowNotificationAction({
type: NotificationType.Info,
message
});
});
.pipe(
map(toPayload),
map((message: string) => {
return new ShowNotificationAction({
type: NotificationType.Info,
message
});
})
);
constructor(private actions$: Actions,
private dataStorageRepository: DataStorageRepositoryService,

View File

@@ -3,15 +3,10 @@ import { Router } from '@angular/router';
import { Action, Store } from '@ngrx/store';
import { Actions, Effect } from '@ngrx/effects';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/empty';
import 'rxjs/add/observable/timer';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/withLatestFrom';
import 'rxjs/add/operator/switchMap';
import { of } from 'rxjs/observable/of';
import { empty } from 'rxjs/observable/empty';
import { timer } from 'rxjs/observable/timer';
import { map, mergeMap, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import {
FirmwareUpgradeIpcResponse,
@@ -58,195 +53,228 @@ export class DeviceEffects {
@Effect()
deviceConnectionStateChange$: Observable<Action> = this.actions$
.ofType<ConnectionStateChangedAction>(ActionTypes.CONNECTION_STATE_CHANGED)
.withLatestFrom(this.store.select(getRouterState), this.store.select(deviceConnected))
.do(([action, route]) => {
const state = action.payload;
.pipe(
withLatestFrom(this.store.select(getRouterState), this.store.select(deviceConnected)),
tap(([action, route]) => {
const state = action.payload;
if (route.state && route.state.url.startsWith('/device/firmware')) {
return;
}
if (route.state && route.state.url.startsWith('/device/firmware')) {
return;
}
if (!state.hasPermission) {
return this.router.navigate(['/privilege']);
}
if (!state.hasPermission) {
return this.router.navigate(['/privilege']);
}
if (state.bootloaderActive) {
return this.router.navigate(['/recovery-device']);
}
if (state.bootloaderActive) {
return this.router.navigate(['/recovery-device']);
}
if (!state.zeroInterfaceAvailable) {
return this.router.navigate(['/privilege']);
}
if (!state.zeroInterfaceAvailable) {
return this.router.navigate(['/privilege']);
}
if (state.connected && state.zeroInterfaceAvailable) {
return this.router.navigate(['/']);
}
if (state.connected && state.zeroInterfaceAvailable) {
return this.router.navigate(['/']);
}
return this.router.navigate(['/detection']);
})
.switchMap(([action, route, connected]) => {
const payload = action.payload;
return this.router.navigate(['/detection']);
}),
switchMap(([action, route, connected]) => {
const payload = action.payload;
if (connected && payload.hasPermission && payload.zeroInterfaceAvailable) {
return Observable.of(new LoadConfigFromDeviceAction());
}
if (connected && payload.hasPermission && payload.zeroInterfaceAvailable) {
return Observable.of(new LoadConfigFromDeviceAction());
}
return Observable.empty();
});
return empty();
})
);
@Effect({dispatch: false})
@Effect({ dispatch: false })
setPrivilegeOnLinux$: Observable<Action> = this.actions$
.ofType(ActionTypes.SET_PRIVILEGE_ON_LINUX)
.do(() => {
this.deviceRendererService.setPrivilegeOnLinux();
});
.pipe(
tap(() => {
this.deviceRendererService.setPrivilegeOnLinux();
})
);
@Effect()
setPrivilegeOnLinuxReply$: Observable<Action> = this.actions$
.ofType<SetPrivilegeOnLinuxReplyAction>(ActionTypes.SET_PRIVILEGE_ON_LINUX_REPLY)
.map(action => action.payload)
.switchMap((response: any): any => {
if (response.success) {
this.appRendererService.getAppStartInfo();
return Observable.empty();
}
.pipe(
map(action => action.payload),
switchMap((response: any): any => {
if (response.success) {
this.appRendererService.getAppStartInfo();
return empty();
}
return Observable.of(new SetupPermissionErrorAction(response.error));
});
return of(new SetupPermissionErrorAction(response.error));
})
);
@Effect({dispatch: false})
@Effect({ dispatch: false })
saveConfiguration$: Observable<Action> = this.actions$
.ofType(ActionTypes.SAVE_CONFIGURATION)
.withLatestFrom(this.store)
.do(([action, state]) => {
setTimeout(() => this.sendUserConfigToKeyboard(state.userConfiguration, state.app.hardwareConfig), 100);
})
.switchMap(() => Observable.empty());
.pipe(
withLatestFrom(this.store),
tap(([action, state]) => {
setTimeout(() => this.sendUserConfigToKeyboard(state.userConfiguration, state.app.hardwareConfig), 100);
}),
switchMap(() => empty())
);
@Effect()
saveConfigurationReply$: Observable<Action> = this.actions$
.ofType<SaveConfigurationReplyAction>(ActionTypes.SAVE_CONFIGURATION_REPLY)
.map(action => action.payload)
.mergeMap((response: IpcResponse) => {
if (response.success) {
return [
new SaveToKeyboardSuccessAction()
];
}
.pipe(
map(action => action.payload),
mergeMap((response: IpcResponse) => {
if (response.success) {
return [
new SaveToKeyboardSuccessAction()
];
}
return [
new ShowNotificationAction({
type: NotificationType.Error,
message: response.error.message
}),
new SaveToKeyboardSuccessFailed()
];
});
return [
new ShowNotificationAction({
type: NotificationType.Error,
message: response.error.message
}),
new SaveToKeyboardSuccessFailed()
];
})
);
@Effect()
autoHideSaveToKeyboardButton$: Observable<Action> = this.actions$
.ofType(ActionTypes.SAVE_TO_KEYBOARD_SUCCESS)
.withLatestFrom(this.store)
.switchMap(([action, state]) => Observable.timer(1000)
.mergeMap(() => {
const actions = [new HideSaveToKeyboardButton()];
.pipe(
withLatestFrom(this.store),
switchMap(([action, state]) => timer(1000)
.mergeMap(() => {
const actions = [new HideSaveToKeyboardButton()];
if (state.device.hasBackupUserConfiguration) {
actions.push(new RestoreUserConfigurationFromBackupSuccessAction());
this.router.navigate(['/']);
}
if (state.device.hasBackupUserConfiguration) {
actions.push(new RestoreUserConfigurationFromBackupSuccessAction());
this.router.navigate(['/']);
}
return actions;
})
return actions;
})
)
);
@Effect()
resetMouseSpeedSettings$: Observable<Action> = this.actions$
.ofType(ActionTypes.RESET_MOUSE_SPEED_SETTINGS)
.switchMap(() => {
const config = this.defaultUserConfigurationService.getDefault();
const mouseSpeedDefaultSettings = {};
const mouseSpeedProps = [
'mouseMoveInitialSpeed',
'mouseMoveAcceleration',
'mouseMoveDeceleratedSpeed',
'mouseMoveBaseSpeed',
'mouseMoveAcceleratedSpeed',
'mouseScrollInitialSpeed',
'mouseScrollAcceleration',
'mouseScrollDeceleratedSpeed',
'mouseScrollBaseSpeed',
'mouseScrollAcceleratedSpeed'
];
mouseSpeedProps.forEach(prop => {
mouseSpeedDefaultSettings[prop] = config[prop];
});
return Observable.of(new LoadResetUserConfigurationAction(<UserConfiguration>mouseSpeedDefaultSettings));
});
.pipe(
switchMap(() => {
const config = this.defaultUserConfigurationService.getDefault();
const mouseSpeedDefaultSettings = {};
const mouseSpeedProps = [
'mouseMoveInitialSpeed',
'mouseMoveAcceleration',
'mouseMoveDeceleratedSpeed',
'mouseMoveBaseSpeed',
'mouseMoveAcceleratedSpeed',
'mouseScrollInitialSpeed',
'mouseScrollAcceleration',
'mouseScrollDeceleratedSpeed',
'mouseScrollBaseSpeed',
'mouseScrollAcceleratedSpeed'
];
mouseSpeedProps.forEach(prop => {
mouseSpeedDefaultSettings[prop] = config[prop];
});
return of(new LoadResetUserConfigurationAction(<UserConfiguration>mouseSpeedDefaultSettings));
})
);
@Effect() resetUserConfiguration$: Observable<Action> = this.actions$
.ofType(ActionTypes.RESET_USER_CONFIGURATION)
.switchMap(() => {
const config = this.defaultUserConfigurationService.getDefault();
return Observable.of(new LoadResetUserConfigurationAction(config));
});
.pipe(
switchMap(() => {
const config = this.defaultUserConfigurationService.getDefault();
return of(new LoadResetUserConfigurationAction(config));
})
);
@Effect() saveResetUserConfigurationToDevice$ = this.actions$
.ofType<ApplyUserConfigurationFromFileAction
| LoadResetUserConfigurationAction>(
UserConfigActions.LOAD_RESET_USER_CONFIGURATION,
UserConfigActions.APPLY_USER_CONFIGURATION_FROM_FILE)
.map(action => action.payload)
.switchMap((config: UserConfiguration) => {
this.dataStorageRepository.saveConfig(config);
.pipe(
map(action => action.payload),
switchMap((config: UserConfiguration) => {
this.dataStorageRepository.saveConfig(config);
return Observable.of(new SaveConfigurationAction());
});
return of(new SaveConfigurationAction());
})
);
@Effect({dispatch: false}) updateFirmware$ = this.actions$
@Effect({ dispatch: false }) updateFirmware$ = this.actions$
.ofType<UpdateFirmwareAction>(ActionTypes.UPDATE_FIRMWARE)
.do(() => this.deviceRendererService.updateFirmware({
versionInformation: getVersions()
}));
.pipe(
tap(() => this.deviceRendererService.updateFirmware({
versionInformation: getVersions()
}))
);
@Effect({dispatch: false}) updateFirmwareWith$ = this.actions$
@Effect({ dispatch: false }) updateFirmwareWith$ = this.actions$
.ofType<UpdateFirmwareWithAction>(ActionTypes.UPDATE_FIRMWARE_WITH)
.map(action => action.payload)
.do(data => this.deviceRendererService.updateFirmware({
versionInformation: getVersions(),
firmware: data
}));
.pipe(
map(action => action.payload),
tap(data => this.deviceRendererService.updateFirmware({
versionInformation: getVersions(),
firmware: data
}))
);
@Effect() updateFirmwareReply$ = this.actions$
.ofType<UpdateFirmwareReplyAction>(ActionTypes.UPDATE_FIRMWARE_REPLY)
.map(action => action.payload)
.switchMap((response: FirmwareUpgradeIpcResponse)
: Observable<UpdateFirmwareSuccessAction | UpdateFirmwareFailedAction> => {
if (response.success) {
return Observable.of(new UpdateFirmwareSuccessAction(response.modules));
}
.pipe(
map(action => action.payload),
switchMap((response: FirmwareUpgradeIpcResponse)
: Observable<UpdateFirmwareSuccessAction | UpdateFirmwareFailedAction> => {
return Observable.of(new UpdateFirmwareFailedAction({
error: response.error,
modules: response.modules
}));
});
if (response.success) {
return Observable.of(new UpdateFirmwareSuccessAction(response.modules));
}
return of(new UpdateFirmwareFailedAction({
error: response.error,
modules: response.modules
}));
})
);
@Effect() restoreUserConfiguration$ = this.actions$
.ofType<ResetUserConfigurationAction>(ActionTypes.RESTORE_CONFIGURATION_FROM_BACKUP)
.map(() => new SaveConfigurationAction());
.pipe(
map(() => new SaveConfigurationAction())
);
@Effect({dispatch: false}) recoveryDevice$ = this.actions$
@Effect({ dispatch: false }) recoveryDevice$ = this.actions$
.ofType<RecoveryDeviceAction>(ActionTypes.RECOVERY_DEVICE)
.do(() => this.deviceRendererService.recoveryDevice());
.pipe(
tap(() => this.deviceRendererService.recoveryDevice())
);
@Effect({dispatch: false}) enableUsbStackTest$ = this.actions$
@Effect({ dispatch: false }) enableUsbStackTest$ = this.actions$
.ofType<EnableUsbStackTestAction>(ActionTypes.ENABLE_USB_STACK_TEST)
.do(() => this.deviceRendererService.enableUsbStackTest());
.pipe(
tap(() => this.deviceRendererService.enableUsbStackTest())
);
@Effect({dispatch: false}) startConnectionPoller$ = this.actions$
@Effect({ dispatch: false }) startConnectionPoller$ = this.actions$
.ofType(ActionTypes.START_CONNECTION_POLLER)
.do(() => this.deviceRendererService.startConnectionPoller());
.pipe(
tap(() => this.deviceRendererService.startConnectionPoller())
);
constructor(private actions$: Actions,
private router: Router,

View File

@@ -4,14 +4,8 @@ import { Router } from '@angular/router';
import { Actions, Effect } from '@ngrx/effects';
import { Action, Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/pairwise';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/withLatestFrom';
import 'rxjs/add/observable/of';
import { of } from 'rxjs/observable/of';
import { map, pairwise, startWith, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import { Keymap } from 'uhk-common';
import { findNewItem } from '../../util';
@@ -24,47 +18,60 @@ export class KeymapEffects {
@Effect() loadKeymaps$: Observable<Action> = this.actions$
.ofType(KeymapActions.LOAD_KEYMAPS)
.startWith(KeymapActions.loadKeymaps())
.switchMap(() => {
const presetsRequireContext = (<any>require).context('../../../res/presets', false, /.json$/);
const uhkPresets = presetsRequireContext.keys().map(presetsRequireContext) // load the presets into an array
.map((keymap: any) => new Keymap().fromJsonObject(keymap));
.pipe(
startWith(KeymapActions.loadKeymaps()),
switchMap(() => {
const presetsRequireContext = (<any>require).context('../../../res/presets', false, /.json$/);
const uhkPresets = presetsRequireContext.keys().map(presetsRequireContext) // load the presets into an array
.map((keymap: any) => new Keymap().fromJsonObject(keymap));
return Observable.of(KeymapActions.loadKeymapsSuccess(uhkPresets));
});
return of(KeymapActions.loadKeymapsSuccess(uhkPresets));
})
);
@Effect({ dispatch: false }) addOrDuplicate$: any = this.actions$
.ofType(KeymapActions.ADD, KeymapActions.DUPLICATE)
.withLatestFrom(this.store.let(getKeymaps()).pairwise(), (action, latest) => latest)
.do(([prevKeymaps, newKeymaps]) => {
const newKeymap = findNewItem(prevKeymaps, newKeymaps);
this.router.navigate(['/keymap', newKeymap.abbreviation]);
});
.pipe(
withLatestFrom(this.store.let(getKeymaps())
.pipe(
pairwise()
)
),
map(([action, latest]) => latest),
tap(([prevKeymaps, newKeymaps]) => {
const newKeymap = findNewItem(prevKeymaps, newKeymaps);
this.router.navigate(['/keymap', newKeymap.abbreviation]);
})
);
@Effect({ dispatch: false }) remove$: any = this.actions$
.ofType(KeymapActions.REMOVE)
.withLatestFrom(this.store)
.map(latest => latest[1].userConfiguration.keymaps)
.do(keymaps => {
if (keymaps.length === 0) {
this.router.navigate(['/keymap/add']);
} else {
const favourite: Keymap = keymaps.find(keymap => keymap.isDefault);
this.router.navigate(['/keymap', favourite.abbreviation]);
}
});
.pipe(
withLatestFrom(this.store),
map(latest => latest[1].userConfiguration.keymaps),
tap(keymaps => {
if (keymaps.length === 0) {
this.router.navigate(['/keymap/add']);
} else {
const favourite: Keymap = keymaps.find(keymap => keymap.isDefault);
this.router.navigate(['/keymap', favourite.abbreviation]);
}
})
);
@Effect({ dispatch: false }) editAbbr$: any = this.actions$
.ofType(KeymapActions.EDIT_ABBR)
.withLatestFrom(this.store)
.do(([action, store]: [KeymapActions.EditKeymapAbbreviationAction, AppState]) => {
for (const keymap of store.userConfiguration.keymaps) {
if (keymap.name === action.payload.name && keymap.abbreviation === action.payload.newAbbr) {
this.router.navigate(['/keymap', action.payload.newAbbr]);
return;
.pipe(
withLatestFrom(this.store),
tap(([action, store]: [KeymapActions.EditKeymapAbbreviationAction, AppState]) => {
for (const keymap of store.userConfiguration.keymaps) {
if (keymap.name === action.payload.name && keymap.abbreviation === action.payload.newAbbr) {
this.router.navigate(['/keymap', action.payload.newAbbr]);
return;
}
}
}
});
})
);
constructor(private actions$: Actions, private router: Router, private store: Store<AppState>) { }
}

View File

@@ -3,14 +3,10 @@ import { Router } from '@angular/router';
import { Actions, Effect } from '@ngrx/effects';
import { Store, Action } from '@ngrx/store';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/pairwise';
import 'rxjs/add/operator/withLatestFrom';
import { map, pairwise, tap, withLatestFrom } from 'rxjs/operators';
import { Macro } from 'uhk-common';
import { KeymapActions, MacroActions } from '../actions';
import { KeymapActions, MacroAction, MacroActions } from '../actions';
import { AppState } from '../index';
import { getMacros } from '../reducers/user-configuration';
import { findNewItem } from '../../util';
@@ -19,29 +15,42 @@ import { findNewItem } from '../../util';
export class MacroEffects {
@Effect({ dispatch: false }) remove$: any = this.actions$
.ofType(MacroActions.REMOVE)
.do<any>(action => this.store.dispatch(KeymapActions.checkMacro(action.payload)))
.withLatestFrom(this.store)
.map(([action, state]) => state.userConfiguration.macros)
.do(macros => {
if (macros.length === 0) {
this.router.navigate(['/macro']);
} else {
this.router.navigate(['/macro', macros[0].id]);
}
});
.ofType<MacroAction>(MacroActions.REMOVE)
.pipe(
tap(action => this.store.dispatch(KeymapActions.checkMacro(action.payload))),
withLatestFrom(this.store),
map(([action, state]) => state.userConfiguration.macros),
tap(macros => {
if (macros.length === 0) {
return this.router.navigate(['/macro']);
}
return this.router.navigate(['/macro', macros[0].id]);
}
)
);
@Effect({ dispatch: false }) addOrDuplicate$: any = this.actions$
.ofType(MacroActions.ADD, MacroActions.DUPLICATE)
.withLatestFrom(this.store.let(getMacros()).pairwise(), (action, latest) => ([action, latest[0], latest[1]]))
.do(([action, prevMacros, newMacros]: [Action, Macro[], Macro[]]) => {
const newMacro = findNewItem(prevMacros, newMacros);
const commands = ['/macro', newMacro.id];
if (action.type === MacroActions.ADD) {
commands.push('new');
}
this.router.navigate(commands);
});
.ofType<MacroAction>(MacroActions.ADD, MacroActions.DUPLICATE)
.pipe(
withLatestFrom(this.store.let(getMacros())
.pipe(
pairwise()
)
),
map(([action, latest]) => ([action, latest[0], latest[1]])),
tap(([action, prevMacros, newMacros]: [Action, Macro[], Macro[]]) => {
const newMacro = findNewItem(prevMacros, newMacros);
const commands = ['/macro', newMacro.id];
if (action.type === MacroActions.ADD) {
commands.push('new');
}
this.router.navigate(commands);
})
);
constructor(private actions$: Actions, private router: Router, private store: Store<AppState>) { }
constructor(private actions$: Actions,
private router: Router,
private store: Store<AppState>) {
}
}

View File

@@ -3,17 +3,11 @@ import { Router } from '@angular/router';
import { Actions, Effect } from '@ngrx/effects';
import { Observable } from 'rxjs/Observable';
import { defer } from 'rxjs/observable/defer';
import { of } from 'rxjs/observable/of';
import { map, mergeMap, tap, withLatestFrom } from 'rxjs/operators';
import { Action, Store } from '@ngrx/store';
import { saveAs } from 'file-saver';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/withLatestFrom';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/empty';
import {
getHardwareConfigFromDeviceResponse,
getUserConfigFromDeviceResponse,
@@ -57,7 +51,7 @@ import { UploadFileData } from '../../models/upload-file-data';
export class UserConfigEffects {
@Effect() loadUserConfig$: Observable<Action> = defer(() => {
return Observable.of(new LoadUserConfigSuccessAction(this.getUserConfiguration()));
return of(new LoadUserConfigSuccessAction(this.getUserConfiguration()));
});
@Effect() saveUserConfig$: Observable<Action> = (this.actions$
@@ -66,160 +60,175 @@ export class UserConfigEffects {
KeymapActions.SET_DEFAULT, KeymapActions.REMOVE, KeymapActions.SAVE_KEY, KeymapActions.EDIT_DESCRIPTION,
MacroActions.ADD, MacroActions.DUPLICATE, MacroActions.EDIT_NAME, MacroActions.REMOVE, MacroActions.ADD_ACTION,
MacroActions.SAVE_ACTION, MacroActions.DELETE_ACTION, MacroActions.REORDER_ACTION,
ActionTypes.RENAME_USER_CONFIGURATION, ActionTypes.SET_USER_CONFIGURATION_VALUE) as
Observable<KeymapAction | MacroAction | RenameUserConfigurationAction>)
.withLatestFrom(this.store.select(getUserConfiguration), this.store.select(getPrevUserConfiguration))
.mergeMap(([action, config, prevUserConfiguration]) => {
config.recalculateConfigurationLength();
this.dataStorageRepository.saveConfig(config);
ActionTypes.RENAME_USER_CONFIGURATION, ActionTypes.SET_USER_CONFIGURATION_VALUE
) as Observable<KeymapAction | MacroAction | RenameUserConfigurationAction>)
.pipe(
withLatestFrom(this.store.select(getUserConfiguration), this.store.select(getPrevUserConfiguration)),
mergeMap(([action, config, prevUserConfiguration]) => {
config.recalculateConfigurationLength();
this.dataStorageRepository.saveConfig(config);
if (action.type === KeymapActions.REMOVE || action.type === MacroActions.REMOVE) {
const text = action.type === KeymapActions.REMOVE ? 'Keymap' : 'Macro';
const pathPrefix = action.type === KeymapActions.REMOVE ? 'keymap' : 'macro';
const payload: UndoUserConfigData = {
path: `/${pathPrefix}/${action.payload}`,
config: prevUserConfiguration.toJsonObject()
};
if (action.type === KeymapActions.REMOVE || action.type === MacroActions.REMOVE) {
const text = action.type === KeymapActions.REMOVE ? 'Keymap' : 'Macro';
const pathPrefix = action.type === KeymapActions.REMOVE ? 'keymap' : 'macro';
const payload: UndoUserConfigData = {
path: `/${pathPrefix}/${action.payload}`,
config: prevUserConfiguration.toJsonObject()
};
return [
new SaveUserConfigSuccessAction(config),
new ShowNotificationAction({
type: NotificationType.Undoable,
message: `${text} has been deleted`,
extra: {
payload,
type: KeymapActions.UNDO_LAST_ACTION
}
}),
new ShowSaveToKeyboardButtonAction()
];
}
return [
new SaveUserConfigSuccessAction(config),
new ShowNotificationAction({
type: NotificationType.Undoable,
message: `${text} has been deleted`,
extra: {
payload,
type: KeymapActions.UNDO_LAST_ACTION
}
}),
new DismissUndoNotificationAction(),
new ShowSaveToKeyboardButtonAction()
];
}
return [
new SaveUserConfigSuccessAction(config),
new DismissUndoNotificationAction(),
new ShowSaveToKeyboardButtonAction()
];
});
})
);
@Effect() undoUserConfig$: Observable<Action> = this.actions$
.ofType<UndoLastAction>(KeymapActions.UNDO_LAST_ACTION)
.map(action => action.payload)
.mergeMap((payload: UndoUserConfigData) => {
const config = new UserConfiguration().fromJsonObject(payload.config);
this.dataStorageRepository.saveConfig(config);
this.router.navigate([payload.path]);
return [new LoadUserConfigSuccessAction(config)];
});
.pipe(
map(action => action.payload),
mergeMap((payload: UndoUserConfigData) => {
const config = new UserConfiguration().fromJsonObject(payload.config);
this.dataStorageRepository.saveConfig(config);
this.router.navigate([payload.path]);
@Effect({dispatch: false}) loadConfigFromDevice$ = this.actions$
return [new LoadUserConfigSuccessAction(config)];
})
);
@Effect({ dispatch: false }) loadConfigFromDevice$ = this.actions$
.ofType(ActionTypes.LOAD_CONFIG_FROM_DEVICE)
.do(() => this.deviceRendererService.loadConfigurationFromKeyboard());
.pipe(
tap(() => this.deviceRendererService.loadConfigurationFromKeyboard())
);
@Effect() loadConfigFromDeviceReply$ = this.actions$
.ofType<LoadConfigFromDeviceReplyAction>(ActionTypes.LOAD_CONFIG_FROM_DEVICE_REPLY)
.withLatestFrom(this.store.select(getRouterState))
.mergeMap(([action, route]): any => {
const data: ConfigurationReply = action.payload;
.pipe(
withLatestFrom(this.store.select(getRouterState)),
mergeMap(([action, route]): any => {
const data: ConfigurationReply = action.payload;
if (!data.success) {
return [new ShowNotificationAction({
type: NotificationType.Error,
message: data.error
})];
}
const result = [];
let newPageDestination: Array<string>;
try {
const userConfig = getUserConfigFromDeviceResponse(data.userConfiguration);
result.push(new LoadUserConfigSuccessAction(userConfig));
if (route.state && !route.state.url.startsWith('/device/firmware')) {
newPageDestination = ['/'];
if (!data.success) {
return [new ShowNotificationAction({
type: NotificationType.Error,
message: data.error
})];
}
} catch (err) {
this.logService.error('Eeprom user-config parse error:', err);
const userConfig = new UserConfiguration().fromJsonObject(data.backupConfiguration);
const result = [];
let newPageDestination: Array<string>;
result.push(new HasBackupUserConfigurationAction(!!data.backupConfiguration));
result.push(new LoadUserConfigSuccessAction(userConfig));
try {
const userConfig = getUserConfigFromDeviceResponse(data.userConfiguration);
result.push(new LoadUserConfigSuccessAction(userConfig));
newPageDestination = ['/device/restore-user-configuration'];
}
if (route.state && !route.state.url.startsWith('/device/firmware')) {
newPageDestination = ['/'];
}
try {
const hardwareConfig = getHardwareConfigFromDeviceResponse(data.hardwareConfiguration);
result.push(new LoadHardwareConfigurationSuccessAction(hardwareConfig));
} catch (err) {
this.logService.error('Eeprom hardware-config parse error:', err);
result.push(
new ShowNotificationAction({
type: NotificationType.Error,
message: err
}));
}
} catch (err) {
this.logService.error('Eeprom user-config parse error:', err);
const userConfig = new UserConfiguration().fromJsonObject(data.backupConfiguration);
result.push(new HardwareModulesLoadedAction(data.modules));
result.push(new HasBackupUserConfigurationAction(!!data.backupConfiguration));
result.push(new LoadUserConfigSuccessAction(userConfig));
if (newPageDestination) {
this.router.navigate(newPageDestination);
}
newPageDestination = ['/device/restore-user-configuration'];
}
return result;
});
try {
const hardwareConfig = getHardwareConfigFromDeviceResponse(data.hardwareConfiguration);
result.push(new LoadHardwareConfigurationSuccessAction(hardwareConfig));
} catch (err) {
this.logService.error('Eeprom hardware-config parse error:', err);
result.push(
new ShowNotificationAction({
type: NotificationType.Error,
message: err
}));
}
@Effect({dispatch: false}) saveUserConfigInJsonFile$ = this.actions$
result.push(new HardwareModulesLoadedAction(data.modules));
if (newPageDestination) {
this.router.navigate(newPageDestination);
}
return result;
})
);
@Effect({ dispatch: false }) saveUserConfigInJsonFile$ = this.actions$
.ofType(ActionTypes.SAVE_USER_CONFIG_IN_JSON_FILE)
.withLatestFrom(this.store.select(getUserConfiguration))
.do(([action, userConfiguration]) => {
const asString = JSON.stringify(userConfiguration.toJsonObject(), null, 2);
const asBlob = new Blob([asString], {type: 'text/plain'});
saveAs(asBlob, 'UserConfiguration.json');
});
.pipe(
withLatestFrom(this.store.select(getUserConfiguration)),
tap(([action, userConfiguration]) => {
const asString = JSON.stringify(userConfiguration.toJsonObject(), null, 2);
const asBlob = new Blob([asString], { type: 'text/plain' });
saveAs(asBlob, 'UserConfiguration.json');
})
);
@Effect({dispatch: false}) saveUserConfigInBinFile$ = this.actions$
@Effect({ dispatch: false }) saveUserConfigInBinFile$ = this.actions$
.ofType(ActionTypes.SAVE_USER_CONFIG_IN_BIN_FILE)
.withLatestFrom(this.store.select(getUserConfiguration))
.do(([action, userConfiguration]) => {
const uhkBuffer = new UhkBuffer();
userConfiguration.toBinary(uhkBuffer);
const blob = new Blob([uhkBuffer.getBufferContent()]);
saveAs(blob, 'UserConfiguration.bin');
});
.pipe(
withLatestFrom(this.store.select(getUserConfiguration)),
tap(([action, userConfiguration]) => {
const uhkBuffer = new UhkBuffer();
userConfiguration.toBinary(uhkBuffer);
const blob = new Blob([uhkBuffer.getBufferContent()]);
saveAs(blob, 'UserConfiguration.bin');
})
);
@Effect() loadUserConfigurationFromFile$ = this.actions$
.ofType<LoadUserConfigurationFromFileAction>(ActionTypes.LOAD_USER_CONFIGURATION_FROM_FILE)
.map(action => action.payload)
.map((info: UploadFileData) => {
try {
const userConfig = new UserConfiguration();
.pipe(
map(action => action.payload),
map((info: UploadFileData) => {
try {
const userConfig = new UserConfiguration();
if (info.filename.endsWith('.bin')) {
userConfig.fromBinary(UhkBuffer.fromArray(info.data));
} else {
const buffer = new Buffer(info.data);
const json = buffer.toString();
userConfig.fromJsonObject(JSON.parse(json));
if (info.filename.endsWith('.bin')) {
userConfig.fromBinary(UhkBuffer.fromArray(info.data));
} else {
const buffer = new Buffer(info.data);
const json = buffer.toString();
userConfig.fromJsonObject(JSON.parse(json));
}
if (userConfig.userConfigMajorVersion) {
return new ApplyUserConfigurationFromFileAction(userConfig);
}
return new ShowNotificationAction({
type: NotificationType.Error,
message: 'Invalid configuration specified.'
});
} catch (err) {
return new ShowNotificationAction({
type: NotificationType.Error,
message: 'Invalid configuration specified.'
});
}
if (userConfig.userConfigMajorVersion) {
return new ApplyUserConfigurationFromFileAction(userConfig);
}
return new ShowNotificationAction({
type: NotificationType.Error,
message: 'Invalid configuration specified.'
});
} catch (err) {
return new ShowNotificationAction({
type: NotificationType.Error,
message: 'Invalid configuration specified.'
});
}
});
})
);
constructor(private actions$: Actions,
private dataStorageRepository: DataStorageRepositoryService,

View File

@@ -1,8 +1,8 @@
import { Action } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
import { of } from 'rxjs/observable/of';
import { map } from 'rxjs/operators';
import {
KeyAction,
@@ -360,29 +360,37 @@ export function getKeymap(abbr: string) {
}
return (state$: Observable<AppState>) => getKeymaps()(state$)
.map((keymaps: Keymap[]) =>
keymaps.find((keymap: Keymap) => keymap.abbreviation === abbr)
.pipe(
map((keymaps: Keymap[]) =>
keymaps.find((keymap: Keymap) => keymap.abbreviation === abbr)
)
);
}
export function getDefaultKeymap() {
return (state$: Observable<AppState>) => getKeymaps()(state$)
.map((keymaps: Keymap[]) =>
keymaps.find((keymap: Keymap) => keymap.isDefault)
.pipe(
map((keymaps: Keymap[]) =>
keymaps.find((keymap: Keymap) => keymap.isDefault)
)
);
}
export function getMacros(): (state$: Observable<AppState>) => Observable<Macro[]> {
return (state$: Observable<AppState>) => state$
.map(state => state.userConfiguration.macros);
.pipe(
map(state => state.userConfiguration.macros)
);
}
export function getMacro(id: number) {
if (isNaN(id)) {
return () => Observable.of<Macro>(undefined);
return () => of<Macro>(undefined);
} else {
return (state$: Observable<AppState>) => getMacros()(state$)
.map((macros: Macro[]) => macros.find((macro: Macro) => macro.id === id));
.pipe(
map((macros: Macro[]) => macros.find((macro: Macro) => macro.id === id))
);
}
}