Keymap data layer (#95)

This commit is contained in:
Nejc Zdovc
2016-09-18 12:38:42 +02:00
committed by József Farkas
parent fb8cd163ec
commit b78bc5850f
40 changed files with 748 additions and 195 deletions

View File

@@ -8,6 +8,8 @@
}, },
"license": "GPL-3.0", "license": "GPL-3.0",
"devDependencies": { "devDependencies": {
"@ngrx/store-devtools": "3.0.2",
"@ngrx/store-log-monitor": "3.0.2",
"@types/core-js": "^0.9.32", "@types/core-js": "^0.9.32",
"@types/jquery": "1.10.31", "@types/jquery": "1.10.31",
"@types/node": "6.0.38", "@types/node": "6.0.38",
@@ -40,6 +42,9 @@
"@angular/platform-browser": "2.0.0", "@angular/platform-browser": "2.0.0",
"@angular/platform-browser-dynamic": "2.0.0", "@angular/platform-browser-dynamic": "2.0.0",
"@angular/router": "3.0.0", "@angular/router": "3.0.0",
"@ngrx/core": "1.2.0",
"@ngrx/effects": "2.0.0-beta.3",
"@ngrx/store": "2.2.1",
"bootstrap": "^3.3.7", "bootstrap": "^3.3.7",
"browser-stdout": "^1.3.0", "browser-stdout": "^1.3.0",
"core-js": "2.4.1", "core-js": "2.4.1",

View File

@@ -1,13 +1,15 @@
import { NgModule } from '@angular/core'; import { NgModule, ReflectiveInjector } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { StoreModule } from '@ngrx/store';
import { DragulaModule } from 'ng2-dragula/ng2-dragula'; import { DragulaModule } from 'ng2-dragula/ng2-dragula';
import { Select2Component } from 'ng2-select2/ng2-select2'; import { Select2Component } from 'ng2-select2/ng2-select2';
import { ContenteditableDirective } from './directives/contenteditable'; import { ContenteditableDirective } from './directives/contenteditable';
import { KeymapAddComponent, KeymapComponent } from './components/keymap'; import { KeymapAddComponent, KeymapComponent, KeymapHeaderComponent } from './components/keymap';
import { LayersComponent } from './components/layers'; import { LayersComponent } from './components/layers';
import { LegacyLoaderComponent } from './components/legacy-loader'; import { LegacyLoaderComponent } from './components/legacy-loader';
import { import {
@@ -51,11 +53,30 @@ import { DataProviderService } from './services/data-provider.service';
import { MapperService } from './services/mapper.service'; import { MapperService } from './services/mapper.service';
import { UhkConfigurationService } from './services/uhk-configuration.service'; import { UhkConfigurationService } from './services/uhk-configuration.service';
import { DataStorage } from './store/storage';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { StoreLogMonitorModule, useLogMonitor } from '@ngrx/store-log-monitor';
import { keymapReducer, macroReducer, presetReducer } from './store/reducers';
// Create DataStorage dependency injection
const storageProvider = ReflectiveInjector.resolve([DataStorage]);
const storageInjector = ReflectiveInjector.fromResolvedProviders(storageProvider);
const storageService: DataStorage = storageInjector.get(DataStorage);
// All reducers that are used in application
const storeConfig = {
keymaps: storageService.saveSate(keymapReducer),
macros: storageService.saveSate(macroReducer),
presetKeymaps: presetReducer
};
@NgModule({ @NgModule({
declarations: [ declarations: [
Select2Component, Select2Component,
MainAppComponent, MainAppComponent,
KeymapComponent, KeymapComponent,
KeymapHeaderComponent,
LegacyLoaderComponent, LegacyLoaderComponent,
NotificationComponent, NotificationComponent,
SvgIconTextKeyComponent, SvgIconTextKeyComponent,
@@ -95,7 +116,15 @@ import { UhkConfigurationService } from './services/uhk-configuration.service';
BrowserModule, BrowserModule,
FormsModule, FormsModule,
DragulaModule, DragulaModule,
routing routing,
StoreModule.provideStore(storeConfig, storageService.initialState()),
StoreDevtoolsModule.instrumentStore({
monitor: useLogMonitor({
visible: false,
position: 'right'
})
}),
StoreLogMonitorModule
], ],
providers: [ providers: [
DataProviderService, DataProviderService,

View File

@@ -10,23 +10,26 @@
<input type="text" class="form-control" placeholder="Search ..." (input)="filterKeyboards($event.target.value)"> <input type="text" class="form-control" placeholder="Search ..." (input)="filterKeyboards($event.target.value)">
</div> </div>
<div class="keymap__search_amount"> <div class="keymap__search_amount">
{{ keymaps.length }} / {{ keymapsAll.length }} keymaps shown {{ (presets$ | async).length }} / {{ (presetsAll$ | async).length }} keymaps shown
</div> </div>
</div> </div>
<div class="keymap__list"> <div class="keymap__list">
<div #keyboard class="keymap__list_item" *ngFor="let keymap of keymaps"> <div #keyboard class="keymap__list_item" *ngFor="let keymap of presets$ | async">
<h2>{{ keymap.name }}</h2> <h2>{{ keymap.name }}</h2>
<p class="keymap__description"> <p class="keymap__description">
{{ keymap.description }} {{ keymap.description }}
</p> </p>
<svg-keyboard-wrap <svg-keyboard-wrap
[layers]="keymap.layers.elements" [keymap]="keymap"
[popoverEnabled]="false" [popoverEnabled]="false"
[tooltipEnabled]="true" [tooltipEnabled]="true"
> >
</svg-keyboard-wrap> </svg-keyboard-wrap>
<div class="btn-group btn-group-lg"> <div class="btn-group btn-group-lg">
<button class="btn btn-default">Add keymap</button> <button class="btn btn-default" (click)="addKeymap(keymap)">Add keymap</button>
</div> </div>
</div> </div>
</div> </div>
<div *ngIf="(presets$ | async).length === 0">
Sorry, no keyboard found under this search query.
</div>

View File

@@ -1,8 +1,13 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import 'rxjs/add/operator/publishReplay';
import { Observable } from 'rxjs/Observable';
import { ConnectableObservable } from 'rxjs/observable/ConnectableObservable';
import { Keymap } from '../../../config-serializer/config-items/Keymap'; import { Keymap } from '../../../config-serializer/config-items/Keymap';
import { Keymaps } from '../../../config-serializer/config-items/Keymaps'; import { AppState } from '../../../store';
import {DataProviderService} from '../../../services/data-provider.service'; import { KeymapActions } from '../../../store/actions';
@Component({ @Component({
selector: 'keymap-add', selector: 'keymap-add',
@@ -10,19 +15,32 @@ import {DataProviderService} from '../../../services/data-provider.service';
styles: [require('./keymap-add.component.scss')] styles: [require('./keymap-add.component.scss')]
}) })
export class KeymapAddComponent { export class KeymapAddComponent {
private keymaps: Keymap[]; private presets$: Observable<Keymap[]>;
private keymapsAll: Keymap[]; private presetsAll$: Observable<Keymap[]>;
private currentKeyboards: number;
constructor(data: DataProviderService) { constructor(private store: Store<AppState>) {
let json: any = data.getDefaultKeymaps(); let presetConnectable: ConnectableObservable<Keymap[]> = store
let all: Keymaps = new Keymaps().fromJsObject(json.keymaps); .select((appState: AppState) => appState.presetKeymaps)
this.keymaps = all.elements; .publishReplay();
this.keymapsAll = this.keymaps.slice(0);
this.currentKeyboards = this.keymaps.length; this.presets$ = presetConnectable;
presetConnectable.connect();
this.presetsAll$ = store.select((appState: AppState) => appState.presetKeymaps);
} }
filterKeyboards(value: string) { filterKeyboards(value: string) {
this.keymaps = this.keymapsAll.filter((item: Keymap) => item.name.toLocaleLowerCase().indexOf(value) !== -1); let presetConnectable: ConnectableObservable<Keymap[]> = this.presetsAll$
.map((keymaps: Keymap[]) => keymaps.filter(
(keymap: Keymap) => keymap.name.toLocaleLowerCase().includes(value))
)
.publishReplay();
this.presets$ = presetConnectable;
presetConnectable.connect();
}
addKeymap(keymap: Keymap) {
this.store.dispatch(KeymapActions.addKeymap(keymap));
} }
} }

View File

@@ -0,0 +1,30 @@
<div class="row">
<h1 class="col-xs-12 pane-title">
<i class="fa fa-keyboard-o"></i>
<input class="keymap__name pane-title__name"
value="{{ keymap.name }}"
(change)="editKeymapName($event.target.value)"
/> keymap
(<input class="keymap__abbrev pane-title__abbrev"
value="{{ keymap.abbreviation }}"
(change)="editKeymapAbbr($event.target.value)"
[attr.maxLength]="3"
/>)
<i class="fa keymap__is-default"
[ngClass]="{'fa-star-o': !keymap.isDefault, 'fa-star': keymap.isDefault}"
(click)="setDefault()"
></i>
<i class="glyphicon glyphicon-trash keymap__remove pull-right" title=""
data-toggle="tooltip"
data-placement="left"
data-original-title="Remove keymap"
(click)="removeKeymap()"
></i>
<i class="fa fa-files-o keymap__duplicate pull-right" title=""
data-toggle="tooltip"
data-placement="left"
data-original-title="Duplicate keymap"
(click)="duplicateKeymap()"
></i>
</h1>
</div>

View File

@@ -0,0 +1,61 @@
.keymap {
&__is-default {
&.fa-star-o {
cursor: pointer;
&:hover {
color: #b9b6b6;
}
}
}
&__remove {
font-size: 0.75em;
top: 8px;
&:hover {
cursor: pointer;
color: #900;
}
}
&__duplicate {
font-size: 0.75em;
top: 7px;
margin-right: 15px;
position: relative;
&:hover {
cursor: pointer;
color: #900;
}
}
}
.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;
}
}

View File

@@ -0,0 +1,44 @@
import { Component, Input } 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';
@Component({
selector: 'keymap-header',
template: require('./keymap-header.component.html'),
styles: [require('./keymap-header.component.scss')]
})
export class KeymapHeaderComponent {
@Input() keymap: Keymap;
constructor(
private store: Store<AppState>
) { }
setDefault() {
if (!this.keymap.isDefault) {
this.store.dispatch(KeymapActions.setDefault(this.keymap.abbreviation));
}
}
removeKeymap() {
this.store.dispatch(KeymapActions.removeKeymap(this.keymap.abbreviation));
}
duplicateKeymap() {
this.store.dispatch(KeymapActions.duplicateKeymap(this.keymap));
}
editKeymapName(name: string) {
this.store.dispatch(KeymapActions.editKeymapName(this.keymap.abbreviation, name));
}
editKeymapAbbr(newAbbr: string) {
newAbbr = newAbbr.toUpperCase();
this.store.dispatch(KeymapActions.editKeymapAbbr(this.keymap.abbreviation, newAbbr));
}
}

View File

@@ -1,3 +1,4 @@
export * from './keymap.component'; export * from './keymap.component';
export * from './keymap.routes'; export * from './keymap.routes';
export * from './add/keymap-add.component'; export * from './add/keymap-add.component';
export * from './header/keymap-header.component';

View File

@@ -1,13 +1,8 @@
<template [ngIf]="keymap"> <template [ngIf]="keymap$ | async">
<div class="row"> <keymap-header [keymap]="keymap$ | async"></keymap-header>
<h1 class="col-xs-12 pane-title"> <svg-keyboard-wrap [keymap]="keymap$ | async"></svg-keyboard-wrap>
<i class="fa fa-keyboard-o"></i>
<span class="keymap__name pane-title__name" contenteditable="true">{{keymap.name}}</span> keymap
(<span class="keymap__abbrev pane-title__abbrev" contenteditable="true">{{keymap.abbreviation}}</span>)
<i class="fa fa-star-o fa-star keymap__is-default"></i>
<i class="glyphicon glyphicon-trash keymap__remove pull-right" title=""
data-toggle="tooltip" data-placement="left" data-original-title="Remove keymap"></i>
</h1>
</div>
<svg-keyboard-wrap [layers]="layers.elements"></svg-keyboard-wrap>
</template> </template>
<div *ngIf="!(keymap$ | async)" class="not-found">
Sorry, there is no keymap with this abbreviation.
</div>

View File

@@ -4,48 +4,9 @@
display: block; display: block;
} }
.keymap { .not-found {
&__is-default { margin-top: 30px;
&.fa-star { font-size: 16px;
color: #333; text-align: center;
}
&.fa-star-o {
color: #333;
}
&:hover {
color: #555;
cursor: pointer;
}
}
&__remove {
font-size: 0.75em;
top: 0.3em;
&:hover {
cursor: pointer;
color: #900;
}
}
} }
.pane-title {
margin-bottom: 1em;
&__name,
&__abbrev {
&[contenteditable=true] {
border: none;
border-bottom: 2px dotted #999;
padding: 0 0.5rem;
margin: 0 0.25rem;
&.active {
box-shadow: 0 0 0 1px #ccc, 0 0 5px 0 #ccc;
border-color: transparent;
}
}
}
}

View File

@@ -1,42 +1,38 @@
import { Component, OnInit } from '@angular/core'; import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import '@ngrx/core/add/operator/select';
import { Store } from '@ngrx/store';
import 'rxjs/add/operator/let';
import 'rxjs/add/operator/publishReplay';
import 'rxjs/add/operator/switchMap';
import { Observable } from 'rxjs/Observable';
import { ConnectableObservable } from 'rxjs/observable/ConnectableObservable';
import { Keymap } from '../../config-serializer/config-items/Keymap'; import { Keymap } from '../../config-serializer/config-items/Keymap';
import { Layers } from '../../config-serializer/config-items/Layers'; import { AppState } from '../../store';
import { UhkConfigurationService } from '../../services/uhk-configuration.service'; import { getKeymap } from '../../store/reducers/keymap';
@Component({ @Component({
selector: 'keymap', selector: 'keymap',
template: require('./keymap.component.html'), template: require('./keymap.component.html'),
styles: [require('./keymap.component.scss')] styles: [require('./keymap.component.scss')]
}) })
export class KeymapComponent implements OnInit { export class KeymapComponent {
private keymapId: number = 0; private keymap$: Observable<Keymap>;
private layers: Layers;
private keymap: Keymap;
private subParams: Subscription;
constructor( constructor(
private uhkConfigurationService: UhkConfigurationService, private store: Store<AppState>,
private route: ActivatedRoute private route: ActivatedRoute
) { ) {
} let keymapConnectable: ConnectableObservable<Keymap> = route
.params
.select<string>('abbr')
.switchMap((abbr: string) => store.let(getKeymap(abbr)))
.publishReplay();
ngOnInit() { this.keymap$ = keymapConnectable;
this.subParams = this.route.params.subscribe((params: { id: string }) => { keymapConnectable.connect();
let id: number = +params.id;
if (!isNaN(id)) {
this.keymapId = id;
}
this.keymap = this.uhkConfigurationService.getUhkConfiguration().keymaps.elements[this.keymapId];
this.layers = this.keymap.layers;
});
}
ngOnDestroy() {
this.subParams.unsubscribe();
} }
} }

View File

@@ -18,7 +18,7 @@ export const keymapRoutes: Routes = [
component: KeymapAddComponent component: KeymapAddComponent
}, },
{ {
path: 'keymap/:id', path: 'keymap/:abbr',
component: KeymapComponent component: KeymapComponent
} }
]; ];

View File

@@ -46,7 +46,7 @@
<layer-tab #tab *ngSwitchCase="TabName.Layer" class="popover-content" [defaultKeyAction]="defaultKeyAction"></layer-tab> <layer-tab #tab *ngSwitchCase="TabName.Layer" class="popover-content" [defaultKeyAction]="defaultKeyAction"></layer-tab>
<mouse-tab #tab *ngSwitchCase="TabName.Mouse" class="popover-content" [defaultKeyAction]="defaultKeyAction"></mouse-tab> <mouse-tab #tab *ngSwitchCase="TabName.Mouse" class="popover-content" [defaultKeyAction]="defaultKeyAction"></mouse-tab>
<macro-tab #tab *ngSwitchCase="TabName.Macro" class="popover-content" [defaultKeyAction]="defaultKeyAction"></macro-tab> <macro-tab #tab *ngSwitchCase="TabName.Macro" class="popover-content" [defaultKeyAction]="defaultKeyAction"></macro-tab>
<keymap-tab #tab *ngSwitchCase="TabName.Keymap" class="popover-content" [defaultKeyAction]="defaultKeyAction"></keymap-tab> <keymap-tab #tab *ngSwitchCase="TabName.Keymap" class="popover-content" [defaultKeyAction]="defaultKeyAction" [keymaps]="keymaps$ | async"></keymap-tab>
<none-tab #tab *ngSwitchCase="TabName.None" class="popover-content"></none-tab> <none-tab #tab *ngSwitchCase="TabName.None" class="popover-content"></none-tab>
</div> </div>
<div class="row"> <div class="row">

View File

@@ -1,4 +1,6 @@
import {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { Store } from '@ngrx/store';
import { import {
KeyAction, KeyAction,
@@ -8,7 +10,12 @@ import {
SwitchKeymapAction, SwitchKeymapAction,
SwitchLayerAction SwitchLayerAction
} from '../../config-serializer/config-items/key-action'; } from '../../config-serializer/config-items/key-action';
import {Tab} from './tab/tab'; import { Keymap } from '../../config-serializer/config-items/Keymap';
import { Tab } from './tab/tab';
import { AppState } from '../../store';
import { Observable } from 'rxjs/Observable';
enum TabName { enum TabName {
Keypress, Keypress,
@@ -38,11 +45,15 @@ export class PopoverComponent implements OnInit {
private TabName = TabName; private TabName = TabName;
/* tslint:enable:no-unused-variable tslint:enable:variable-name */ /* tslint:enable:no-unused-variable tslint:enable:variable-name */
private activeTab: TabName; private activeTab: TabName;
private keymaps$: Observable<Keymap[]>;
constructor() { } constructor(private store: Store<AppState>) {
this.keymaps$ = store.select((appState: AppState) => appState.keymaps);
}
ngOnInit() { ngOnInit() {
let tab: TabName; let tab: TabName;
if (this.defaultKeyAction instanceof KeystrokeAction) { if (this.defaultKeyAction instanceof KeystrokeAction) {
tab = TabName.Keypress; tab = TabName.Keypress;
} else if (this.defaultKeyAction instanceof SwitchLayerAction) { } else if (this.defaultKeyAction instanceof SwitchLayerAction) {
@@ -56,6 +67,7 @@ export class PopoverComponent implements OnInit {
} else { } else {
tab = TabName.None; tab = TabName.None;
} }
this.selectTab(tab); this.selectTab(tab);
} }
@@ -76,5 +88,4 @@ export class PopoverComponent implements OnInit {
selectTab(tab: TabName): void { selectTab(tab: TabName): void {
this.activeTab = tab; this.activeTab = tab;
} }
} }

View File

@@ -1,12 +1,17 @@
<div> <div>
<b>Switch to keymap:</b> <b>Switch to keymap:</b>
<select2 [data]="keymapOptions" [value]="keymapOptions[selectedKeymapIndex + 1].id" (valueChanged)="onChange($event)" [width]="'100%'"></select2> <select2
[data]="keymapOptions"
[value]="selectedKeymap?.abbreviation"
(valueChanged)="onChange($event)"
[width]="'100%'"
></select2>
</div> </div>
<div> <div>
<div *ngIf="selectedKeymapIndex === -1"> <div *ngIf="!selectedKeymap?.abbreviation">
<img src="./images/base-layer--blank.svg"> <img src="./images/base-layer--blank.svg">
</div> </div>
<svg-keyboard *ngIf="selectedKeymapIndex >= 0" <svg-keyboard *ngIf="selectedKeymap?.abbreviation"
[moduleConfig]="keymaps[selectedKeymapIndex].layers.elements[0].modules.elements"> [moduleConfig]="selectedKeymap.layers.elements[0].modules.elements">
</svg-keyboard> </svg-keyboard>
</div> </div>

View File

@@ -1,56 +1,55 @@
import {Component, Input, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import {Select2OptionData} from 'ng2-select2/ng2-select2'; import { Select2OptionData } from 'ng2-select2/ng2-select2';
import {KeyAction, SwitchKeymapAction} from '../../../../config-serializer/config-items/key-action'; import { KeyAction, SwitchKeymapAction } from '../../../../config-serializer/config-items/key-action';
import {Keymap} from '../../../../config-serializer/config-items/Keymap'; import { Keymap } from '../../../../config-serializer/config-items/Keymap';
import {Tab} from '../tab'; import { Tab } from '../tab';
import {UhkConfigurationService} from '../../../../services/uhk-configuration.service';
@Component({ @Component({
selector: 'keymap-tab', selector: 'keymap-tab',
template: require('./keymap-tab.component.html'), template: require('./keymap-tab.component.html'),
styles: [require('./keymap-tab.component.scss')] styles: [require('./keymap-tab.component.scss')],
changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class KeymapTabComponent implements OnInit, Tab { export class KeymapTabComponent implements OnInit, Tab {
@Input() defaultKeyAction: KeyAction; @Input() defaultKeyAction: KeyAction;
@Input() keymaps: Keymap[];
private keymaps: Keymap[];
private keymapOptions: Array<Select2OptionData>; private keymapOptions: Array<Select2OptionData>;
private selectedKeymapIndex: number; private selectedKeymap: Keymap;
constructor(private uhkConfigurationService: UhkConfigurationService) { constructor() {
this.keymaps = [];
this.keymapOptions = []; this.keymapOptions = [];
this.selectedKeymapIndex = -1;
} }
ngOnInit() { ngOnInit() {
this.keymaps = this.uhkConfigurationService.getUhkConfiguration().keymaps.elements;
this.keymapOptions.push({ this.keymapOptions.push({
id: '-1', id: '-1',
text: 'Switch to keymap' text: 'Switch to keymap'
}); });
this.keymapOptions = this.keymapOptions.concat(this.keymaps.map(function (keymap: Keymap): Select2OptionData { this.keymapOptions = this.keymaps.map((keymap: Keymap): Select2OptionData => {
return { return {
id: keymap.id.toString(), id: keymap.abbreviation,
text: keymap.name text: keymap.name
}; };
})); });
this.fromKeyAction(this.defaultKeyAction); this.fromKeyAction(this.defaultKeyAction);
} }
// TODO: change to the correct type when the wrapper has added it. // TODO: change to the correct type when the wrapper has added it.
onChange(event: any) { onChange(event: any) {
this.selectedKeymapIndex = +event.value; if (event.value === '-1') {
this.selectedKeymap = undefined;
} else {
this.selectedKeymap = this.keymaps.find((keymap: Keymap) => keymap.abbreviation === event.value);
}
} }
keyActionValid(): boolean { keyActionValid(): boolean {
return this.selectedKeymapIndex >= 0; return !!this.selectedKeymap;
} }
fromKeyAction(keyAction: KeyAction): boolean { fromKeyAction(keyAction: KeyAction): boolean {
@@ -58,16 +57,17 @@ export class KeymapTabComponent implements OnInit, Tab {
return false; return false;
} }
let switchKeymapAction: SwitchKeymapAction = <SwitchKeymapAction>keyAction; let switchKeymapAction: SwitchKeymapAction = <SwitchKeymapAction>keyAction;
this.selectedKeymapIndex = this.keymaps.findIndex(keymap => switchKeymapAction.keymapId === keymap.id); this.selectedKeymap = this.keymaps
return true; .find((keymap: Keymap) => keymap.abbreviation === switchKeymapAction.keymapAbbreviation);
} }
toKeyAction(): SwitchKeymapAction { toKeyAction(): SwitchKeymapAction {
if (!this.keyActionValid()) { if (!this.keyActionValid()) {
throw new Error('KeyAction is not valid. No selected keymap!'); throw new Error('KeyAction is not valid. No selected keymap!');
} }
let keymapAction = new SwitchKeymapAction(); let keymapAction = new SwitchKeymapAction();
keymapAction.keymapId = this.keymaps[this.selectedKeymapIndex].id; keymapAction.keymapAbbreviation = this.selectedKeymap.abbreviation;
return keymapAction; return keymapAction;
} }
} }

View File

@@ -8,9 +8,9 @@
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'keymap')"></i> <i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'keymap')"></i>
</div> </div>
<ul [@toggler]="animation.keymap"> <ul [@toggler]="animation.keymap">
<li *ngFor="let keymap of keymaps" class="sidebar__level-2--item"> <li *ngFor="let keymap of keymaps$ | async" class="sidebar__level-2--item">
<div class="sidebar__level-2"> <div class="sidebar__level-2">
<a [routerLink]="['/keymap', keymap.id]">{{keymap.name}}</a> <a [routerLink]="['/keymap', keymap.abbreviation]">{{keymap.name}}</a>
<i *ngIf="keymap.isDefault" class="fa fa-star sidebar__fav"></i> <i *ngIf="keymap.isDefault" class="fa fa-star sidebar__fav"></i>
</div> </div>
</li> </li>
@@ -25,7 +25,7 @@
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'macro')"></i> <i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'macro')"></i>
</div> </div>
<ul [@toggler]="animation.macro"> <ul [@toggler]="animation.macro">
<li *ngFor="let macro of macros" class="sidebar__level-2--item"> <li *ngFor="let macro of macros$ | async" class="sidebar__level-2--item">
<div class="sidebar__level-2"> <div class="sidebar__level-2">
<a [routerLink]="['/macro', macro.id]">{{macro.name}}</a> <a [routerLink]="['/macro', macro.id]">{{macro.name}}</a>
</div> </div>

View File

@@ -1,9 +1,11 @@
import { Component, OnInit, Renderer, animate, state, style, transition, trigger } from '@angular/core'; import { Component, Renderer, animate, state, style, transition, trigger } from '@angular/core';
import { Keymap } from '../../config-serializer/config-items/Keymap'; import { Keymap } from '../../config-serializer/config-items/Keymap';
import { Macro } from '../../config-serializer/config-items/Macro'; import { Macro } from '../../config-serializer/config-items/Macro';
import { UhkConfigurationService } from '../../services/uhk-configuration.service'; import { AppState } from '../../store';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
@Component({ @Component({
animations: [ animations: [
@@ -21,22 +23,20 @@ import { UhkConfigurationService } from '../../services/uhk-configuration.servic
template: require('./side-menu.component.html'), template: require('./side-menu.component.html'),
styles: [require('./side-menu.component.scss')] styles: [require('./side-menu.component.scss')]
}) })
export class SideMenuComponent implements OnInit { export class SideMenuComponent {
private keymaps: Keymap[]; private keymaps$: Observable<Keymap[]>;
private macros: Macro[]; private macros$: Observable<Macro[]>;
private animation: {[key: string]: 'active' | 'inactive'}; private animation: {[key: string]: 'active' | 'inactive'};
constructor(private uhkConfigurationService: UhkConfigurationService, private renderer: Renderer) { constructor(private store: Store<AppState>, private renderer: Renderer) {
this.animation = { this.animation = {
keymap: 'active', keymap: 'active',
macro: 'active', macro: 'active',
addon: 'active' addon: 'active'
}; };
}
ngOnInit() { this.keymaps$ = store.select(appState => appState.keymaps);
this.keymaps = this.uhkConfigurationService.getUhkConfiguration().keymaps.elements; this.macros$ = store.select(appState => appState.macros);
this.macros = this.uhkConfigurationService.getUhkConfiguration().macros.elements;
} }
toggleHide(event: Event, type: string) { toggleHide(event: Event, type: string) {

View File

@@ -145,8 +145,7 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges {
} else if (this.keyAction instanceof SwitchKeymapAction) { } else if (this.keyAction instanceof SwitchKeymapAction) {
let keyAction: SwitchKeymapAction = this.keyAction as SwitchKeymapAction; let keyAction: SwitchKeymapAction = this.keyAction as SwitchKeymapAction;
this.labelType = LabelTypes.SwitchKeymap; this.labelType = LabelTypes.SwitchKeymap;
let uhkConfiguration: UhkConfiguration = this.uhkConfigurationService.getUhkConfiguration(); this.labelSource = keyAction.keymapAbbreviation;
this.labelSource = uhkConfiguration.getKeymap(keyAction.keymapId).abbreviation;
} else if (this.keyAction instanceof PlayMacroAction) { } else if (this.keyAction instanceof PlayMacroAction) {
let keyAction: PlayMacroAction = this.keyAction as PlayMacroAction; let keyAction: PlayMacroAction = this.keyAction as PlayMacroAction;
this.labelType = LabelTypes.IconText; this.labelType = LabelTypes.IconText;

View File

@@ -1,11 +1,17 @@
import { import {
Component, Input, OnChanges, OnInit, animate, Component, Input, OnChanges, SimpleChanges, animate,
state, style, transition, trigger state, style, transition, trigger
} from '@angular/core'; } from '@angular/core';
import { Store } from '@ngrx/store';
import { KeyAction, NoneAction } from '../../../config-serializer/config-items/key-action'; import { KeyAction, NoneAction } from '../../../config-serializer/config-items/key-action';
import { Keymap } from '../../../config-serializer/config-items/Keymap';
import { Layer } from '../../../config-serializer/config-items/Layer'; import { Layer } from '../../../config-serializer/config-items/Layer';
import { AppState } from '../../../store';
import { KeymapActions } from '../../../store/actions';
@Component({ @Component({
selector: 'svg-keyboard-wrap', selector: 'svg-keyboard-wrap',
template: require('./svg-keyboard-wrap.component.html'), template: require('./svg-keyboard-wrap.component.html'),
@@ -74,8 +80,8 @@ import { Layer } from '../../../config-serializer/config-items/Layer';
]) ])
] ]
}) })
export class SvgKeyboardWrapComponent implements OnInit, OnChanges { export class SvgKeyboardWrapComponent implements OnChanges {
@Input() layers: Layer[]; @Input() keymap: Keymap;
@Input() popoverEnabled: boolean = true; @Input() popoverEnabled: boolean = true;
@Input() tooltipEnabled: boolean = false; @Input() tooltipEnabled: boolean = false;
@@ -84,8 +90,11 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
private popoverInitKeyAction: KeyAction; private popoverInitKeyAction: KeyAction;
private currentLayer: number = 0; private currentLayer: number = 0;
private tooltipData: { posTop: number, posLeft: number, content: {name: string, value: string}[], shown: boolean }; private tooltipData: { posTop: number, posLeft: number, content: {name: string, value: string}[], shown: boolean };
private layers: Layer[];
constructor() { constructor(
private store: Store<AppState>
) {
this.keyEditConfig = { this.keyEditConfig = {
keyActions: undefined, keyActions: undefined,
keyId: undefined keyId: undefined
@@ -99,15 +108,15 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
}; };
} }
ngOnInit() { ngOnChanges(changes: SimpleChanges) {
this.layers[0].animation = 'leftIn'; if (changes['keymap'].previousValue.abbreviation !== changes['keymap'].currentValue.abbreviation) {
} this.layers = this.keymap.layers.elements;
this.currentLayer = 0;
ngOnChanges() { if (this.layers.length > 0) {
this.currentLayer = 0; this.layers.forEach(element => element.animation = 'none');
if (this.layers.length > 0) { this.layers[0].animation = 'leftIn';
this.layers.forEach(element => element.animation = 'none'); }
this.layers[0].animation = 'leftIn';
} }
} }
@@ -137,6 +146,7 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
onRemap(keyAction: KeyAction): void { onRemap(keyAction: KeyAction): void {
this.changeKeyAction(keyAction); this.changeKeyAction(keyAction);
this.store.dispatch(KeymapActions.saveKey(this.keymap));
this.hidePopover(); this.hidePopover();
} }

View File

@@ -5,9 +5,6 @@ import { Layers } from './Layers';
export class Keymap extends Serializable<Keymap> { export class Keymap extends Serializable<Keymap> {
@assertUInt8
id: number;
name: string; name: string;
description: string; description: string;
@@ -23,7 +20,7 @@ export class Keymap extends Serializable<Keymap> {
if (!keymap) { if (!keymap) {
return; return;
} }
this.id = keymap.id;
this.name = keymap.name; this.name = keymap.name;
this.description = keymap.description; this.description = keymap.description;
this.abbreviation = keymap.abbreviation; this.abbreviation = keymap.abbreviation;
@@ -32,7 +29,6 @@ export class Keymap extends Serializable<Keymap> {
} }
_fromJsObject(jsObject: any): Keymap { _fromJsObject(jsObject: any): Keymap {
this.id = jsObject.id;
this.isDefault = jsObject.isDefault; this.isDefault = jsObject.isDefault;
this.abbreviation = jsObject.abbreviation; this.abbreviation = jsObject.abbreviation;
this.name = jsObject.name; this.name = jsObject.name;
@@ -42,7 +38,6 @@ export class Keymap extends Serializable<Keymap> {
} }
_fromBinary(buffer: UhkBuffer): Keymap { _fromBinary(buffer: UhkBuffer): Keymap {
this.id = buffer.readUInt8();
this.isDefault = buffer.readBoolean(); this.isDefault = buffer.readBoolean();
this.abbreviation = buffer.readString(); this.abbreviation = buffer.readString();
this.name = buffer.readString(); this.name = buffer.readString();
@@ -53,7 +48,6 @@ export class Keymap extends Serializable<Keymap> {
_toJsObject(): any { _toJsObject(): any {
return { return {
id: this.id,
isDefault: this.isDefault, isDefault: this.isDefault,
abbreviation: this.abbreviation, abbreviation: this.abbreviation,
name: this.name, name: this.name,
@@ -63,7 +57,6 @@ export class Keymap extends Serializable<Keymap> {
} }
_toBinary(buffer: UhkBuffer): void { _toBinary(buffer: UhkBuffer): void {
buffer.writeUInt8(this.id);
buffer.writeBoolean(this.isDefault); buffer.writeBoolean(this.isDefault);
buffer.writeString(this.abbreviation); buffer.writeString(this.abbreviation);
buffer.writeString(this.name); buffer.writeString(this.name);
@@ -72,6 +65,6 @@ export class Keymap extends Serializable<Keymap> {
} }
toString(): string { toString(): string {
return `<Keymap id="${this.id}" name="${this.name}">`; return `<Keymap abbreviation="${this.abbreviation}" name="${this.name}">`;
} }
} }

View File

@@ -88,10 +88,10 @@ export class UhkConfiguration extends Serializable<UhkConfiguration> {
return `<UhkConfiguration signature="${this.signature}">`; return `<UhkConfiguration signature="${this.signature}">`;
} }
getKeymap(keymapId: number): Keymap { getKeymap(keymapAbbreviation: string): Keymap {
let keymaps: Keymap[] = this.keymaps.elements; let keymaps: Keymap[] = this.keymaps.elements;
for (let i = 0; i < keymaps.length; ++i) { for (let i = 0; i < keymaps.length; ++i) {
if (keymapId === keymaps[i].id) { if (keymapAbbreviation === keymaps[i].abbreviation) {
return keymaps[i]; return keymaps[i];
} }
} }

View File

@@ -1,45 +1,43 @@
import { assertUInt8 } from '../../assert';
import { UhkBuffer } from '../../UhkBuffer'; import { UhkBuffer } from '../../UhkBuffer';
import { KeyAction, KeyActionId, keyActionType } from './KeyAction'; import { KeyAction, KeyActionId, keyActionType } from './KeyAction';
export class SwitchKeymapAction extends KeyAction { export class SwitchKeymapAction extends KeyAction {
@assertUInt8 keymapAbbreviation: string;
keymapId: number;
constructor(other?: SwitchKeymapAction) { constructor(other?: SwitchKeymapAction) {
super(); super();
if (!other) { if (!other) {
return; return;
} }
this.keymapId = other.keymapId; this.keymapAbbreviation = other.keymapAbbreviation;
} }
_fromJsObject(jsObject: any): SwitchKeymapAction { _fromJsObject(jsObject: any): SwitchKeymapAction {
this.assertKeyActionType(jsObject); this.assertKeyActionType(jsObject);
this.keymapId = jsObject.keymapId; this.keymapAbbreviation = jsObject.keymapAbbreviation;
return this; return this;
} }
_fromBinary(buffer: UhkBuffer): SwitchKeymapAction { _fromBinary(buffer: UhkBuffer): SwitchKeymapAction {
this.readAndAssertKeyActionId(buffer); this.readAndAssertKeyActionId(buffer);
this.keymapId = buffer.readUInt8(); this.keymapAbbreviation = buffer.readString();
return this; return this;
} }
_toJsObject(): any { _toJsObject(): any {
return { return {
keyActionType: keyActionType.SwitchKeymapAction, keyActionType: keyActionType.SwitchKeymapAction,
keymapId: this.keymapId keymapAbbreviation: this.keymapAbbreviation
}; };
} }
_toBinary(buffer: UhkBuffer) { _toBinary(buffer: UhkBuffer) {
buffer.writeUInt8(KeyActionId.SwitchKeymapAction); buffer.writeUInt8(KeyActionId.SwitchKeymapAction);
buffer.writeUInt8(this.keymapId); buffer.writeString(this.keymapAbbreviation);
} }
toString(): string { toString(): string {
return `<SwitchKeymapAction keymapId="${this.keymapId}">`; return `<SwitchKeymapAction keymapAbbreviation="${this.keymapAbbreviation}">`;
} }
} }

View File

@@ -1,8 +1,7 @@
{ {
"keymaps": [ "keymaps": [
{ {
"id": 0, "isDefault": false,
"isDefault": true,
"abbreviation": "QTY", "abbreviation": "QTY",
"name": "QWERTY", "name": "QWERTY",
"description": "Maecenas sem dui, ullamcorper consequat pellentesque ut, mattis at velit. Duis scelerisque eleifend gravida. Aenean at mauris rhoncus, dictum mi vitae, semper eros. Quisque maximus est elit, at condimentum ligula consectetur vel. Aenean lorem felis, molestie id ex suscipit, sagittis mollis dui. Phasellus in felis in libero bibendum ornare. Duis vestibulum dolor sed diam tempor vulputate. Curabitur scelerisque pretium ipsum. Phasellus non orci vestibulum, vehicula lectus sit amet, lacinia velit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In diam lacus, cursus at pretium vel, ullamcorper at ante.", "description": "Maecenas sem dui, ullamcorper consequat pellentesque ut, mattis at velit. Duis scelerisque eleifend gravida. Aenean at mauris rhoncus, dictum mi vitae, semper eros. Quisque maximus est elit, at condimentum ligula consectetur vel. Aenean lorem felis, molestie id ex suscipit, sagittis mollis dui. Phasellus in felis in libero bibendum ornare. Duis vestibulum dolor sed diam tempor vulputate. Curabitur scelerisque pretium ipsum. Phasellus non orci vestibulum, vehicula lectus sit amet, lacinia velit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In diam lacus, cursus at pretium vel, ullamcorper at ante.",
@@ -381,7 +380,7 @@
}, },
{ {
"keyActionType": "switchKeymap", "keyActionType": "switchKeymap",
"keymapId": 2 "keymapAbbreviation": "VIM"
}, },
{ {
"keyActionType": "none" "keyActionType": "none"
@@ -791,7 +790,6 @@
] ]
}, },
{ {
"id": 1,
"isDefault": false, "isDefault": false,
"abbreviation": "VIM", "abbreviation": "VIM",
"name": "VIM", "name": "VIM",
@@ -813,7 +811,7 @@
}, },
{ {
"keyActionType": "switchKeymap", "keyActionType": "switchKeymap",
"keymapId": 1 "keymapAbbreviation": "QTY"
} }
] ]
}, },
@@ -879,7 +877,6 @@
] ]
}, },
{ {
"id": 2,
"isDefault": false, "isDefault": false,
"abbreviation": "DVR", "abbreviation": "DVR",
"name": "DVR", "name": "DVR",

View File

@@ -14,7 +14,6 @@
], ],
"keymaps": [ "keymaps": [
{ {
"id": 0,
"isDefault": true, "isDefault": true,
"abbreviation": "QTY", "abbreviation": "QTY",
"name": "QWERTY", "name": "QWERTY",
@@ -394,7 +393,7 @@
}, },
{ {
"keyActionType": "switchKeymap", "keyActionType": "switchKeymap",
"keymapId": 2 "keymapAbbreviation": "VIM"
}, },
{ {
"keyActionType": "none" "keyActionType": "none"
@@ -809,7 +808,6 @@
] ]
}, },
{ {
"id": 1,
"isDefault": false, "isDefault": false,
"abbreviation": "VIM", "abbreviation": "VIM",
"name": "VIM", "name": "VIM",
@@ -831,7 +829,7 @@
}, },
{ {
"keyActionType": "switchKeymap", "keyActionType": "switchKeymap",
"keymapId": 1 "keymapAbbreviation": "QTY"
} }
] ]
}, },
@@ -912,7 +910,6 @@
] ]
}, },
{ {
"id": 2,
"isDefault": false, "isDefault": false,
"abbreviation": "DVR", "abbreviation": "DVR",
"name": "DVR", "name": "DVR",

View File

@@ -27,7 +27,6 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
<script src="vendor/bootstrap/js/bootstrap.min.js"></script> <!-- TODO remove in near future --> <script src="vendor/bootstrap/js/bootstrap.min.js"></script> <!-- TODO remove in near future -->
<script src="vendor/sortablejs/Sortable.min.js"></script> <script src="vendor/sortablejs/Sortable.min.js"></script>
<script src="vendor/select2/js/select2.min.js"></script> <script src="vendor/select2/js/select2.min.js"></script>
<script src="app.js"></script> <!-- TODO move to typescript and components -->
<script src="uhk.js"></script> <script src="uhk.js"></script>
</body> </body>
</html> </html>

View File

@@ -6,3 +6,4 @@
<div class="github-fork-ribbon"> <div class="github-fork-ribbon">
<a class="" href="https://github.com/UltimateHackingKeyboard/agent" title="Fork me on GitHub">Fork me on GitHub</a> <a class="" href="https://github.com/UltimateHackingKeyboard/agent" title="Fork me on GitHub">Fork me on GitHub</a>
</div> </div>
<ngrx-store-log-monitor toggleCommand="alt-t"></ngrx-store-log-monitor>

View File

@@ -12,7 +12,7 @@ export class DataProviderService {
} }
getDefaultKeymaps(): any { getDefaultKeymaps(): any {
return require('json!../config-serializer/default-keymaps.json'); return require('json!../config-serializer/preset-keymaps.json');
} }
getKeyboardSvgAttributes(): { viewBox: string, transform: string, fill: string } { getKeyboardSvgAttributes(): { viewBox: string, transform: string, fill: string } {

View File

@@ -0,0 +1,2 @@
export * from './keymap';
export * from './macro';

View File

@@ -0,0 +1,69 @@
import { Action } from '@ngrx/store';
import { Keymap } from '../../config-serializer/config-items/Keymap';
export namespace KeymapActions {
export const PREFIX = '[Keymap] ';
export const ADD = KeymapActions.PREFIX + 'Add keymap';
export const DUPLICATE = KeymapActions.PREFIX + 'Duplicate keymap';
export const EDIT_ABBR = KeymapActions.PREFIX + 'Edit keymap abbreviation';
export const EDIT_NAME = KeymapActions.PREFIX + 'Edit keymap title';
export const SAVE_KEY = KeymapActions.PREFIX + 'Save key action';
export const SET_DEFAULT = KeymapActions.PREFIX + 'Set default option';
export const REMOVE = KeymapActions.PREFIX + 'Remove keymap';
export function addKeymap(item: Keymap): Action {
return {
type: KeymapActions.ADD,
payload: item
};
}
export function setDefault(abbr: string): Action {
return {
type: KeymapActions.SET_DEFAULT,
payload: abbr
};
}
export function removeKeymap(abbr: string): Action {
return {
type: KeymapActions.REMOVE,
payload: abbr
};
}
export function duplicateKeymap(keymap: Keymap): Action {
return {
type: KeymapActions.DUPLICATE,
payload: keymap
};
}
export function editKeymapName(abbr: string, name: string): Action {
return {
type: KeymapActions.EDIT_NAME,
payload: {
abbr: abbr,
name: name
}
};
}
export function editKeymapAbbr(abbr: string, newAbbr: string): Action {
return {
type: KeymapActions.EDIT_ABBR,
payload: {
abbr: abbr,
newAbbr: newAbbr
}
};
}
export function saveKey(keymap: Keymap): Action {
return {
type: KeymapActions.SAVE_KEY,
payload: keymap
};
}
}

View File

@@ -0,0 +1,12 @@
import { Action } from '@ngrx/store';
export namespace MacroActions {
export const PREFIX = '[Macro] ';
export const GET_ALL = MacroActions.PREFIX + 'Get all macros';
export function getAll(): Action {
return {
type: MacroActions.GET_ALL
};
}
}

View File

@@ -0,0 +1,32 @@
import { Injectable } from '@angular/core';
import { Actions, Effect } from '@ngrx/effects';
import 'rxjs/add/operator/map';
import { KeymapActions } from '../actions/keymap';
@Injectable()
export class KeymapEffects {
@Effect()remove$: any = this.actions$
.ofType(KeymapActions.REMOVE)
.map(() => {
// TODO: Waiting for the fix: https://github.com/angular/angular/issues/10770
// If state is empty router.navigate(['/keymap']);
// Else router.navigate(['/keymap']);
});
@Effect() editAbbr$: any = this.actions$
.ofType(KeymapActions.EDIT_ABBR)
.map<string>(action => action.payload.abbr)
.map((abbr: string) => {
console.log(abbr);
// TODO: Waiting for the fix: https://github.com/angular/angular/issues/10770
// // router.navigate(['/keymap', abbr]);
});
constructor(
private actions$: Actions
) {}
}

9
src/store/index.ts Normal file
View File

@@ -0,0 +1,9 @@
import { Keymap } from '../config-serializer/config-items/Keymap';
import { Macro } from '../config-serializer/config-items/Macro';
// State interface for the application
export interface AppState {
keymaps: Keymap[];
macros: Macro[];
presetKeymaps: Keymap[];
}

View File

@@ -0,0 +1,5 @@
import keymapReducer from './keymap';
import macroReducer from './macro';
import presetReducer from './preset';
export { keymapReducer, macroReducer, presetReducer };

View File

@@ -0,0 +1,134 @@
import '@ngrx/core/add/operator/select';
import { Action } from '@ngrx/store';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';
import { Keymap } from '../../config-serializer/config-items/Keymap';
import { KeymapActions } from '../actions';
import { AppState } from '../index';
const initialState: Keymap[] = [];
export default function(state = initialState, action: Action): Keymap[] {
switch (action.type) {
case KeymapActions.ADD:
case KeymapActions.DUPLICATE:
let newKeymap: Keymap = new Keymap(action.payload);
newKeymap.abbreviation = generateAbbr(state, newKeymap.abbreviation);
newKeymap.name = generateName(state, newKeymap.name);
newKeymap.isDefault = false;
return [...state, newKeymap];
case KeymapActions.EDIT_NAME:
let name: string = generateName(state, action.payload.name);
return state.map((keymap: Keymap) => {
if (keymap.abbreviation === action.payload.abbr) {
keymap.name = name;
}
return keymap;
});
case KeymapActions.EDIT_ABBR:
let abbr: string = generateAbbr(state, action.payload.newAbbr);
return state.map((keymap: Keymap) => {
if (keymap.abbreviation === action.payload.abbr) {
keymap.abbreviation = abbr;
}
return keymap;
});
case KeymapActions.SET_DEFAULT:
return state.map((keymap: Keymap) => {
keymap.isDefault = (keymap.abbreviation === action.payload);
return keymap;
});
case KeymapActions.REMOVE:
let isDefault: boolean;
let filtered: Keymap[] = state.filter((keymap: Keymap) => {
if (keymap.abbreviation === action.payload) {
isDefault = keymap.isDefault;
return false;
}
return true;
}
);
// If deleted one is default set default keymap to the first on the list of keymaps
if (isDefault && filtered.length > 0) {
filtered[0].isDefault = true;
}
return filtered;
case KeymapActions.SAVE_KEY:
let changedKeymap: Keymap = new Keymap;
return state.map((keymap: Keymap) => {
if (keymap.abbreviation === action.payload.abbreviation) {
keymap = Object.assign(changedKeymap, action.payload);
}
return keymap;
});
default: {
return state;
}
}
}
export function getKeymap(abbr: string) {
if (abbr === undefined) {
return getDefault();
}
return (state$: Observable<AppState>) => state$
.select(appState => appState.keymaps)
.map((keymaps: Keymap[]) =>
keymaps.find((keymap: Keymap) => keymap.abbreviation === abbr)
);
}
export function getDefault() {
return (state$: Observable<AppState>) => state$
.select(appState => appState.keymaps)
.map((keymaps: Keymap[]) =>
keymaps.find((keymap: Keymap) => keymap.isDefault)
);
}
function generateAbbr(keymaps: Keymap[], abbr: string): string {
const chars: string[] = '23456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
let position = 0;
while (keymaps.some((keymap: Keymap) => keymap.abbreviation === abbr)) {
abbr = abbr.substring(0, abbr.length - 1) + chars[position];
++position;
}
return abbr;
}
function generateName(keymaps: Keymap[], name: string) {
let suffix = 2;
const oldName: string = name;
while (keymaps.some((keymap: Keymap) => keymap.name === name)) {
name = oldName + ` (${suffix})`;
++suffix;
}
return name;
}

View File

@@ -0,0 +1,16 @@
import { Action } from '@ngrx/store';
import { Macro } from '../../config-serializer/config-items/Macro';
import { MacroActions } from '../actions';
const initialState: Macro[] = [];
export default function(state = initialState, action: Action): Macro[] {
switch (action.type) {
case MacroActions.GET_ALL:
break;
default: {
return state;
}
}
}

View File

@@ -0,0 +1,7 @@
import { Keymap } from '../../config-serializer/config-items/Keymap';
const initialState: Keymap[] = [];
export default function(state = initialState): Keymap[] {
return state;
}

View File

@@ -0,0 +1,10 @@
export class Electron {
initialState() {
// TODO implement load logic
}
/* tslint:disable:no-unused-variable */
saveSate(reducer: any): any {
// TODO implement save logic
}
}

View File

@@ -0,0 +1,37 @@
import { Injectable } from '@angular/core';
import { Action } from '@ngrx/store';
import { AppState } from '../index';
import { Electron } from './electron';
import { Local } from './local';
@Injectable()
export class DataStorage {
private _environment: Local | Electron;
constructor() {
this.detectEnvironment();
}
initialState() {
return this._environment.initialState();
}
detectEnvironment() {
// Electron
// TODO check if we can remove <any> when electron will be implemented (maybe use process.versions['electron'])
if (typeof window !== 'undefined' && (<any>window).process && (<any>window).process.type === 'renderer') {
this._environment = new Electron();
}
// Local storage
else {
this._environment = new Local();
}
}
saveSate(reducer: any): (state: AppState, action: Action) => AppState {
return this._environment.saveSate(reducer);
}
}

View File

@@ -0,0 +1,67 @@
import { Action } from '@ngrx/store';
import { Keymap } from '../../config-serializer/config-items/Keymap';
import { Keymaps } from '../../config-serializer/config-items/Keymaps';
import { Macro } from '../../config-serializer/config-items/Macro';
import { UhkConfiguration } from '../../config-serializer/config-items/UhkConfiguration';
import { KeymapActions, MacroActions } from '../actions';
import { AppState } from '../index';
export class Local {
initialState(): AppState {
let config: UhkConfiguration;
let presetAll: Keymaps;
// Load data from json
if (!localStorage.getItem('config')) {
const jsonUser: JSON = require('json!../../config-serializer/uhk-config.json');
const jsonPreset: any = require('json!../../config-serializer/preset-keymaps.json');
config = new UhkConfiguration().fromJsObject(jsonUser);
presetAll = new Keymaps().fromJsObject(jsonPreset.keymaps);
// Save to local storage
localStorage.setItem('config', JSON.stringify(config.toJsObject()));
localStorage.setItem('preset', JSON.stringify(presetAll.toJsObject()));
}
// Load data from local storage
else {
config = new UhkConfiguration().fromJsObject(
JSON.parse(localStorage.getItem('config'))
);
presetAll = new Keymaps().fromJsObject(
JSON.parse(localStorage.getItem('preset'))
);
}
return {
keymaps: config.keymaps.elements,
macros: config.macros.elements,
presetKeymaps: presetAll.elements
};
}
saveSate(reducer: any): (state: any, action: Action) => AppState {
return function (state: any, action: Action) {
let nextState = reducer(state, action);
let config: UhkConfiguration;
// Save elements to the UhkConfiguration
if (action.type.startsWith(KeymapActions.PREFIX) && state.length && state[0] instanceof Keymap) {
config = new UhkConfiguration().fromJsObject(
JSON.parse(localStorage.getItem('config'))
);
config.keymaps.elements = Object.values(nextState);
localStorage.setItem('config', JSON.stringify(config.toJsObject()));
} else if (action.type.startsWith(MacroActions.PREFIX) && state.length && state[0] instanceof Macro) {
config = new UhkConfiguration().fromJsObject(
JSON.parse(localStorage.getItem('config'))
);
config.macros.elements = Object.values(nextState);
localStorage.setItem('config', JSON.stringify(config.toJsObject()));
}
return nextState;
};
}
}