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:
committed by
László Monda
parent
c135aed7c9
commit
8d7269a998
@@ -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');
|
||||
}
|
||||
|
||||
@@ -11,3 +11,8 @@
|
||||
<a class="" href="https://github.com/UltimateHackingKeyboard/agent" title="Fork me on GitHub">Fork me on GitHub</a>
|
||||
</div>
|
||||
<notifier-container></notifier-container>
|
||||
<progress-button class="save-to-keyboard-button"
|
||||
*ngIf="(saveToKeyboardState$ | async).showButton"
|
||||
[@showSaveToKeyboardButton]
|
||||
[state]="saveToKeyboardState$ | async"
|
||||
(clicked)="clickedOnProgressButton($event)"></progress-button>
|
||||
|
||||
@@ -37,3 +37,9 @@ main-app {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.save-to-keyboard-button {
|
||||
position: fixed;
|
||||
bottom: 15px;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
@@ -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<boolean>;
|
||||
deviceConnected$: Observable<boolean>;
|
||||
runningInElectron$: Observable<boolean>;
|
||||
saveToKeyboardState$: Observable<ProgressButtonState>;
|
||||
|
||||
constructor(private store: Store<AppState>) {
|
||||
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')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<button class="btn btn-primary"
|
||||
(click)="onClicked()"
|
||||
[disabled]="state.showProgress">
|
||||
<i class="fa fa-spin fa-spinner" *ngIf="state.showProgress"></i> {{state.text}}
|
||||
</button>
|
||||
@@ -0,0 +1,3 @@
|
||||
button {
|
||||
min-width: 150px;
|
||||
}
|
||||
@@ -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<Action> = new EventEmitter<Action>();
|
||||
|
||||
onClicked() {
|
||||
this.clicked.emit(this.state.action);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import { NotifierOptions } from 'angular-notifier';
|
||||
|
||||
export const angularNotifierConfig: NotifierOptions = {
|
||||
behaviour: {
|
||||
autoHide: false
|
||||
},
|
||||
position: {
|
||||
|
||||
horizontal: {
|
||||
|
||||
@@ -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$
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user