UHK detection

Closes #258
This commit is contained in:
Farkas József
2017-02-18 14:15:16 +01:00
committed by József Farkas
parent c2103c7d50
commit 715b924be0
18 changed files with 202 additions and 29 deletions

View File

@@ -10,6 +10,7 @@ import { StoreLogMonitorModule, useLogMonitor } from '@ngrx/store-log-monitor';
import { DragulaModule } from 'ng2-dragula/ng2-dragula';
import { Select2Module } from 'ng2-select2/ng2-select2';
import { MissingDeviceComponent } from './components/missing-device/missing-device.component';
import { AddOnComponent } from './shared/components/add-on';
import { KeyboardSliderComponent } from './shared/components/keyboard/slider';
import { KeymapAddComponent, KeymapHeaderComponent } from './shared/components/keymap';
@@ -59,7 +60,9 @@ import {
} from './shared/components/svg/keys';
import { SvgModuleComponent } from './shared/components/svg/module';
import { SvgKeyboardWrapComponent } from './shared/components/svg/wrap';
import { MainAppComponent, appRoutingProviders, routing } from './main-app';
import { appRoutingProviders, routing } from './app/app.routes';
import { AppComponent } from './app/app.component';
import { MainAppComponent } from './main-app';
import { CancelableDirective } from './shared/directives';
@@ -74,6 +77,8 @@ 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';
// Create DataStorage dependency injection
const storageProvider = ReflectiveInjector.resolve([DataStorage]);
const storageInjector = ReflectiveInjector.fromResolvedProviders(storageProvider);
@@ -87,6 +92,7 @@ const storeConfig = {
@NgModule({
declarations: [
AppComponent,
MainAppComponent,
KeymapEditComponent,
KeymapHeaderComponent,
@@ -133,6 +139,7 @@ const storeConfig = {
AddOnComponent,
SettingsComponent,
KeyboardSliderComponent,
MissingDeviceComponent,
CancelableDirective
],
imports: [
@@ -153,6 +160,7 @@ const storeConfig = {
EffectsModule.runAfterBootstrap(MacroEffects)
],
providers: [
UHkConnectedGuard,
MapperService,
appRoutingProviders,
KeymapEditGuard,
@@ -160,6 +168,6 @@ const storeConfig = {
CaptureService,
UhkDeviceService
],
bootstrap: [MainAppComponent]
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@@ -0,0 +1 @@
<router-outlet></router-outlet>

View File

@@ -0,0 +1,6 @@
app {
display: block;
min-height: 100vh;
height: 100vh;
width: 100%;
}

View File

@@ -0,0 +1,9 @@
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class AppComponent { }

View File

@@ -0,0 +1,24 @@
import { UHkConnectedGuard } from './../services/uhk-connected.guard';
import { ModuleWithProviders } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { MissingDeviceComponent } from './../components/missing-device/missing-device.component';
import { MainAppComponent } from './../main-app/main-app.component';
import { mainAppRoutes } from './../main-app/main-app.routes';
const appRoutes: Routes = [
{
path: 'detection',
component: MissingDeviceComponent
},
{
path: '',
component: MainAppComponent,
canActivate: [UHkConnectedGuard],
children: mainAppRoutes
}
];
export const appRoutingProviders: any[] = [];
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes, { useHash: true });

View File

@@ -0,0 +1 @@
export * from './app.component';

View File

@@ -7,7 +7,7 @@ import { KeymapEditGuard } from '../../shared/components/keymap/edit';
export const keymapRoutes: Routes = [
{
path: '',
redirectTo: '/keymap',
redirectTo: 'keymap',
pathMatch: 'full'
},
{

View File

@@ -0,0 +1 @@
export * from './missing-device.component';

View File

@@ -0,0 +1,7 @@
<span class="missing-device-wrapper">
<img class="agent-logo" src="images/agent-icon.png"/>
<div class="messages">
<h1> Cannot find your UHK </h1>
<h2> Please plug it in! </h2>
</div>
</span>

View File

@@ -0,0 +1,19 @@
.missing-device-wrapper {
display: flex;
height: 100%;
align-items: center;
justify-content: center;
}
.agent-logo {
padding: 2em;
}
.message {
display: flex;
flex-direction: column;
> h2 {
margin-top: 10px;
}
}

View File

@@ -0,0 +1,29 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/ignoreElements';
import 'rxjs/add/operator/takeWhile';
import { UhkDeviceService } from './../../services/uhk-device.service';
@Component({
selector: 'missing-device',
templateUrl: 'missing-device.component.html',
styleUrls: ['missing-device.component.scss']
})
export class MissingDeviceComponent {
constructor(uhkDevice: UhkDeviceService, router: Router) {
uhkDevice.isConnected()
.distinctUntilChanged()
.takeWhile(connected => !connected)
.ignoreElements()
.subscribe({
complete: () => {
router.navigate(['/']);
}
});
}
}

View File

@@ -27,7 +27,7 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
</script>
<!-- End Google Tag Manager -->
<main-app></main-app>
<app></app>
<script src="app.uhk.js"></script> <!-- This should be moved to the head -->
</body>

View File

@@ -1,7 +1,12 @@
import { Component, ViewEncapsulation, HostListener } from '@angular/core';
import { Router } from '@angular/router';
import { Store } from '@ngrx/store';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/ignoreElements';
import 'rxjs/add/operator/takeWhile';
import { AppState } from '../shared/store';
import { getUserConfiguration } from '../shared/store/reducers/user-configuration';
@@ -17,7 +22,17 @@ import { UhkDeviceService } from '../services/uhk-device.service';
})
export class MainAppComponent {
constructor(private uhkDevice: UhkDeviceService, private store: Store<AppState>) { }
constructor(private uhkDevice: UhkDeviceService, private store: Store<AppState>, router: Router) {
uhkDevice.isConnected()
.distinctUntilChanged()
.takeWhile(connected => connected)
.ignoreElements()
.subscribe({
complete: () => {
router.navigate(['/detection']);
}
});
}
@HostListener('window:keydown.control.o', ['$event'])
onCtrlO(event: KeyboardEvent): void {

View File

@@ -1,18 +1,13 @@
import { ModuleWithProviders } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { Routes } from '@angular/router';
import { addOnRoutes } from '../shared/components/add-on';
import { keymapRoutes } from '../components/keymap';
import { macroRoutes } from '../shared/components/macro';
import { settingsRoutes } from '../shared/components/settings';
const appRoutes: Routes = [
export const mainAppRoutes: Routes = [
...keymapRoutes,
...macroRoutes,
...addOnRoutes,
...settingsRoutes
];
export const appRoutingProviders: any[] = [ ];
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes, { useHash: true });

View File

@@ -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/first';
import { UhkDeviceService } from './uhk-device.service';
@Injectable()
export class UHkConnectedGuard implements CanActivate {
constructor(private uhkDevice: UhkDeviceService, private router: Router) { }
canActivate(): Observable<boolean> {
return this.uhkDevice.isConnected()
.first()
.do(connected => {
if (!connected) {
return this.router.navigate(['/detection']);
}
});
}
}

View File

@@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import { Injectable, OnDestroy, NgZone } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observer } from 'rxjs/Observer';
import { ConnectableObservable } from 'rxjs/observable/ConnectableObservable';
import { Subject } from 'rxjs/Subject';
@@ -14,7 +15,7 @@ import 'rxjs/add/operator/concatMap';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/publish';
import { Device, Interface, InEndpoint, OutEndpoint, findByIds } from 'usb';
import { Device, Interface, InEndpoint, OutEndpoint, findByIds, on } from 'usb';
import { Layer } from '../shared/config-serializer/config-items/Layer';
import { UhkBuffer } from '../shared/config-serializer/UhkBuffer';
@@ -34,28 +35,38 @@ interface SenderMessage {
};
@Injectable()
export class UhkDeviceService {
export class UhkDeviceService implements OnDestroy {
private device: Device;
private connected: boolean;
private connected$: BehaviorSubject<boolean>;
private messageIn$: Observable<Buffer>;
private messageOut$: Subject<SenderMessage>;
private outSubscription: Subscription;
constructor() {
constructor(zone: NgZone) {
this.messageOut$ = new Subject<SenderMessage>();
this.connected$ = new BehaviorSubject(false);
this.connect();
// The change detection doesn't work properly if the callbacks are called outside Angular Zone
on('attach', (device: Device) => zone.run(() => this.onDeviceAttach(device)));
on('detach', (device: Device) => zone.run(() => this.onDeviceDetach(device)));
}
ngOnDestroy() {
this.disconnect();
this.connected$.unsubscribe();
}
connect(): void {
if (this.connected) {
if (this.connected$.getValue()) {
return;
}
this.device = findByIds(vendorId, productId);
if (!this.device) {
throw new Error('UhkDevice not found.');
return;
}
this.device.open();
@@ -103,18 +114,20 @@ export class UhkDeviceService {
}).publish();
this.outSubscription = outSending.connect();
this.connected = true;
this.connected$.next(true);
}
disconnect() {
if (!this.connected) {
if (!this.connected$.getValue()) {
return;
}
this.outSubscription.unsubscribe();
this.messageIn$ = undefined;
this.device.interface(0).release();
this.device.close();
this.connected = false;
this.connected$.next(false);
}
isConnected(): Observable<boolean> {
return this.connected$.asObservable();
}
sendConfig(configBuffer: Buffer): Observable<Buffer> {
@@ -159,4 +172,22 @@ export class UhkDeviceService {
});
}
onDeviceAttach(device: Device) {
if (device.deviceDescriptor.idVendor !== vendorId || device.deviceDescriptor.idProduct !== productId) {
return;
}
if (!this.connected$.getValue()) {
this.connect();
}
}
onDeviceDetach(device: Device) {
if (device.deviceDescriptor.idVendor !== vendorId || device.deviceDescriptor.idProduct !== productId) {
return;
}
if (this.connected$.getValue()) {
this.disconnect();
}
}
}

View File

@@ -1,14 +1,12 @@
main-app {
display: block;
min-height: 100vh;
width: 100%;
overflow: hidden;
position: relative;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 26px;
}
}
.main-content {
margin-left: 250px;

View File

@@ -30,3 +30,8 @@
text-shadow: 0 0 5px #444;
}
}
main-app {
min-height: 100vh;
width: 100%;
}