diff --git a/electron/src/app.module.ts b/electron/src/app.module.ts index 297507af..45ff56da 100644 --- a/electron/src/app.module.ts +++ b/electron/src/app.module.ts @@ -79,7 +79,10 @@ import { DataStorage } from './shared/store/storage'; import { KeymapEditGuard } from './shared/components/keymap/edit'; import { MacroNotFoundGuard } from './shared/components/macro/not-found'; -import { UHkConnectedGuard } from './services/uhk-connected.guard'; +import { UhkDeviceConnectedGuard } from './services/uhk-device-connected.guard'; +import { UhkDeviceDisconnectedGuard } from './services/uhk-device-disconnected.guard'; +import { UhkDeviceInitializedGuard } from './services/uhk-device-initialized.guard'; +import { UhkDeviceUninitializedGuard } from './services/uhk-device-uninitialized.guard'; // Create DataStorage dependency injection const storageProvider = ReflectiveInjector.resolve([DataStorage]); @@ -164,7 +167,10 @@ const storeConfig = { EffectsModule.runAfterBootstrap(MacroEffects) ], providers: [ - UHkConnectedGuard, + UhkDeviceConnectedGuard, + UhkDeviceDisconnectedGuard, + UhkDeviceInitializedGuard, + UhkDeviceUninitializedGuard, MapperService, appRoutingProviders, KeymapEditGuard, diff --git a/electron/src/app/app.routes.ts b/electron/src/app/app.routes.ts index 388df4ae..a1d18be1 100644 --- a/electron/src/app/app.routes.ts +++ b/electron/src/app/app.routes.ts @@ -1,4 +1,3 @@ -import { UHkConnectedGuard } from './../services/uhk-connected.guard'; import { ModuleWithProviders } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; @@ -7,19 +6,26 @@ import { PrivilegeCheckerComponent } from './../components/privilege-checker'; import { MainAppComponent } from './../main-app/main-app.component'; import { mainAppRoutes } from './../main-app/main-app.routes'; +import { UhkDeviceConnectedGuard } from './../services/uhk-device-connected.guard'; +import { UhkDeviceDisconnectedGuard } from './../services/uhk-device-disconnected.guard'; +import { UhkDeviceInitializedGuard } from './../services/uhk-device-initialized.guard'; +import { UhkDeviceUninitializedGuard } from './../services/uhk-device-uninitialized.guard'; + const appRoutes: Routes = [ { path: 'detection', - component: MissingDeviceComponent + component: MissingDeviceComponent, + canActivate: [UhkDeviceDisconnectedGuard] }, { path: 'privilege', - component: PrivilegeCheckerComponent + component: PrivilegeCheckerComponent, + canActivate: [UhkDeviceConnectedGuard, UhkDeviceUninitializedGuard] }, { path: '', component: MainAppComponent, - canActivate: [UHkConnectedGuard], + canActivate: [UhkDeviceInitializedGuard], children: mainAppRoutes } ]; diff --git a/electron/src/components/missing-device/missing-device.component.ts b/electron/src/components/missing-device/missing-device.component.ts index e86b79f7..0b153e21 100644 --- a/electron/src/components/missing-device/missing-device.component.ts +++ b/electron/src/components/missing-device/missing-device.component.ts @@ -21,7 +21,7 @@ export class MissingDeviceComponent { .ignoreElements() .subscribe({ complete: () => { - router.navigate(['/privilige']); + router.navigate(['/privilege']); } }); } diff --git a/electron/src/components/privilege-checker/privilege-checker.component.ts b/electron/src/components/privilege-checker/privilege-checker.component.ts index 17c327d0..399acbf7 100644 --- a/electron/src/components/privilege-checker/privilege-checker.component.ts +++ b/electron/src/components/privilege-checker/privilege-checker.component.ts @@ -3,6 +3,12 @@ import { Router } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/of'; +import 'rxjs/add/observable/throw'; +import 'rxjs/add/operator/distinctUntilChanged'; +import 'rxjs/add/operator/ignoreElements'; +import 'rxjs/add/operator/takeWhile'; + +import { UhkDeviceService } from './../../services/uhk-device.service'; @Component({ selector: 'privilege-checker', @@ -11,13 +17,17 @@ import 'rxjs/add/observable/of'; }) export class PrivilegeCheckerComponent { - constructor(router: Router) { - this.checkPermissions() - .subscribe(hasPermisson => { - if (hasPermisson) { - router.navigate(['/privilige']); + constructor(private router: Router, private uhkDevice: UhkDeviceService) { + uhkDevice.isConnected() + .distinctUntilChanged() + .takeWhile(connected => connected) + .ignoreElements() + .subscribe({ + complete: () => { + router.navigate(['/detection']); } }); + } checkPermissions(): Observable { diff --git a/electron/src/main-app/main-app.component.ts b/electron/src/main-app/main-app.component.ts index 8ab63457..8f6bf739 100644 --- a/electron/src/main-app/main-app.component.ts +++ b/electron/src/main-app/main-app.component.ts @@ -23,9 +23,9 @@ import { UhkDeviceService } from '../services/uhk-device.service'; export class MainAppComponent { constructor(private uhkDevice: UhkDeviceService, private store: Store, router: Router) { - uhkDevice.isConnected() + uhkDevice.isInitialized() .distinctUntilChanged() - .takeWhile(connected => connected) + .takeWhile(initialized => initialized) .ignoreElements() .subscribe({ complete: () => { diff --git a/electron/src/services/uhk-connected.guard.ts b/electron/src/services/uhk-device-connected.guard.ts similarity index 74% rename from electron/src/services/uhk-connected.guard.ts rename to electron/src/services/uhk-device-connected.guard.ts index 17b7887a..5e753227 100644 --- a/electron/src/services/uhk-connected.guard.ts +++ b/electron/src/services/uhk-device-connected.guard.ts @@ -3,21 +3,19 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/do'; -import 'rxjs/add/operator/first'; import { UhkDeviceService } from './uhk-device.service'; @Injectable() -export class UHkConnectedGuard implements CanActivate { +export class UhkDeviceConnectedGuard implements CanActivate { constructor(private uhkDevice: UhkDeviceService, private router: Router) { } canActivate(): Observable { return this.uhkDevice.isConnected() - .first() .do(connected => { if (!connected) { - return this.router.navigate(['/detection']); + this.router.navigate(['/detection']); } }); } diff --git a/electron/src/services/uhk-device-disconnected.guard.ts b/electron/src/services/uhk-device-disconnected.guard.ts new file mode 100644 index 00000000..c9474913 --- /dev/null +++ b/electron/src/services/uhk-device-disconnected.guard.ts @@ -0,0 +1,24 @@ +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 { UhkDeviceService } from './uhk-device.service'; + +@Injectable() +export class UhkDeviceDisconnectedGuard implements CanActivate { + + constructor(private uhkDevice: UhkDeviceService, private router: Router) { } + + canActivate(): Observable { + return this.uhkDevice.isConnected() + .do(connected => { + if (connected) { + this.router.navigate(['/privilege']); + } + }) + .map(connected => !connected); + } +} diff --git a/electron/src/services/uhk-device-initialized.guard.ts b/electron/src/services/uhk-device-initialized.guard.ts new file mode 100644 index 00000000..0145132e --- /dev/null +++ b/electron/src/services/uhk-device-initialized.guard.ts @@ -0,0 +1,22 @@ +import { CanActivate, Router } from '@angular/router'; +import { Injectable } from '@angular/core'; + +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/operator/do'; + +import { UhkDeviceService } from './uhk-device.service'; + +@Injectable() +export class UhkDeviceInitializedGuard implements CanActivate { + + constructor(private uhkDevice: UhkDeviceService, private router: Router) { } + + canActivate(): Observable { + return this.uhkDevice.isInitialized() + .do(initialized => { + if (!initialized) { + this.router.navigate(['/detection']); + } + }); + } +} diff --git a/electron/src/services/uhk-device-uninitialized.guard.ts b/electron/src/services/uhk-device-uninitialized.guard.ts new file mode 100644 index 00000000..50e7e5c1 --- /dev/null +++ b/electron/src/services/uhk-device-uninitialized.guard.ts @@ -0,0 +1,24 @@ +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 { UhkDeviceService } from './uhk-device.service'; + +@Injectable() +export class UhkDeviceUninitializedGuard implements CanActivate { + + constructor(private uhkDevice: UhkDeviceService, private router: Router) { } + + canActivate(): Observable { + return this.uhkDevice.isInitialized() + .do(initialized => { + if (initialized) { + this.router.navigate(['/']); + } + }) + .map(initialized => !initialized); + } +} diff --git a/electron/src/services/uhk-device.service.ts b/electron/src/services/uhk-device.service.ts index fb3422bd..1232b863 100644 --- a/electron/src/services/uhk-device.service.ts +++ b/electron/src/services/uhk-device.service.ts @@ -1,4 +1,5 @@ import { Injectable, OnDestroy, NgZone } from '@angular/core'; + import { Observable } from 'rxjs/Observable'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { Observer } from 'rxjs/Observer'; @@ -9,11 +10,14 @@ import { Subscription } from 'rxjs/Subscription'; import 'rxjs/add/observable/empty'; import 'rxjs/add/observable/from'; +import 'rxjs/add/observable/of'; import 'rxjs/add/operator/catch'; import 'rxjs/add/operator/concat'; +import 'rxjs/add/operator/combineLatest'; import 'rxjs/add/operator/concatMap'; import 'rxjs/add/operator/mergeMap'; import 'rxjs/add/operator/publish'; +import 'rxjs/add/operator/switchMap'; import { Device, Interface, InEndpoint, OutEndpoint, findByIds, on } from 'usb'; @@ -38,7 +42,9 @@ interface SenderMessage { export class UhkDeviceService implements OnDestroy { private device: Device; + private deviceOpened$: BehaviorSubject; private connected$: BehaviorSubject; + private initizalized$: BehaviorSubject; private messageIn$: Observable; private messageOut$: Subject; @@ -47,8 +53,12 @@ export class UhkDeviceService implements OnDestroy { constructor(zone: NgZone) { this.messageOut$ = new Subject(); + this.initizalized$ = new BehaviorSubject(false); this.connected$ = new BehaviorSubject(false); - this.connect(); + this.deviceOpened$ = new BehaviorSubject(false); + this.outSubscription = Subscription.EMPTY; + + this.initialize(); // The change detection doesn't work properly if the callbacks are called outside Angular Zone on('attach', (device: Device) => zone.run(() => this.onDeviceAttach(device))); @@ -57,18 +67,27 @@ export class UhkDeviceService implements OnDestroy { ngOnDestroy() { this.disconnect(); + this.initizalized$.unsubscribe(); this.connected$.unsubscribe(); + this.deviceOpened$.unsubscribe(); } - connect(): void { - if (this.connected$.getValue()) { + initialize(): void { + if (this.initizalized$.getValue()) { return; } this.device = findByIds(vendorId, productId); + this.connected$.next(!!this.device); if (!this.device) { return; } - this.device.open(); + try { + this.device.open(); + this.deviceOpened$.next(true); + } catch (error) { + console.log(error); + return; + } const usbInterface: Interface = this.device.interface(0); // https://github.com/tessel/node-usb/issues/147 @@ -114,22 +133,50 @@ export class UhkDeviceService implements OnDestroy { }).publish(); this.outSubscription = outSending.connect(); - this.connected$.next(true); + this.initizalized$.next(true); } disconnect() { - if (!this.connected$.getValue()) { - return; - } this.outSubscription.unsubscribe(); this.messageIn$ = undefined; + this.initizalized$.next(false); + this.deviceOpened$.next(false); this.connected$.next(false); } + isInitialized(): Observable { + return this.initizalized$.asObservable(); + } + isConnected(): Observable { return this.connected$.asObservable(); } + hasPermissions(): Observable { + return this.isConnected() + .combineLatest(this.deviceOpened$) + .map((latest: boolean[]) => { + const connected = latest[0]; + const opened = latest[1]; + if (!connected) { + return false; + } else if (opened) { + return true; + } + try { + this.device.open(); + } catch (error) { + return false; + } + this.device.close(); + return true; + }); + } + + isOpened(): Observable { + return this.deviceOpened$.asObservable(); + } + sendConfig(configBuffer: Buffer): Observable { return Observable.create((subscriber: Subscriber) => { console.log('Sending...', configBuffer); @@ -176,18 +223,14 @@ export class UhkDeviceService implements OnDestroy { if (device.deviceDescriptor.idVendor !== vendorId || device.deviceDescriptor.idProduct !== productId) { return; } - if (!this.connected$.getValue()) { - this.connect(); - } + this.initialize(); } onDeviceDetach(device: Device) { if (device.deviceDescriptor.idVendor !== vendorId || device.deviceDescriptor.idProduct !== productId) { return; } - if (this.connected$.getValue()) { - this.disconnect(); - } + this.disconnect(); } }