diff --git a/packages/uhk-agent/src/services/device.service.ts b/packages/uhk-agent/src/services/device.service.ts
index 33217fe5..f1a43422 100644
--- a/packages/uhk-agent/src/services/device.service.ts
+++ b/packages/uhk-agent/src/services/device.service.ts
@@ -92,7 +92,7 @@ export class DeviceService {
const applyTransferData = this.getTransferData(applyBuffer);
this.logService.debug('Fragment: ', JSON.stringify(applyTransferData));
device.write(applyTransferData);
-
+ device.close();
response.success = true;
this.logService.info('transferring finished');
}
diff --git a/packages/uhk-web/src/app/app.component.html b/packages/uhk-web/src/app/app.component.html
index 7bc22327..965f6c80 100644
--- a/packages/uhk-web/src/app/app.component.html
+++ b/packages/uhk-web/src/app/app.component.html
@@ -11,3 +11,8 @@
Fork me on GitHub
+
diff --git a/packages/uhk-web/src/app/app.component.scss b/packages/uhk-web/src/app/app.component.scss
index 230b8b00..8715ffe3 100644
--- a/packages/uhk-web/src/app/app.component.scss
+++ b/packages/uhk-web/src/app/app.component.scss
@@ -37,3 +37,9 @@ main-app {
display: block;
position: relative;
}
+
+.save-to-keyboard-button {
+ position: fixed;
+ bottom: 15px;
+ right: 15px;
+}
diff --git a/packages/uhk-web/src/app/app.component.ts b/packages/uhk-web/src/app/app.component.ts
index 32bddab7..04fdeeff 100644
--- a/packages/uhk-web/src/app/app.component.ts
+++ b/packages/uhk-web/src/app/app.component.ts
@@ -1,33 +1,52 @@
import { Component, HostListener, ViewEncapsulation } from '@angular/core';
+import { animate, style, transition, trigger } from '@angular/animations';
import { Observable } from 'rxjs/Observable';
-import { Store } from '@ngrx/store';
+import { Action, Store } from '@ngrx/store';
+
+import 'rxjs/add/operator/last';
import { DoNotUpdateAppAction, UpdateAppAction } from './store/actions/app-update.action';
import {
AppState,
getShowAppUpdateAvailable,
deviceConnected,
- runningInElectron
+ runningInElectron,
+ saveToKeyboardState
} from './store';
import { getUserConfiguration } from './store/reducers/user-configuration';
import { UhkBuffer } from './config-serializer/uhk-buffer';
-import { SaveConfigurationAction } from './store/actions/device';
+import { ProgressButtonState } from './store/reducers/progress-button-state';
@Component({
selector: 'main-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
- encapsulation: ViewEncapsulation.None
+ encapsulation: ViewEncapsulation.None,
+ animations: [
+ trigger(
+ 'showSaveToKeyboardButton', [
+ transition(':enter', [
+ style({transform: 'translateY(100%)'}),
+ animate('400ms ease-in-out', style({transform: 'translateY(0)'}))
+ ]),
+ transition(':leave', [
+ style({transform: 'translateY(0)'}),
+ animate('400ms ease-in-out', style({transform: 'translateY(100%)'}))
+ ])
+ ])
+ ]
})
export class MainAppComponent {
showUpdateAvailable$: Observable;
deviceConnected$: Observable;
runningInElectron$: Observable;
+ saveToKeyboardState$: Observable;
constructor(private store: Store) {
this.showUpdateAvailable$ = store.select(getShowAppUpdateAvailable);
this.deviceConnected$ = store.select(deviceConnected);
this.runningInElectron$ = store.select(runningInElectron);
+ this.saveToKeyboardState$ = store.select(saveToKeyboardState);
}
updateApp() {
@@ -38,12 +57,8 @@ export class MainAppComponent {
this.store.dispatch(new DoNotUpdateAppAction());
}
- @HostListener('window:keydown.control.o', ['$event'])
- onCtrlO(event: KeyboardEvent): void {
- console.log('ctrl + o pressed');
- event.preventDefault();
- event.stopPropagation();
- this.sendUserConfiguration();
+ clickedOnProgressButton(action: Action) {
+ return this.store.dispatch(action);
}
@HostListener('window:keydown.alt.j', ['$event'])
@@ -55,7 +70,7 @@ export class MainAppComponent {
.first()
.subscribe(userConfiguration => {
const asString = JSON.stringify(userConfiguration.toJsonObject());
- const asBlob = new Blob([asString], { type: 'text/plain' });
+ const asBlob = new Blob([asString], {type: 'text/plain'});
saveAs(asBlob, 'UserConfiguration.json');
});
}
@@ -74,20 +89,4 @@ export class MainAppComponent {
})
.subscribe(blob => saveAs(blob, 'UserConfiguration.bin'));
}
-
- private sendUserConfiguration(): void {
- this.store
- .let(getUserConfiguration())
- .first()
- .map(userConfiguration => {
- const uhkBuffer = new UhkBuffer();
- userConfiguration.toBinary(uhkBuffer);
- return uhkBuffer.getBufferContent();
- })
- .subscribe(
- buffer => this.store.dispatch(new SaveConfigurationAction(buffer)),
- error => console.error('Error during uploading user configuration', error),
- () => console.log('User configuration has been successfully uploaded')
- );
- }
}
diff --git a/packages/uhk-web/src/app/components/progress-button/progress-button.component.html b/packages/uhk-web/src/app/components/progress-button/progress-button.component.html
new file mode 100644
index 00000000..24b5ac9a
--- /dev/null
+++ b/packages/uhk-web/src/app/components/progress-button/progress-button.component.html
@@ -0,0 +1,5 @@
+
diff --git a/packages/uhk-web/src/app/components/progress-button/progress-button.component.scss b/packages/uhk-web/src/app/components/progress-button/progress-button.component.scss
new file mode 100644
index 00000000..d45efbef
--- /dev/null
+++ b/packages/uhk-web/src/app/components/progress-button/progress-button.component.scss
@@ -0,0 +1,3 @@
+button {
+ min-width: 150px;
+}
diff --git a/packages/uhk-web/src/app/components/progress-button/progress-button.component.ts b/packages/uhk-web/src/app/components/progress-button/progress-button.component.ts
new file mode 100644
index 00000000..d637c624
--- /dev/null
+++ b/packages/uhk-web/src/app/components/progress-button/progress-button.component.ts
@@ -0,0 +1,19 @@
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
+import { Action } from '@ngrx/store';
+
+import { ProgressButtonState, initProgressButtonState } from '../../store/reducers/progress-button-state';
+
+@Component({
+ selector: 'progress-button',
+ templateUrl: './progress-button.component.html',
+ styleUrls: ['./progress-button.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ProgressButtonComponent {
+ @Input() state: ProgressButtonState = initProgressButtonState;
+ @Output() clicked: EventEmitter = new EventEmitter();
+
+ onClicked() {
+ this.clicked.emit(this.state.action);
+ }
+}
diff --git a/packages/uhk-web/src/app/models/angular-notifier-config.ts b/packages/uhk-web/src/app/models/angular-notifier-config.ts
index e89a7019..4df803b2 100644
--- a/packages/uhk-web/src/app/models/angular-notifier-config.ts
+++ b/packages/uhk-web/src/app/models/angular-notifier-config.ts
@@ -1,6 +1,9 @@
import { NotifierOptions } from 'angular-notifier';
export const angularNotifierConfig: NotifierOptions = {
+ behaviour: {
+ autoHide: false
+ },
position: {
horizontal: {
diff --git a/packages/uhk-web/src/app/store/actions/device.ts b/packages/uhk-web/src/app/store/actions/device.ts
index 9a0c3925..2beed482 100644
--- a/packages/uhk-web/src/app/store/actions/device.ts
+++ b/packages/uhk-web/src/app/store/actions/device.ts
@@ -10,7 +10,12 @@ export const ActionTypes = {
CONNECTION_STATE_CHANGED: type(PREFIX + 'connection state changed'),
PERMISSION_STATE_CHANGED: type(PREFIX + 'permission state changed'),
SAVE_CONFIGURATION: type(PREFIX + 'save configuration'),
- SAVE_CONFIGURATION_REPLY: type(PREFIX + 'save configuration reply')
+ SAVE_CONFIGURATION_REPLY: type(PREFIX + 'save configuration reply'),
+ SAVING_CONFIGURATION: type(PREFIX + 'saving configuration'),
+ SHOW_SAVE_TO_KEYBOARD_BUTTON: type(PREFIX + 'show save to keyboard button'),
+ SAVE_TO_KEYBOARD_SUCCESS: type(PREFIX + 'save to keyboard success'),
+ SAVE_TO_KEYBOARD_FAILED: type(PREFIX + 'save to keyboard failed'),
+ HIDE_SAVE_TO_KEYBOARD_BUTTON: type(PREFIX + 'hide save to keyboard button')
};
export class SetPrivilegeOnLinuxAction implements Action {
@@ -38,7 +43,7 @@ export class PermissionStateChangedAction implements Action {
export class SaveConfigurationAction implements Action {
type = ActionTypes.SAVE_CONFIGURATION;
- constructor(public payload: Buffer) {}
+ constructor() {}
}
export class SaveConfigurationReplyAction implements Action {
@@ -47,6 +52,30 @@ export class SaveConfigurationReplyAction implements Action {
constructor(public payload: IpcResponse) {}
}
+export class ShowSaveToKeyboardButtonAction implements Action {
+ type = ActionTypes.SHOW_SAVE_TO_KEYBOARD_BUTTON;
+}
+
+export class SaveToKeyboardSuccessAction implements Action {
+ type = ActionTypes.SAVE_TO_KEYBOARD_SUCCESS;
+}
+
+export class SaveToKeyboardSuccessFailed implements Action {
+ type = ActionTypes.SAVE_TO_KEYBOARD_FAILED;
+}
+
+export class HideSaveToKeyboardButton implements Action {
+ type = ActionTypes.HIDE_SAVE_TO_KEYBOARD_BUTTON;
+}
+
export type Actions
= SetPrivilegeOnLinuxAction
- | ConnectionStateChangedAction;
+ | SetPrivilegeOnLinuxReplyAction
+ | ConnectionStateChangedAction
+ | PermissionStateChangedAction
+ | ShowSaveToKeyboardButtonAction
+ | SaveConfigurationAction
+ | SaveConfigurationReplyAction
+ | SaveToKeyboardSuccessAction
+ | SaveToKeyboardSuccessFailed
+ | HideSaveToKeyboardButton;
diff --git a/packages/uhk-web/src/app/store/effects/device.ts b/packages/uhk-web/src/app/store/effects/device.ts
index 39509403..bdbbf20f 100644
--- a/packages/uhk-web/src/app/store/effects/device.ts
+++ b/packages/uhk-web/src/app/store/effects/device.ts
@@ -1,21 +1,34 @@
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
-import { Action } from '@ngrx/store';
+import { Action, Store } from '@ngrx/store';
import { Actions, Effect, toPayload } from '@ngrx/effects';
import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/observable/of';
+import 'rxjs/add/observable/empty';
+import 'rxjs/add/observable/timer';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
+import 'rxjs/add/operator/withLatestFrom';
import { NotificationType, IpcResponse } from 'uhk-common';
-import { ActionTypes, ConnectionStateChangedAction, PermissionStateChangedAction } from '../actions/device';
+import {
+ ActionTypes,
+ ConnectionStateChangedAction, HideSaveToKeyboardButton,
+ PermissionStateChangedAction,
+ SaveToKeyboardSuccessAction,
+ SaveToKeyboardSuccessFailed
+} from '../actions/device';
import { DeviceRendererService } from '../../services/device-renderer.service';
import { ShowNotificationAction } from '../actions/app';
+import { AppState } from '../index';
+import { UserConfiguration } from '../../config-serializer/config-items/user-configuration';
+import { UhkBuffer } from '../../config-serializer/uhk-buffer';
@Injectable()
export class DeviceEffects {
- @Effect({ dispatch: false })
+ @Effect({dispatch: false})
deviceConnectionStateChange$: Observable = this.actions$
.ofType(ActionTypes.CONNECTION_STATE_CHANGED)
.map(toPayload)
@@ -28,7 +41,7 @@ export class DeviceEffects {
}
});
- @Effect({ dispatch: false })
+ @Effect({dispatch: false})
permissionStateChange$: Observable = this.actions$
.ofType(ActionTypes.PERMISSION_STATE_CHANGED)
.map(toPayload)
@@ -41,7 +54,7 @@ export class DeviceEffects {
}
});
- @Effect({ dispatch: false })
+ @Effect({dispatch: false})
setPrivilegeOnLinux$: Observable = this.actions$
.ofType(ActionTypes.SET_PRIVILEGE_ON_LINUX)
.do(() => {
@@ -67,35 +80,52 @@ export class DeviceEffects {
];
});
- @Effect({ dispatch: false })
+ @Effect({dispatch: false})
saveConfiguration$: Observable = this.actions$
.ofType(ActionTypes.SAVE_CONFIGURATION)
- .map(toPayload)
- .do((buffer: Buffer) => {
- this.deviceRendererService.saveUserConfiguration(buffer);
- });
+ .withLatestFrom(this.store)
+ .map(([action, state]) => state.userConfiguration)
+ .do((userConfiguration: UserConfiguration) => {
+ setTimeout(() => this.sendUserConfigToKeyboard(userConfiguration), 100);
+ })
+ .switchMap(() => Observable.empty());
@Effect()
saveConfigurationReply$: Observable = this.actions$
.ofType(ActionTypes.SAVE_CONFIGURATION_REPLY)
.map(toPayload)
- .map((response: IpcResponse) => {
+ .mergeMap((response: IpcResponse) => {
if (response.success) {
- return new ShowNotificationAction({
- type: NotificationType.Success,
- message: 'Save configuration successful.'
- });
+ return [
+ new SaveToKeyboardSuccessAction()
+ ];
}
- return new ShowNotificationAction({
- type: NotificationType.Error,
- message: response.error.message
- });
+ return [
+ new ShowNotificationAction({
+ type: NotificationType.Error,
+ message: response.error.message
+ }),
+ new SaveToKeyboardSuccessFailed()
+ ];
});
+ @Effect()
+ autoHideSaveToKeyboardButton$: Observable = this.actions$
+ .ofType(ActionTypes.SAVE_TO_KEYBOARD_SUCCESS)
+ .switchMap(() => Observable.timer(1000)
+ .switchMap(() => Observable.of(new HideSaveToKeyboardButton()))
+ );
+
constructor(private actions$: Actions,
private router: Router,
- private deviceRendererService: DeviceRendererService) {
+ private deviceRendererService: DeviceRendererService,
+ private store: Store) {
}
+ private sendUserConfigToKeyboard(userConfiguration: UserConfiguration): void {
+ const uhkBuffer = new UhkBuffer();
+ userConfiguration.toBinary(uhkBuffer);
+ this.deviceRendererService.saveUserConfiguration(uhkBuffer.getBufferContent());
+ }
}
diff --git a/packages/uhk-web/src/app/store/effects/user-config.ts b/packages/uhk-web/src/app/store/effects/user-config.ts
index 1e7fc203..8f29392a 100644
--- a/packages/uhk-web/src/app/store/effects/user-config.ts
+++ b/packages/uhk-web/src/app/store/effects/user-config.ts
@@ -28,6 +28,7 @@ import { KeymapActions } from '../actions/keymap';
import { MacroActions } from '../actions/macro';
import { UndoUserConfigData } from '../../models/undo-user-config-data';
import { ShowNotificationAction, DismissUndoNotificationAction } from '../actions/app';
+import { ShowSaveToKeyboardButtonAction } from '../actions/device';
@Injectable()
export class UserConfigEffects {
@@ -64,11 +65,16 @@ export class UserConfigEffects {
payload,
type: KeymapActions.UNDO_LAST_ACTION
}
- })
+ }),
+ new ShowSaveToKeyboardButtonAction()
];
}
- return [new SaveUserConfigSuccessAction(config), new DismissUndoNotificationAction()];
+ return [
+ new SaveUserConfigSuccessAction(config),
+ new DismissUndoNotificationAction(),
+ new ShowSaveToKeyboardButtonAction()
+ ];
});
@Effect() undoUserConfig$: Observable = this.actions$
diff --git a/packages/uhk-web/src/app/store/index.ts b/packages/uhk-web/src/app/store/index.ts
index 8411b272..cbcc77a4 100644
--- a/packages/uhk-web/src/app/store/index.ts
+++ b/packages/uhk-web/src/app/store/index.ts
@@ -12,6 +12,7 @@ import * as fromAppUpdate from './reducers/app-update.reducer';
import * as autoUpdateSettings from './reducers/auto-update-settings';
import * as fromApp from './reducers/app.reducer';
import * as fromDevice from './reducers/device';
+import { initProgressButtonState } from './reducers/progress-button-state';
export const reducers = {
userConfiguration: userConfigurationReducer,
@@ -41,7 +42,7 @@ export function reducer(state: any, action: any) {
// if (isDev) {
// return developmentReducer(state, action);
// } else {
- return productionReducer(state, action);
+ return productionReducer(state, action);
// }
}
@@ -69,3 +70,7 @@ export const devicePermission = createSelector(deviceState, fromDevice.hasDevice
export const hasDevicePermission = createSelector(runningInElectron, devicePermission, (electron, permission) => {
return !electron ? true : permission;
});
+export const saveToKeyboardStateSelector = createSelector(deviceState, fromDevice.getSaveToKeyboardState);
+export const saveToKeyboardState = createSelector(runningInElectron, saveToKeyboardStateSelector, (electron, state) => {
+ return electron ? state : initProgressButtonState;
+});
diff --git a/packages/uhk-web/src/app/store/reducers/device.ts b/packages/uhk-web/src/app/store/reducers/device.ts
index 67a4d36d..adf8bdc5 100644
--- a/packages/uhk-web/src/app/store/reducers/device.ts
+++ b/packages/uhk-web/src/app/store/reducers/device.ts
@@ -1,15 +1,18 @@
import { Action } from '@ngrx/store';
-import { ActionTypes } from '../actions/device';
+import { ActionTypes, HideSaveToKeyboardButton, SaveConfigurationAction } from '../actions/device';
+import { initProgressButtonState, ProgressButtonState } from './progress-button-state';
export interface State {
connected: boolean;
hasPermission: boolean;
+ saveToKeyboard: ProgressButtonState;
}
const initialState: State = {
connected: true,
- hasPermission: true
+ hasPermission: true,
+ saveToKeyboard: initProgressButtonState
};
export function reducer(state = initialState, action: Action) {
@@ -26,6 +29,63 @@ export function reducer(state = initialState, action: Action) {
hasPermission: action.payload
};
+ case ActionTypes.SAVING_CONFIGURATION: {
+ return {
+ ...state,
+ savingToKeyboard: true
+ };
+ }
+
+ case ActionTypes.SHOW_SAVE_TO_KEYBOARD_BUTTON: {
+ return {
+ ...state,
+ saveToKeyboard: {
+ showButton: true,
+ text: 'Save to keyboard',
+ action: new SaveConfigurationAction()
+ }
+ };
+ }
+
+ case ActionTypes.SAVE_CONFIGURATION: {
+ return {
+ ...state,
+ saveToKeyboard: {
+ showButton: true,
+ text: 'Saving',
+ showProgress: true
+ }
+ };
+ }
+
+ case ActionTypes.SAVE_TO_KEYBOARD_SUCCESS: {
+ return {
+ ...state,
+ saveToKeyboard: {
+ showButton: true,
+ text: 'Saved!',
+ action: null
+ }
+ };
+ }
+
+ case ActionTypes.SAVE_TO_KEYBOARD_FAILED: {
+ return {
+ ...state,
+ saveToKeyboard: {
+ showButton: true,
+ text: 'Save to keyboard',
+ action: new SaveConfigurationAction()
+ }
+ };
+ }
+
+ case ActionTypes.HIDE_SAVE_TO_KEYBOARD_BUTTON: {
+ return {
+ ...state,
+ saveToKeyboard: initProgressButtonState
+ };
+ }
default:
return state;
}
@@ -33,3 +93,4 @@ export function reducer(state = initialState, action: Action) {
export const isDeviceConnected = (state: State) => state.connected;
export const hasDevicePermission = (state: State) => state.hasPermission;
+export const getSaveToKeyboardState = (state: State) => state.saveToKeyboard;
diff --git a/packages/uhk-web/src/app/store/reducers/index.ts b/packages/uhk-web/src/app/store/reducers/index.ts
deleted file mode 100644
index 05378611..00000000
--- a/packages/uhk-web/src/app/store/reducers/index.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { routerReducer } from '@ngrx/router-store';
-
-import userConfigurationReducer from './user-configuration';
-import presetReducer from './preset';
-import { reducer as autoUpdateReducer } from './auto-update-settings';
-import { reducer as appReducer } from './app.reducer';
-import * as fromAppUpdate from './app-update.reducer';
-import * as fromDevice from './device';
-
-export { userConfigurationReducer, presetReducer, autoUpdateReducer, appReducer };
-
-// All reducers that are used in application
diff --git a/packages/uhk-web/src/app/store/reducers/progress-button-state.ts b/packages/uhk-web/src/app/store/reducers/progress-button-state.ts
new file mode 100644
index 00000000..2c6a44ba
--- /dev/null
+++ b/packages/uhk-web/src/app/store/reducers/progress-button-state.ts
@@ -0,0 +1,14 @@
+import { Action } from '@ngrx/store';
+
+export interface ProgressButtonState {
+ showButton: boolean;
+ text: string;
+ showProgress: boolean;
+ action?: Action;
+}
+
+export const initProgressButtonState = {
+ showButton: false,
+ text: null,
+ showProgress: false
+};
diff --git a/packages/uhk-web/src/app/web.module.ts b/packages/uhk-web/src/app/web.module.ts
index a38c9816..dfc12731 100644
--- a/packages/uhk-web/src/app/web.module.ts
+++ b/packages/uhk-web/src/app/web.module.ts
@@ -103,6 +103,7 @@ import { MainPage } from './pages/main-page/main.page';
import { DeviceEffects } from './store/effects/device';
import { DeviceRendererService } from './services/device-renderer.service';
import { UhkDeviceInitializedGuard } from './services/uhk-device-initialized.guard';
+import { ProgressButtonComponent } from './components/progress-button/progress-button.component';
@NgModule({
declarations: [
@@ -163,7 +164,8 @@ import { UhkDeviceInitializedGuard } from './services/uhk-device-initialized.gua
UhkMessageComponent,
MissingDeviceComponent,
PrivilegeCheckerComponent,
- MainPage
+ MainPage,
+ ProgressButtonComponent
],
imports: [
BrowserModule,