refactore: create feature modules (#387)
* add @angular/cli to the project * increase nodejs version -> 8.2.1 * add lerna * merge web and shared module * move electron module into packages as uhk-agent Electron agent functionality is not working * delete symlinker * convert private properties to public of component if used in html * revert uhk-message.component * fix component path * fix the correct name of the uhk-message.component.scss * building web and electron module * delete uhk-renderer package * handle device connect disconnect state * add privilege detection * fix set privilege functionality * turn back download keymap functionality * add bootstrap, select2 js and fix null pointer exception * turn back upload data to keyboard * fix send keymap * fix test-serializer * add missing package.json * merging * fix appveyor build * fix linting * turn back electron storage service * commit the missing electron-datastorage-repository * update node to 8.3.0 in .nvmrc and log node version in appveyor build * set exact version number in appveyor build * vertical align privilege and missing device components * set back node version to 8 in appveyor * move node-usb dependency from usb dir to root maybe it is fix the appveyor build * revert usb to root * fix electron builder script * fix electron builder script * turn off electron devtools * remove CTRL+U functionality * fix CTRL+o * fix lint error * turnoff store freeze * start process when got `Error: EPERM: operation not permitted` error * move files from root usb dir -> packages/usb
This commit is contained in:
committed by
László Monda
parent
97770f67c0
commit
0f558e4132
@@ -0,0 +1,35 @@
|
||||
<h1>
|
||||
<i class="fa fa-keyboard-o"></i>
|
||||
<span>Add new keymap</span>
|
||||
</h1>
|
||||
<div class="keymap__search clearfix">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon" id="sizing-addon1">
|
||||
<i class="fa fa-search"></i>
|
||||
</span>
|
||||
<input type="text" class="form-control" placeholder="Search ..." (input)="filterKeyboards($event.target.value)">
|
||||
</div>
|
||||
<div class="keymap__search_amount">
|
||||
{{ (presets$ | async).length }} / {{ (presetsAll$ | async).length }} keymaps shown
|
||||
</div>
|
||||
</div>
|
||||
<div class="keymap__list">
|
||||
<div #keyboard class="keymap__list_item" *ngFor="let keymap of presets$ | async">
|
||||
<h2>{{ keymap.name }}</h2>
|
||||
<p class="keymap__description">
|
||||
{{ keymap.description }}
|
||||
</p>
|
||||
<svg-keyboard-wrap
|
||||
[keymap]="keymap"
|
||||
[popoverEnabled]="false"
|
||||
[tooltipEnabled]="true"
|
||||
>
|
||||
</svg-keyboard-wrap>
|
||||
<div class="btn-group btn-group-lg">
|
||||
<button class="btn btn-default" (click)="addKeymap(keymap)">Add keymap</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="(presets$ | async).length === 0">
|
||||
Sorry, no keyboard found under this search query.
|
||||
</div>
|
||||
@@ -0,0 +1,61 @@
|
||||
:host {
|
||||
overflow-y: auto;
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.uhk__layer-switcher--wrapper {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: attr(data-title);
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
bottom: -0.3em;
|
||||
right: 100%;
|
||||
font-size: 2.4rem;
|
||||
padding-right: 0.25em;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.keymap {
|
||||
&__search {
|
||||
margin-top: 10px;
|
||||
|
||||
.input-group {
|
||||
width: 100%;
|
||||
max-width: 350px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
&_amount {
|
||||
float: left;
|
||||
margin: 7px 0 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__description {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&__list {
|
||||
margin-top: 40px;
|
||||
|
||||
&_item {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.btn-group-lg {
|
||||
margin: 30px 0 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
||||
.btn {
|
||||
float: none;
|
||||
padding-left: 50px;
|
||||
padding-right: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/combineLatest';
|
||||
import 'rxjs/add/operator/publishReplay';
|
||||
|
||||
import { Keymap } from '../../../config-serializer/config-items/keymap';
|
||||
import { AppState } from '../../../store';
|
||||
import { KeymapActions } from '../../../store/actions';
|
||||
|
||||
@Component({
|
||||
selector: 'keymap-add',
|
||||
templateUrl: './keymap-add.component.html',
|
||||
styleUrls: ['./keymap-add.component.scss'],
|
||||
host: {
|
||||
'class': 'container-fluid'
|
||||
}
|
||||
})
|
||||
export class KeymapAddComponent {
|
||||
presets$: Observable<Keymap[]>;
|
||||
presetsAll$: Observable<Keymap[]>;
|
||||
private filterExpression$: BehaviorSubject<string>;
|
||||
|
||||
constructor(private store: Store<AppState>) {
|
||||
this.presetsAll$ = store.select((appState: AppState) => appState.presetKeymaps);
|
||||
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();
|
||||
}
|
||||
|
||||
filterKeyboards(filterExpression: string) {
|
||||
this.filterExpression$.next(filterExpression);
|
||||
}
|
||||
|
||||
addKeymap(keymap: Keymap) {
|
||||
this.store.dispatch(KeymapActions.addKeymap(keymap));
|
||||
}
|
||||
}
|
||||
2
packages/uhk-web/src/app/components/keymap/edit/index.ts
Normal file
2
packages/uhk-web/src/app/components/keymap/edit/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { KeymapEditComponent } from './keymap-edit.component';
|
||||
export { KeymapEditGuard } from './keymap-edit-guard.service';
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CanActivate, Router } from '@angular/router';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
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 { Keymap } from '../../../config-serializer/config-items/keymap';
|
||||
import { AppState } from '../../../store/index';
|
||||
import { getKeymaps } from '../../../store/reducers/user-configuration';
|
||||
|
||||
@Injectable()
|
||||
export class KeymapEditGuard implements CanActivate {
|
||||
|
||||
constructor(private store: Store<AppState>, private router: Router) { }
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<ng-template [ngIf]="keymap$ | async">
|
||||
<keymap-header [keymap]="keymap$ | async" [deletable]="deletable$ | async" (downloadClick)="downloadKeymap()"></keymap-header>
|
||||
<svg-keyboard-wrap [keymap]="keymap$ | async" [halvesSplit]="keyboardSplit"></svg-keyboard-wrap>
|
||||
</ng-template>
|
||||
|
||||
<div *ngIf="!(keymap$ | async)" class="not-found">
|
||||
Sorry, there is no keymap with this abbreviation.
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
:host {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.not-found {
|
||||
margin-top: 30px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { Component, HostListener, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import '@ngrx/core/add/operator/select';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/first';
|
||||
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';
|
||||
|
||||
import { Keymap } from '../../../config-serializer/config-items/keymap';
|
||||
import { AppState } from '../../../store';
|
||||
import { getKeymap, getKeymaps, getUserConfiguration } from '../../../store/reducers/user-configuration';
|
||||
import 'rxjs/add/operator/pluck';
|
||||
import { SvgKeyboardWrapComponent } from '../../svg/wrap/svg-keyboard-wrap.component';
|
||||
|
||||
@Component({
|
||||
selector: 'keymap-edit',
|
||||
templateUrl: './keymap-edit.component.html',
|
||||
styleUrls: ['./keymap-edit.component.scss'],
|
||||
host: {
|
||||
'class': 'container-fluid'
|
||||
}
|
||||
})
|
||||
export class KeymapEditComponent {
|
||||
|
||||
@ViewChild(SvgKeyboardWrapComponent) wrap: SvgKeyboardWrapComponent;
|
||||
|
||||
keyboardSplit: boolean;
|
||||
|
||||
deletable$: Observable<boolean>;
|
||||
protected keymap$: Observable<Keymap>;
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
route: ActivatedRoute) {
|
||||
this.keymap$ = route
|
||||
.params
|
||||
.pluck<{}, string>('abbr')
|
||||
.switchMap((abbr: string) => store.let(getKeymap(abbr)))
|
||||
.publishReplay(1)
|
||||
.refCount();
|
||||
|
||||
this.deletable$ = store.let(getKeymaps())
|
||||
.map((keymaps: Keymap[]) => keymaps.length > 1);
|
||||
}
|
||||
|
||||
downloadKeymap() {
|
||||
const exportableJSON$: Observable<string> = this.keymap$
|
||||
.switchMap(keymap => this.toExportableJSON(keymap))
|
||||
.map(exportableJSON => JSON.stringify(exportableJSON));
|
||||
|
||||
this.keymap$
|
||||
.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);
|
||||
});
|
||||
}
|
||||
|
||||
@HostListener('window:keydown.alt.s', ['$event'])
|
||||
toggleKeyboardSplit() {
|
||||
this.keyboardSplit = !this.keyboardSplit;
|
||||
}
|
||||
|
||||
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',
|
||||
dataModelVersion: userConfiguration.dataModelVersion,
|
||||
objectType: 'keymap',
|
||||
objectValue: keymap.toJsonObject()
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<uhk-header>
|
||||
<div class="row">
|
||||
<h1 class="col-xs-12 pane-title">
|
||||
<i class="fa fa-keyboard-o"></i>
|
||||
<input #name cancelable
|
||||
class="keymap__name pane-title__name"
|
||||
type="text"
|
||||
(change)="editKeymapName($event.target.value)"
|
||||
(keyup.enter)="name.blur()"
|
||||
/> keymap
|
||||
(<input #abbr cancelable
|
||||
class="keymap__abbrev pane-title__abbrev"
|
||||
type="text"
|
||||
(change)="editKeymapAbbr($event.target.value)"
|
||||
(keyup.enter)="abbr.blur()"
|
||||
[attr.maxLength]="3"
|
||||
/>)
|
||||
<i class="fa keymap__is-default"
|
||||
[ngClass]="{'fa-star-o': !keymap.isDefault, 'fa-star': keymap.isDefault}"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
[title]="starTitle"
|
||||
(click)="setDefault()"
|
||||
></i>
|
||||
<i class="glyphicon glyphicon-trash keymap__remove pull-right"
|
||||
[title]="trashTitle"
|
||||
[class.disabled]="!deletable"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
html="true"
|
||||
(click)="removeKeymap()"
|
||||
></i>
|
||||
<i class="fa fa-files-o keymap__duplicate pull-right"
|
||||
title="Duplicate keymap"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
(click)="duplicateKeymap()"
|
||||
></i>
|
||||
<i class="fa fa-download keymap__download pull-right"
|
||||
title="Download keymap"
|
||||
[html]="true"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
(click)="onDownloadIconClick()"></i>
|
||||
</h1>
|
||||
</div>
|
||||
</uhk-header>
|
||||
@@ -0,0 +1,83 @@
|
||||
@import '../../../../styles/variables';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.keymap {
|
||||
&__is-default {
|
||||
|
||||
&.fa-star-o {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: $icon-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__remove {
|
||||
font-size: 0.75em;
|
||||
top: 8px;
|
||||
|
||||
&:not(.disabled):hover {
|
||||
cursor: pointer;
|
||||
color: $icon-hover-delete;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
&__duplicate {
|
||||
font-size: 0.75em;
|
||||
top: 7px;
|
||||
margin-right: 15px;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $icon-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.keymap__download {
|
||||
top: 10px;
|
||||
font-size: 0.8em;
|
||||
position: relative;
|
||||
margin-right: 10px;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $icon-hover;
|
||||
}
|
||||
}
|
||||
|
||||
.pane-title {
|
||||
margin-bottom: 1em;
|
||||
|
||||
&__name,
|
||||
&__abbrev {
|
||||
border: none;
|
||||
border-bottom: 2px dotted #999;
|
||||
padding: 0;
|
||||
margin: 0 0.25rem;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1px #ccc, 0 0 5px 0 #ccc;
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&__name {
|
||||
width: 290px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&__abbrev {
|
||||
width: 90px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnChanges,
|
||||
Output,
|
||||
Renderer2,
|
||||
SimpleChanges,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { Keymap } from '../../../config-serializer/config-items/keymap';
|
||||
|
||||
import { AppState } from '../../../store';
|
||||
import { KeymapActions } from '../../../store/actions';
|
||||
|
||||
const DEFAULT_TRASH_TITLE = '<span class="text-nowrap">Delete keymap</span>';
|
||||
|
||||
@Component({
|
||||
selector: 'keymap-header',
|
||||
templateUrl: './keymap-header.component.html',
|
||||
styleUrls: ['./keymap-header.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class KeymapHeaderComponent implements OnChanges {
|
||||
|
||||
@Input() keymap: Keymap;
|
||||
@Input() deletable: boolean;
|
||||
@Output() downloadClick = new EventEmitter<void>();
|
||||
|
||||
@ViewChild('name') keymapName: ElementRef;
|
||||
@ViewChild('abbr') keymapAbbr: ElementRef;
|
||||
|
||||
starTitle: string;
|
||||
trashTitle: string = DEFAULT_TRASH_TITLE;
|
||||
|
||||
constructor(private store: Store<AppState>, private renderer: Renderer2) { }
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['keymap']) {
|
||||
this.setKeymapTitle();
|
||||
this.setName();
|
||||
this.setAbbreviation();
|
||||
}
|
||||
if (changes['deletable']) {
|
||||
this.setTrashTitle();
|
||||
}
|
||||
}
|
||||
|
||||
setDefault() {
|
||||
if (!this.keymap.isDefault) {
|
||||
this.store.dispatch(KeymapActions.setDefault(this.keymap.abbreviation));
|
||||
}
|
||||
}
|
||||
|
||||
removeKeymap() {
|
||||
if (this.deletable) {
|
||||
this.store.dispatch(KeymapActions.removeKeymap(this.keymap.abbreviation));
|
||||
}
|
||||
}
|
||||
|
||||
duplicateKeymap() {
|
||||
this.store.dispatch(KeymapActions.duplicateKeymap(this.keymap));
|
||||
}
|
||||
|
||||
editKeymapName(name: string) {
|
||||
if (name.length === 0) {
|
||||
this.setName();
|
||||
return;
|
||||
}
|
||||
|
||||
this.store.dispatch(KeymapActions.editKeymapName(this.keymap.abbreviation, name));
|
||||
}
|
||||
|
||||
editKeymapAbbr(newAbbr: string) {
|
||||
const regexp = new RegExp(/^[a-zA-Z\d]+$/g);
|
||||
|
||||
if (newAbbr.length < 1 || newAbbr.length > 3 || !regexp.test(newAbbr)) {
|
||||
this.setAbbreviation();
|
||||
return;
|
||||
}
|
||||
|
||||
newAbbr = newAbbr.toUpperCase();
|
||||
this.store.dispatch(KeymapActions.editKeymapAbbr(this.keymap.name, this.keymap.abbreviation, newAbbr));
|
||||
}
|
||||
|
||||
setKeymapTitle(): void {
|
||||
this.starTitle = this.keymap.isDefault
|
||||
? 'This is the default keymap which gets activated when powering the keyboard.'
|
||||
: 'Makes this keymap the default keymap which gets activated when powering the keyboard.';
|
||||
}
|
||||
|
||||
setTrashTitle(): void {
|
||||
this.trashTitle = this.deletable
|
||||
? DEFAULT_TRASH_TITLE
|
||||
: '<span class="text-nowrap">The last keymap cannot be deleted.</span>';
|
||||
}
|
||||
|
||||
onDownloadIconClick(): void {
|
||||
this.downloadClick.emit();
|
||||
}
|
||||
|
||||
private setName(): void {
|
||||
this.renderer.setProperty(this.keymapName.nativeElement, 'value', this.keymap.name);
|
||||
}
|
||||
|
||||
private setAbbreviation() {
|
||||
this.renderer.setProperty(this.keymapAbbr.nativeElement, 'value', this.keymap.abbreviation);
|
||||
}
|
||||
}
|
||||
4
packages/uhk-web/src/app/components/keymap/index.ts
Normal file
4
packages/uhk-web/src/app/components/keymap/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './add/keymap-add.component';
|
||||
export * from './edit/keymap-edit.component';
|
||||
export * from './header/keymap-header.component';
|
||||
export * from './keymap.routes';
|
||||
26
packages/uhk-web/src/app/components/keymap/keymap.routes.ts
Normal file
26
packages/uhk-web/src/app/components/keymap/keymap.routes.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { KeymapAddComponent } from './add/keymap-add.component';
|
||||
import { KeymapEditComponent } from './edit';
|
||||
import { KeymapEditGuard } from './edit';
|
||||
|
||||
export const keymapRoutes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'keymap',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'keymap',
|
||||
component: KeymapEditComponent,
|
||||
canActivate: [KeymapEditGuard]
|
||||
},
|
||||
{
|
||||
path: 'keymap/add',
|
||||
component: KeymapAddComponent
|
||||
},
|
||||
{
|
||||
path: 'keymap/:abbr',
|
||||
component: KeymapEditComponent
|
||||
}
|
||||
];
|
||||
Reference in New Issue
Block a user