feat(user-config): Upload user configuration from json/bin file (#545)

* feat(user-config): Upload user configuration from json/bin file

* fix error message

* remove file extension filter

* apply user config after loaded from file

* add file filter

* remove file filter
This commit is contained in:
Róbert Kiss
2018-01-08 05:29:31 +01:00
committed by László Monda
parent b3f2e3451e
commit f0139c55ee
8 changed files with 117 additions and 9 deletions

View File

@@ -14,9 +14,11 @@
<span role="button" class="btn-link" (click)="saveConfigurationInBINFormat()">binary</span> format. <span role="button" class="btn-link" (click)="saveConfigurationInBINFormat()">binary</span> format.
</li> </li>
<li> <li>
<button class="btn btn-default" <label class="btn btn-default btn-file">
>Upload device configuration Upload device configuration
</button> <input type="file"
(change)="changeFile($event)">
</label>
</li> </li>
<li> <li>
<button class="btn btn-danger" <button class="btn btn-danger"

View File

@@ -3,7 +3,11 @@ import { Store } from '@ngrx/store';
import { AppState } from '../../../store'; import { AppState } from '../../../store';
import { ResetUserConfigurationAction } from '../../../store/actions/device'; import { ResetUserConfigurationAction } from '../../../store/actions/device';
import { SaveUserConfigInBinaryFileAction, SaveUserConfigInJsonFileAction } from '../../../store/actions/user-config'; import {
LoadUserConfigurationFromFileAction,
SaveUserConfigInBinaryFileAction,
SaveUserConfigInJsonFileAction
} from '../../../store/actions/user-config';
@Component({ @Component({
selector: 'device-settings', selector: 'device-settings',
@@ -29,4 +33,17 @@ export class DeviceConfigurationComponent {
saveConfigurationInBINFormat() { saveConfigurationInBINFormat() {
this.store.dispatch(new SaveUserConfigInBinaryFileAction()); this.store.dispatch(new SaveUserConfigInBinaryFileAction());
} }
changeFile(event): void {
const files = event.srcElement.files;
const fileReader = new FileReader();
fileReader.onloadend = function () {
const arrayBuffer = new Uint8Array(fileReader.result);
this.store.dispatch(new LoadUserConfigurationFromFileAction({
filename: event.srcElement.value,
data: Array.from(arrayBuffer)
}));
}.bind(this);
fileReader.readAsArrayBuffer(files[0]);
}
} }

View File

@@ -0,0 +1,4 @@
export interface UploadFileData {
filename: string;
data: Array<number>;
}

View File

@@ -1,6 +1,7 @@
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { type, UserConfiguration, ConfigurationReply } from 'uhk-common'; import { type, UserConfiguration, ConfigurationReply } from 'uhk-common';
import { UserConfigurationValue } from '../../models/user-configuration-value'; import { UserConfigurationValue } from '../../models/user-configuration-value';
import { UploadFileData } from '../../models/upload-file-data';
const PREFIX = '[user-config] '; const PREFIX = '[user-config] ';
@@ -15,7 +16,9 @@ export const ActionTypes = {
SAVE_USER_CONFIG_IN_BIN_FILE: type(PREFIX + 'Save User Config in binary file'), SAVE_USER_CONFIG_IN_BIN_FILE: type(PREFIX + 'Save User Config in binary file'),
LOAD_RESET_USER_CONFIGURATION: type(PREFIX + 'Load reset user configuration'), LOAD_RESET_USER_CONFIGURATION: type(PREFIX + 'Load reset user configuration'),
RENAME_USER_CONFIGURATION: type(PREFIX + 'Rename user configuration'), RENAME_USER_CONFIGURATION: type(PREFIX + 'Rename user configuration'),
SET_USER_CONFIGURATION_VALUE: type(PREFIX + 'Set user configuration value') SET_USER_CONFIGURATION_VALUE: type(PREFIX + 'Set user configuration value'),
LOAD_USER_CONFIGURATION_FROM_FILE: type(PREFIX + 'Load user configuration from file'),
APPLY_USER_CONFIGURATION_FROM_FILE: type(PREFIX + 'Apply user configuration from file')
}; };
export class LoadUserConfigAction implements Action { export class LoadUserConfigAction implements Action {
@@ -76,6 +79,20 @@ export class SetUserConfigurationValueAction implements Action {
} }
} }
export class LoadUserConfigurationFromFileAction implements Action {
type = ActionTypes.LOAD_USER_CONFIGURATION_FROM_FILE;
constructor(public payload: UploadFileData) {
}
}
export class ApplyUserConfigurationFromFileAction implements Action {
type = ActionTypes.APPLY_USER_CONFIGURATION_FROM_FILE;
constructor(public payload: UserConfiguration) {
}
}
export type Actions export type Actions
= LoadUserConfigAction = LoadUserConfigAction
| LoadUserConfigSuccessAction | LoadUserConfigSuccessAction
@@ -87,4 +104,6 @@ export type Actions
| LoadResetUserConfigurationAction | LoadResetUserConfigurationAction
| RenameUserConfigurationAction | RenameUserConfigurationAction
| SetUserConfigurationValueAction | SetUserConfigurationValueAction
| LoadUserConfigurationFromFileAction
| ApplyUserConfigurationFromFileAction
; ;

View File

@@ -162,7 +162,7 @@ export class DeviceEffects {
}); });
@Effect() saveResetUserConfigurationToDevice$ = this.actions$ @Effect() saveResetUserConfigurationToDevice$ = this.actions$
.ofType(UserConfigActions.LOAD_RESET_USER_CONFIGURATION) .ofType(UserConfigActions.LOAD_RESET_USER_CONFIGURATION, UserConfigActions.APPLY_USER_CONFIGURATION_FROM_FILE)
.switchMap(() => Observable.of(new SaveConfigurationAction())); .switchMap(() => Observable.of(new SaveConfigurationAction()));
@Effect({dispatch: false}) updateFirmware$ = this.actions$ @Effect({dispatch: false}) updateFirmware$ = this.actions$

View File

@@ -15,12 +15,21 @@ import 'rxjs/add/observable/of';
import 'rxjs/add/observable/empty'; import 'rxjs/add/observable/empty';
import { import {
ConfigurationReply, HardwareConfiguration, LogService, NotificationType, UhkBuffer, ConfigurationReply,
HardwareConfiguration,
LogService,
NotificationType,
UhkBuffer,
UserConfiguration UserConfiguration
} from 'uhk-common'; } from 'uhk-common';
import { import {
ActionTypes, LoadConfigFromDeviceReplyAction, LoadUserConfigSuccessAction, RenameUserConfigurationAction, ActionTypes,
ApplyUserConfigurationFromFileAction,
LoadConfigFromDeviceReplyAction,
LoadUserConfigSuccessAction,
LoadUserConfigurationFromFileAction,
RenameUserConfigurationAction,
SaveUserConfigSuccessAction SaveUserConfigSuccessAction
} from '../actions/user-config'; } from '../actions/user-config';
@@ -29,12 +38,15 @@ import { DefaultUserConfigurationService } from '../../services/default-user-con
import { AppState, autoWriteUserConfiguration, getPrevUserConfiguration, getUserConfiguration } from '../index'; import { AppState, autoWriteUserConfiguration, getPrevUserConfiguration, getUserConfiguration } from '../index';
import { KeymapAction, KeymapActions, MacroAction, MacroActions } from '../actions'; import { KeymapAction, KeymapActions, MacroAction, MacroActions } from '../actions';
import { import {
DismissUndoNotificationAction, LoadHardwareConfigurationSuccessAction, ShowNotificationAction, DismissUndoNotificationAction,
LoadHardwareConfigurationSuccessAction,
ShowNotificationAction,
UndoLastAction UndoLastAction
} from '../actions/app'; } from '../actions/app';
import { SaveConfigurationAction, ShowSaveToKeyboardButtonAction } from '../actions/device'; import { SaveConfigurationAction, ShowSaveToKeyboardButtonAction } from '../actions/device';
import { DeviceRendererService } from '../../services/device-renderer.service'; import { DeviceRendererService } from '../../services/device-renderer.service';
import { UndoUserConfigData } from '../../models/undo-user-config-data'; import { UndoUserConfigData } from '../../models/undo-user-config-data';
import { UploadFileData } from '../../models/upload-file-data';
@Injectable() @Injectable()
export class UserConfigEffects { export class UserConfigEffects {
@@ -198,6 +210,38 @@ export class UserConfigEffects {
} }
}); });
@Effect() loadUserConfigurationFromFile$ = this.actions$
.ofType<LoadUserConfigurationFromFileAction>(ActionTypes.LOAD_USER_CONFIGURATION_FROM_FILE)
.map(action => action.payload)
.map((info: UploadFileData) => {
try {
const userConfig = new UserConfiguration();
if (info.filename.endsWith('.bin')) {
userConfig.fromBinary(UhkBuffer.fromArray(info.data));
}
else {
const buffer = new Buffer(info.data);
const json = buffer.toString();
userConfig.fromJsonObject(JSON.parse(json));
}
if (userConfig.userConfigMajorVersion) {
return new ApplyUserConfigurationFromFileAction(userConfig);
}
return new ShowNotificationAction({
type: NotificationType.Error,
message: 'Invalid configuration specified.'
});
} catch (err) {
return new ShowNotificationAction({
type: NotificationType.Error,
message: 'Invalid configuration specified.'
});
}
});
constructor(private actions$: Actions, constructor(private actions$: Actions,
private dataStorageRepository: DataStorageRepositoryService, private dataStorageRepository: DataStorageRepositoryService,
private store: Store<AppState>, private store: Store<AppState>,

View File

@@ -16,6 +16,7 @@ export function reducer(state = initialState, action: Action & { payload?: any }
const changedUserConfiguration: UserConfiguration = Object.assign(new UserConfiguration(), state); const changedUserConfiguration: UserConfiguration = Object.assign(new UserConfiguration(), state);
switch (action.type) { switch (action.type) {
case ActionTypes.APPLY_USER_CONFIGURATION_FROM_FILE:
case ActionTypes.LOAD_RESET_USER_CONFIGURATION: case ActionTypes.LOAD_RESET_USER_CONFIGURATION:
case ActionTypes.LOAD_USER_CONFIG_SUCCESS: { case ActionTypes.LOAD_USER_CONFIG_SUCCESS: {
return Object.assign(changedUserConfiguration, action.payload); return Object.assign(changedUserConfiguration, action.payload);

View File

@@ -94,3 +94,24 @@ a.disabled {
.noUi-value { .noUi-value {
top: 2rem; top: 2rem;
} }
.btn-file {
position: relative;
overflow: hidden;
input[type=file] {
position: absolute;
top: 0;
right: 0;
min-width: 100%;
min-height: 100%;
font-size: 100px;
text-align: right;
filter: alpha(opacity=0);
opacity: 0;
outline: none;
background: white;
cursor: inherit;
display: block;
}
}