feat(device): Add 'Save to keyboard' button (#402)

* feat(device): Add 'Save to keyboard' button

Created a 'Progress Button' that have 2 state in progress or not.
Able to set different text for different state:
- baseText for normal state
- progressText for in progress state
close: #377

* fix 'Save to keyboard' button visibility in web version

* remove success notification when save to keyboard success

* feat(notifier): Turn off auto hide of the notifier

* feat(device): Show saved state of 'Save to keyboard button'

* style: Format import in app.component.ts

* feat(device): Auto hide 'Save to Keyboard' button

* fix(device): Fix saving animation

* fix(device): Fix saving animation

* fix(device): Fix tslint
This commit is contained in:
Róbert Kiss
2017-09-10 23:22:54 +00:00
committed by László Monda
parent c135aed7c9
commit 8d7269a998
16 changed files with 244 additions and 69 deletions
@@ -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;
@@ -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<Action> = this.actions$
.ofType(ActionTypes.CONNECTION_STATE_CHANGED)
.map(toPayload)
@@ -28,7 +41,7 @@ export class DeviceEffects {
}
});
@Effect({ dispatch: false })
@Effect({dispatch: false})
permissionStateChange$: Observable<Action> = this.actions$
.ofType(ActionTypes.PERMISSION_STATE_CHANGED)
.map(toPayload)
@@ -41,7 +54,7 @@ export class DeviceEffects {
}
});
@Effect({ dispatch: false })
@Effect({dispatch: false})
setPrivilegeOnLinux$: Observable<Action> = this.actions$
.ofType(ActionTypes.SET_PRIVILEGE_ON_LINUX)
.do(() => {
@@ -67,35 +80,52 @@ export class DeviceEffects {
];
});
@Effect({ dispatch: false })
@Effect({dispatch: false})
saveConfiguration$: Observable<Action> = 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<Action> = 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<Action> = 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<AppState>) {
}
private sendUserConfigToKeyboard(userConfiguration: UserConfiguration): void {
const uhkBuffer = new UhkBuffer();
userConfiguration.toBinary(uhkBuffer);
this.deviceRendererService.saveUserConfiguration(uhkBuffer.getBufferContent());
}
}
@@ -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<Action> = this.actions$
+6 -1
View File
@@ -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;
});
@@ -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;
@@ -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
@@ -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
};