feat: show udev rules on missing device screen (#842)
* feat: show udev rules on missing device screen * delete MissingDeviceComponent * feat: change privilege and message device screen texts * feat: change message device screen texts * fix: load config from device * fix: load config when hasPermission = true * fix: load app start info after set permission rules * fix: action dispatch * fix: load config from device when UdevRulesInfo.Ok * fix: load config from device when 0 device available * feat: add extra space between the "old udev rule" and permission button
This commit is contained in:
committed by
László Monda
parent
10cd06c70b
commit
1a9bd7de83
@@ -31,10 +31,10 @@
|
||||
"scripts": {
|
||||
"start": "electron ./dist/electron-main.js",
|
||||
"electron:spe": "electron ./dist/electron-main.js --spe",
|
||||
"build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-blhost",
|
||||
"build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-to-tmp-folder",
|
||||
"build:usb": "electron-rebuild -w node-hid -p -m ./dist",
|
||||
"install:build-deps": "cd ./dist && npm i",
|
||||
"download-firmware": "node ../../scripts/download-firmware.js",
|
||||
"copy-blhost": "node ../../scripts/copy-blhost.js"
|
||||
"copy-to-tmp-folder": "node ../../scripts/copy-to-tmp-folder.js"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ function createWindow() {
|
||||
});
|
||||
setMenu(win);
|
||||
win.maximize();
|
||||
uhkHidDeviceService = new UhkHidDevice(logger, options);
|
||||
uhkHidDeviceService = new UhkHidDevice(logger, options, packagesDir);
|
||||
uhkBlhost = new UhkBlhost(logger, packagesDir);
|
||||
uhkOperations = new UhkOperations(logger, uhkBlhost, uhkHidDeviceService, packagesDir);
|
||||
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations, packagesDir);
|
||||
|
||||
@@ -23,14 +23,12 @@ export class AppService extends MainServiceBase {
|
||||
|
||||
private async handleAppStartInfo(event: Electron.Event) {
|
||||
this.logService.info('[AppService] getAppStartInfo');
|
||||
const deviceConnectionState = this.uhkHidDeviceService.getDeviceConnectionState();
|
||||
const deviceConnectionState = await this.uhkHidDeviceService.getDeviceConnectionStateAsync();
|
||||
const response: AppStartInfo = {
|
||||
deviceConnectionState,
|
||||
commandLineArgs: {
|
||||
addons: this.options.addons || false
|
||||
},
|
||||
deviceConnected: deviceConnectionState.connected,
|
||||
hasPermission: deviceConnectionState.hasPermission,
|
||||
bootloaderActive: deviceConnectionState.bootloaderActive,
|
||||
platform: process.platform as string,
|
||||
osVersion: os.release()
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import { isEqual } from 'lodash';
|
||||
import {
|
||||
ConfigurationReply,
|
||||
DeviceConnectionState,
|
||||
@@ -12,15 +13,16 @@ import {
|
||||
SaveUserConfigurationData,
|
||||
UpdateFirmwareData
|
||||
} from 'uhk-common';
|
||||
import { deviceConnectionStateComparer, snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
|
||||
import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { emptyDir } from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
|
||||
import 'rxjs/add/observable/interval';
|
||||
import 'rxjs/add/observable/fromPromise';
|
||||
import 'rxjs/add/operator/switchMap';
|
||||
import 'rxjs/add/operator/startWith';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
|
||||
@@ -253,8 +255,8 @@ export class DeviceService {
|
||||
|
||||
this.pollTimer$ = Observable.interval(1000)
|
||||
.startWith(0)
|
||||
.map(() => this.device.getDeviceConnectionState())
|
||||
.distinctUntilChanged<DeviceConnectionState>(deviceConnectionStateComparer)
|
||||
.switchMap(() => Observable.fromPromise(this.device.getDeviceConnectionStateAsync()))
|
||||
.distinctUntilChanged<DeviceConnectionState>(isEqual)
|
||||
.do((state: DeviceConnectionState) => {
|
||||
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
|
||||
this.logService.info('[DeviceService] Device connection state changed to:', state);
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { CommandLineArgs } from './command-line-args';
|
||||
import { DeviceConnectionState } from './device-connection-state';
|
||||
|
||||
export interface AppStartInfo {
|
||||
commandLineArgs: CommandLineArgs;
|
||||
deviceConnected: boolean;
|
||||
hasPermission: boolean;
|
||||
bootloaderActive: boolean;
|
||||
deviceConnectionState: DeviceConnectionState;
|
||||
platform: string;
|
||||
osVersion: string;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { UdevRulesInfo } from './udev-rules-info';
|
||||
|
||||
export interface DeviceConnectionState {
|
||||
connected: boolean;
|
||||
hasPermission: boolean;
|
||||
bootloaderActive: boolean;
|
||||
zeroInterfaceAvailable: boolean;
|
||||
udevRulesInfo: UdevRulesInfo;
|
||||
}
|
||||
|
||||
@@ -8,4 +8,5 @@ export * from './device-connection-state';
|
||||
export * from './hardware-modules';
|
||||
export * from './hardware-module-info';
|
||||
export * from './save-user-configuration-data';
|
||||
export * from './udev-rules-info';
|
||||
export * from './update-firmware-data';
|
||||
|
||||
16
packages/uhk-common/src/models/udev-rules-info.ts
Normal file
16
packages/uhk-common/src/models/udev-rules-info.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* What is the state of the udev rules.
|
||||
* Only on Linux need extra udev rules.
|
||||
*/
|
||||
export enum UdevRulesInfo {
|
||||
Unkonwn,
|
||||
Ok,
|
||||
/**
|
||||
* Udev rules not exists need to setup on Linux
|
||||
*/
|
||||
NeedToSetup,
|
||||
/**
|
||||
* Udev rules exist but different than expected on Linux
|
||||
*/
|
||||
Different
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Device, devices, HID } from 'node-hid';
|
||||
import { CommandLineArgs, DeviceConnectionState, isEqualArray, LogService } from 'uhk-common';
|
||||
import { pathExists } from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { CommandLineArgs, DeviceConnectionState, isEqualArray, LogService, UdevRulesInfo } from 'uhk-common';
|
||||
|
||||
import {
|
||||
ConfigBufferId,
|
||||
@@ -13,7 +15,7 @@ import {
|
||||
UsbCommand,
|
||||
UsbVariables
|
||||
} from './constants';
|
||||
import { bufferToString, getTransferData, isUhkDevice, retry, snooze } from './util';
|
||||
import { bufferToString, getFileContentAsync, getTransferData, isUhkDevice, isUhkZeroInterface, retry, snooze } from './util';
|
||||
|
||||
export const BOOTLOADER_TIMEOUT_MS = 5000;
|
||||
|
||||
@@ -28,9 +30,11 @@ export class UhkHidDevice {
|
||||
private _prevDevices = [];
|
||||
private _device: HID;
|
||||
private _hasPermission = false;
|
||||
private _udevRulesInfo = UdevRulesInfo.Unkonwn;
|
||||
|
||||
constructor(private logService: LogService,
|
||||
private options: CommandLineArgs) {
|
||||
private options: CommandLineArgs,
|
||||
private rootDir: string) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,7 +58,7 @@ export class UhkHidDevice {
|
||||
const devs = devices();
|
||||
this.logDevices(devs);
|
||||
|
||||
const dev = devs.find((x: Device) => isUhkDevice(x) || x.productId === Constants.BOOTLOADER_ID);
|
||||
const dev = devs.find((x: Device) => isUhkZeroInterface(x) || x.productId === Constants.BOOTLOADER_ID);
|
||||
|
||||
if (!dev) {
|
||||
return true;
|
||||
@@ -74,20 +78,26 @@ export class UhkHidDevice {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return with true is an UHK Device is connected to the computer.
|
||||
* @returns {boolean}
|
||||
* Return with the USB device communication sate.
|
||||
* @returns {DeviceConnectionState}
|
||||
*/
|
||||
public getDeviceConnectionState(): DeviceConnectionState {
|
||||
public async getDeviceConnectionStateAsync(): Promise<DeviceConnectionState> {
|
||||
const devs = devices();
|
||||
const result: DeviceConnectionState = {
|
||||
bootloaderActive: false,
|
||||
connected: false,
|
||||
hasPermission: this.hasPermission()
|
||||
zeroInterfaceAvailable: false,
|
||||
hasPermission: this.hasPermission(),
|
||||
udevRulesInfo: await this.getUdevInfoAsync()
|
||||
};
|
||||
|
||||
for (const dev of devs) {
|
||||
if (isUhkDevice(dev)) {
|
||||
result.connected = true;
|
||||
}
|
||||
|
||||
if (isUhkZeroInterface(dev)) {
|
||||
result.zeroInterfaceAvailable = true;
|
||||
} else if (dev.vendorId === Constants.VENDOR_ID &&
|
||||
dev.productId === Constants.BOOTLOADER_ID) {
|
||||
result.bootloaderActive = true;
|
||||
@@ -270,7 +280,7 @@ export class UhkHidDevice {
|
||||
this.logService.debug('[UhkHidDevice] Available devices unchanged');
|
||||
}
|
||||
|
||||
const dev = devs.find(isUhkDevice);
|
||||
const dev = devs.find(isUhkZeroInterface);
|
||||
|
||||
if (!dev) {
|
||||
this.logService.debug('[UhkHidDevice] UHK Device not found:');
|
||||
@@ -279,8 +289,7 @@ export class UhkHidDevice {
|
||||
const device = new HID(dev.path);
|
||||
this.logService.debug('[UhkHidDevice] Used device:', JSON.stringify(dev));
|
||||
return device;
|
||||
}
|
||||
catch (err) {
|
||||
} catch (err) {
|
||||
this.logService.error('[UhkHidDevice] Can not create device:', err);
|
||||
}
|
||||
|
||||
@@ -292,6 +301,31 @@ export class UhkHidDevice {
|
||||
this.logService.debug(JSON.stringify(logDevice));
|
||||
}
|
||||
}
|
||||
|
||||
private async getUdevInfoAsync(): Promise<UdevRulesInfo> {
|
||||
if (this._udevRulesInfo === UdevRulesInfo.Ok) {
|
||||
return UdevRulesInfo.Ok;
|
||||
}
|
||||
|
||||
if (process.platform === 'win32' || process.platform === 'darwin') {
|
||||
this._udevRulesInfo = UdevRulesInfo.Ok;
|
||||
return UdevRulesInfo.Ok;
|
||||
}
|
||||
|
||||
if (!(await pathExists('/etc/udev/rules.d/50-uhk60.rules'))) {
|
||||
return UdevRulesInfo.NeedToSetup;
|
||||
}
|
||||
|
||||
const expectedUdevSettings = await getFileContentAsync(path.join(this.rootDir, 'rules/50-uhk60.rules'));
|
||||
const currentUdevSettings = await getFileContentAsync('/etc/udev/rules.d/50-uhk60.rules');
|
||||
|
||||
if (isEqualArray(expectedUdevSettings, currentUdevSettings)) {
|
||||
this._udevRulesInfo = UdevRulesInfo.Ok;
|
||||
return UdevRulesInfo.Ok;
|
||||
}
|
||||
|
||||
return UdevRulesInfo.Different;
|
||||
}
|
||||
}
|
||||
|
||||
function kbootCommandName(module: ModuleSlotToI2cAddress): string {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Device } from 'node-hid';
|
||||
import { DeviceConnectionState, LogService } from 'uhk-common';
|
||||
import { readFile } from 'fs-extra';
|
||||
import { EOL } from 'os';
|
||||
import { LogService } from 'uhk-common';
|
||||
|
||||
import { Constants, UsbCommand } from './constants';
|
||||
|
||||
@@ -98,13 +100,7 @@ export async function retry(command: Function, maxTry = 3, logService?: LogServi
|
||||
}
|
||||
}
|
||||
|
||||
export const deviceConnectionStateComparer = (a: DeviceConnectionState, b: DeviceConnectionState): boolean => {
|
||||
return a.hasPermission === b.hasPermission
|
||||
&& a.connected === b.connected
|
||||
&& a.bootloaderActive === b.bootloaderActive;
|
||||
};
|
||||
|
||||
export const isUhkDevice = (dev: Device): boolean => {
|
||||
export const isUhkZeroInterface = (dev: Device): boolean => {
|
||||
return dev.vendorId === Constants.VENDOR_ID &&
|
||||
dev.productId === Constants.PRODUCT_ID &&
|
||||
// hidapi can not read the interface number on Mac, so check the usage page and usage
|
||||
@@ -112,3 +108,17 @@ export const isUhkDevice = (dev: Device): boolean => {
|
||||
(dev.usagePage === (0xFF00 | 0x00) && dev.usage === 0x01) || // New firmware
|
||||
dev.interface === 0);
|
||||
};
|
||||
|
||||
export const isUhkDevice = (dev: Device): boolean => {
|
||||
return dev.vendorId === Constants.VENDOR_ID &&
|
||||
(dev.productId === Constants.PRODUCT_ID || dev.productId === Constants.BOOTLOADER_ID);
|
||||
};
|
||||
|
||||
export const getFileContentAsync = async (filePath: string): Promise<Array<string>> => {
|
||||
const fileContent = await readFile(filePath, {encoding: 'utf-8'});
|
||||
|
||||
return fileContent
|
||||
.split(EOL)
|
||||
.map(x => x.trim())
|
||||
.filter(x => !x.startsWith('#') && x.length > 0);
|
||||
};
|
||||
|
||||
@@ -1 +1 @@
|
||||
<uhk-message header="Cannot find your UHK" subtitle="Please plug it in!"></uhk-message>
|
||||
<uhk-message [header]="state.header" [subtitle]="state.subtitle"></uhk-message>
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
import 'rxjs/add/operator/ignoreElements';
|
||||
import 'rxjs/add/operator/takeWhile';
|
||||
import { AppState, getMissingDeviceState } from '../../store';
|
||||
import { MissingDeviceState } from '../../models/missing-device-state';
|
||||
|
||||
@Component({
|
||||
selector: 'missing-device',
|
||||
templateUrl: './missing-device.component.html'
|
||||
})
|
||||
export class MissingDeviceComponent {
|
||||
export class MissingDeviceComponent implements OnDestroy {
|
||||
|
||||
constructor() {}
|
||||
state: MissingDeviceState;
|
||||
|
||||
private stateSubscription: Subscription;
|
||||
|
||||
constructor(private store: Store<AppState>) {
|
||||
this.stateSubscription = this.store
|
||||
.select(getMissingDeviceState)
|
||||
.subscribe(state => this.state = state);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.stateSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,14 @@
|
||||
<uhk-message header="Cannot talk to your UHK"
|
||||
subtitle="Your UHK has been detected, but its permissions are not set up yet, so Agent can't talk to it."></uhk-message>
|
||||
|
||||
<button class="btn btn-default btn-lg btn-primary"
|
||||
(click)="setUpPermissions()"> Set up permissions
|
||||
<div *ngIf="state.updateUdevRules">
|
||||
You seem to have an old udev rule file installed. New Agent versions require and updated udev rule file to find your UHK.
|
||||
</div>
|
||||
|
||||
<button class="btn btn-default btn-lg btn-primary mt-10"
|
||||
(click)="setUpPermissions()">
|
||||
<span *ngIf="!state.updateUdevRules">Set up permissions</span>
|
||||
<span *ngIf="state.updateUdevRules">Update udev rule file</span>
|
||||
</button>
|
||||
|
||||
<div class="mt-10">
|
||||
@@ -22,21 +28,7 @@
|
||||
|
||||
<div *ngIf="state.showWhatWillThisDoContent">
|
||||
If you want to set up permissions manually:
|
||||
<ol>
|
||||
<li>Open a terminal.</li>
|
||||
<li>Run <code>su</code> to become root.</li>
|
||||
<li>Paste the following script, and <a class="link-inline" (click)="retry()">retry</a>.</li>
|
||||
</ol>
|
||||
|
||||
<div class="copy-container">
|
||||
<span class="fa fa-2x fa-copy"
|
||||
ngxClipboard
|
||||
[cbContent]="command"
|
||||
title="Copy to clipboard"
|
||||
data-toggle="tooltip"
|
||||
data-placement="top"></span>
|
||||
<pre><code>{{ command }}</code></pre>
|
||||
</div>
|
||||
<udev-rules></udev-rules>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,17 +18,6 @@ export class PrivilegeCheckerComponent implements OnInit, OnDestroy {
|
||||
|
||||
state: PrivilagePageSate;
|
||||
|
||||
command = `cat <<EOF >/etc/udev/rules.d/50-uhk60.rules
|
||||
# Ultimate Hacking Keyboard rules
|
||||
# These are the udev rules for accessing the USB interfaces of the UHK as non-root users.
|
||||
# Copy this file to /etc/udev/rules.d and physically reconnect the UHK afterwards.
|
||||
SUBSYSTEM=="input", GROUP="input", MODE="0666"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="612[0-7]", MODE:="0666", GROUP="plugdev"
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="612[0-7]", MODE="0666", GROUP="plugdev"
|
||||
EOF
|
||||
udevadm trigger
|
||||
udevadm settle`;
|
||||
|
||||
private stateSubscription: Subscription;
|
||||
|
||||
constructor(private store: Store<AppState>,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<ol>
|
||||
<li>Open a terminal.</li>
|
||||
<li>Run <code>su</code> to become root.</li>
|
||||
<li>Paste the following script:</li>
|
||||
</ol>
|
||||
|
||||
<div class="copy-container">
|
||||
<span class="fa fa-2x fa-copy"
|
||||
ngxClipboard
|
||||
[cbContent]="command"
|
||||
title="Copy to clipboard"
|
||||
data-toggle="tooltip"
|
||||
data-placement="top"></span>
|
||||
<pre><code>{{ command }}</code></pre>
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'udev-rules',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: './udev-rules.component.html'
|
||||
})
|
||||
export class UdevRulesComponent {
|
||||
command = `cat <<EOF >/etc/udev/rules.d/50-uhk60.rules
|
||||
# Ultimate Hacking Keyboard rules
|
||||
# These are the udev rules for accessing the USB interfaces of the UHK as non-root users.
|
||||
# Copy this file to /etc/udev/rules.d and physically reconnect the UHK afterwards.
|
||||
SUBSYSTEM=="input", GROUP="input", MODE="0666"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="612[0-7]", MODE:="0666", GROUP="plugdev"
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="612[0-7]", MODE="0666", GROUP="plugdev"
|
||||
EOF
|
||||
udevadm trigger
|
||||
udevadm settle`;
|
||||
}
|
||||
4
packages/uhk-web/src/app/models/missing-device-state.ts
Normal file
4
packages/uhk-web/src/app/models/missing-device-state.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface MissingDeviceState {
|
||||
header: string;
|
||||
subtitle: string;
|
||||
}
|
||||
@@ -2,4 +2,5 @@ export interface PrivilagePageSate {
|
||||
showWhatWillThisDo: boolean;
|
||||
showWhatWillThisDoContent: boolean;
|
||||
permissionSetupFailed: boolean;
|
||||
updateUdevRules: boolean;
|
||||
}
|
||||
|
||||
@@ -111,6 +111,7 @@ import { UhkDeviceBootloaderNotActiveGuard } from './services/uhk-device-bootloa
|
||||
import { FileUploadComponent } from './components/file-upload';
|
||||
import { AutoGrowInputComponent } from './components/auto-grow-input';
|
||||
import { HelpPageComponent } from './components/agent/help-page/help-page.component';
|
||||
import { UdevRulesComponent } from './components/udev-rules/udev-rules.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -188,7 +189,8 @@ import { HelpPageComponent } from './components/agent/help-page/help-page.compon
|
||||
AutoGrowInputComponent,
|
||||
HelpPageComponent,
|
||||
ExternalUrlDirective,
|
||||
SvgSecondaryRoleComponent
|
||||
SvgSecondaryRoleComponent,
|
||||
UdevRulesComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
@@ -66,11 +66,7 @@ export class ApplicationEffects {
|
||||
this.logService.debug('[AppEffect][processStartInfo] payload:', appInfo);
|
||||
return [
|
||||
new ApplyAppStartInfoAction(appInfo),
|
||||
new ConnectionStateChangedAction({
|
||||
connected: appInfo.deviceConnected,
|
||||
hasPermission: appInfo.hasPermission,
|
||||
bootloaderActive: appInfo.bootloaderActive
|
||||
})
|
||||
new ConnectionStateChangedAction(appInfo.deviceConnectionState)
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
HardwareConfiguration,
|
||||
IpcResponse,
|
||||
NotificationType,
|
||||
UdevRulesInfo,
|
||||
UserConfiguration
|
||||
} from 'uhk-common';
|
||||
import {
|
||||
@@ -39,9 +40,10 @@ import {
|
||||
UpdateFirmwareSuccessAction,
|
||||
UpdateFirmwareWithAction
|
||||
} from '../actions/device';
|
||||
import { AppRendererService } from '../../services/app-renderer.service';
|
||||
import { DeviceRendererService } from '../../services/device-renderer.service';
|
||||
import { SetupPermissionErrorAction, ShowNotificationAction } from '../actions/app';
|
||||
import { AppState, getRouterState } from '../index';
|
||||
import { AppState, deviceConnected, getRouterState } from '../index';
|
||||
import {
|
||||
ActionTypes as UserConfigActions,
|
||||
ApplyUserConfigurationFromFileAction,
|
||||
@@ -57,7 +59,7 @@ export class DeviceEffects {
|
||||
@Effect()
|
||||
deviceConnectionStateChange$: Observable<Action> = this.actions$
|
||||
.ofType<ConnectionStateChangedAction>(ActionTypes.CONNECTION_STATE_CHANGED)
|
||||
.withLatestFrom(this.store.select(getRouterState))
|
||||
.withLatestFrom(this.store.select(getRouterState), this.store.select(deviceConnected))
|
||||
.do(([action, route]) => {
|
||||
const state = action.payload;
|
||||
|
||||
@@ -65,23 +67,24 @@ export class DeviceEffects {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state.hasPermission) {
|
||||
this.router.navigate(['/privilege']);
|
||||
if (!state.hasPermission || !state.zeroInterfaceAvailable) {
|
||||
return this.router.navigate(['/privilege']);
|
||||
}
|
||||
else if (state.bootloaderActive) {
|
||||
this.router.navigate(['/recovery-device']);
|
||||
}
|
||||
else if (state.connected) {
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
else {
|
||||
this.router.navigate(['/detection']);
|
||||
}
|
||||
})
|
||||
.switchMap(([action, route]) => {
|
||||
const state = action.payload;
|
||||
|
||||
if (state.connected && state.hasPermission) {
|
||||
if (state.bootloaderActive) {
|
||||
return this.router.navigate(['/recovery-device']);
|
||||
}
|
||||
|
||||
if (state.connected && state.zeroInterfaceAvailable) {
|
||||
return this.router.navigate(['/']);
|
||||
}
|
||||
|
||||
return this.router.navigate(['/detection']);
|
||||
})
|
||||
.switchMap(([action, route, connected]) => {
|
||||
const payload = action.payload;
|
||||
|
||||
if (connected && payload.hasPermission && payload.zeroInterfaceAvailable) {
|
||||
return Observable.of(new LoadConfigFromDeviceAction());
|
||||
}
|
||||
|
||||
@@ -99,16 +102,13 @@ export class DeviceEffects {
|
||||
setPrivilegeOnLinuxReply$: Observable<Action> = this.actions$
|
||||
.ofType<SetPrivilegeOnLinuxReplyAction>(ActionTypes.SET_PRIVILEGE_ON_LINUX_REPLY)
|
||||
.map(action => action.payload)
|
||||
.map((response: any): any => {
|
||||
.switchMap((response: any): any => {
|
||||
if (response.success) {
|
||||
return new ConnectionStateChangedAction({
|
||||
connected: true,
|
||||
hasPermission: true,
|
||||
bootloaderActive: false
|
||||
});
|
||||
this.appRendererService.getAppStartInfo();
|
||||
return Observable.empty();
|
||||
}
|
||||
|
||||
return new SetupPermissionErrorAction(response.error);
|
||||
return Observable.of(new SetupPermissionErrorAction(response.error));
|
||||
});
|
||||
|
||||
@Effect({dispatch: false})
|
||||
@@ -243,6 +243,7 @@ export class DeviceEffects {
|
||||
|
||||
constructor(private actions$: Actions,
|
||||
private router: Router,
|
||||
private appRendererService: AppRendererService,
|
||||
private deviceRendererService: DeviceRendererService,
|
||||
private store: Store<AppState>,
|
||||
private dataStorageRepository: DataStorageRepositoryService,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import { ActionReducerMap, MetaReducer } from '@ngrx/store';
|
||||
import { RouterReducerState, routerReducer } from '@ngrx/router-store';
|
||||
import { routerReducer, RouterReducerState } from '@ngrx/router-store';
|
||||
import { storeFreeze } from 'ngrx-store-freeze';
|
||||
import { HardwareModules, Keymap, UserConfiguration } from 'uhk-common';
|
||||
|
||||
@@ -14,6 +14,7 @@ import * as fromSelectors from './reducers/selectors';
|
||||
import { initProgressButtonState } from './reducers/progress-button-state';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { RouterStateUrl } from './router-util';
|
||||
import { PrivilagePageSate } from '../models/privilage-page-sate';
|
||||
import { isVersionGte } from '../util';
|
||||
|
||||
// State interface for the application
|
||||
@@ -52,7 +53,6 @@ export const runningInElectron = createSelector(appState, fromApp.runningInElect
|
||||
export const getKeyboardLayout = createSelector(appState, fromApp.getKeyboardLayout);
|
||||
export const deviceConfigurationLoaded = createSelector(appState, fromApp.deviceConfigurationLoaded);
|
||||
export const getAgentVersionInfo = createSelector(appState, fromApp.getAgentVersionInfo);
|
||||
export const getPrivilegePageState = createSelector(appState, fromApp.getPrivilagePageState);
|
||||
export const getOperatingSystem = createSelector(appState, fromSelectors.getOperatingSystem);
|
||||
export const keypressCapturing = createSelector(appState, fromApp.keypressCapturing);
|
||||
export const runningOnNotSupportedWindows = createSelector(appState, fromApp.runningOnNotSupportedWindows);
|
||||
@@ -66,14 +66,21 @@ export const getAutoUpdateSettings = createSelector(appUpdateSettingsState, auto
|
||||
export const getCheckingForUpdate = createSelector(appUpdateSettingsState, autoUpdateSettings.checkingForUpdate);
|
||||
|
||||
export const deviceState = (state: AppState) => state.device;
|
||||
export const isDeviceConnected = createSelector(deviceState, fromDevice.isDeviceConnected);
|
||||
export const deviceConnected = createSelector(runningInElectron, isDeviceConnected, (electron, connected) => {
|
||||
return !electron ? true : connected;
|
||||
});
|
||||
export const devicePermission = createSelector(deviceState, fromDevice.hasDevicePermission);
|
||||
export const hasDevicePermission = createSelector(runningInElectron, devicePermission, (electron, permission) => {
|
||||
return !electron ? true : permission;
|
||||
});
|
||||
export const deviceConnected = createSelector(
|
||||
runningInElectron, deviceState, appState,
|
||||
(electron, device, app) => {
|
||||
if (!electron) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (app.platform === 'linux') {
|
||||
return device.connected && (device.zeroInterfaceAvailable || device.updatingFirmware);
|
||||
}
|
||||
|
||||
return device.connected;
|
||||
});
|
||||
export const hasDevicePermission = createSelector(deviceState, fromDevice.hasDevicePermission);
|
||||
export const getMissingDeviceState = createSelector(deviceState, fromDevice.getMissingDeviceState);
|
||||
export const saveToKeyboardStateSelector = createSelector(deviceState, fromDevice.getSaveToKeyboardState);
|
||||
export const saveToKeyboardState = createSelector(runningInElectron, saveToKeyboardStateSelector, (electron, state) => {
|
||||
return electron ? state : initProgressButtonState;
|
||||
@@ -88,6 +95,18 @@ export const getRestoreUserConfiguration = createSelector(deviceState, fromDevic
|
||||
export const bootloaderActive = createSelector(deviceState, fromDevice.bootloaderActive);
|
||||
export const firmwareUpgradeFailed = createSelector(deviceState, fromDevice.firmwareUpgradeFailed);
|
||||
export const firmwareUpgradeSuccess = createSelector(deviceState, fromDevice.firmwareUpgradeSuccess);
|
||||
export const getUpdateUdevRules = createSelector(deviceState, fromDevice.updateUdevRules);
|
||||
|
||||
export const getPrivilegePageState = createSelector(appState, getUpdateUdevRules, (app, updateUdevRules): PrivilagePageSate => {
|
||||
const permissionSetupFailed = !!app.permissionError;
|
||||
|
||||
return {
|
||||
permissionSetupFailed,
|
||||
updateUdevRules,
|
||||
showWhatWillThisDo: !app.privilegeWhatWillThisDoClicked && !permissionSetupFailed,
|
||||
showWhatWillThisDoContent: app.privilegeWhatWillThisDoClicked || permissionSetupFailed
|
||||
};
|
||||
});
|
||||
|
||||
export const getSideMenuPageState = createSelector(
|
||||
showAddonMenu,
|
||||
|
||||
@@ -183,15 +183,6 @@ export const getKeyboardLayout = (state: State): KeyboardLayout => {
|
||||
};
|
||||
export const deviceConfigurationLoaded = (state: State) => !state.runningInElectron ? true : !!state.hardwareConfig;
|
||||
export const getAgentVersionInfo = (state: State) => state.agentVersionInfo || {} as VersionInformation;
|
||||
export const getPrivilagePageState = (state: State): PrivilagePageSate => {
|
||||
const permissionSetupFailed = !!state.permissionError;
|
||||
|
||||
return {
|
||||
permissionSetupFailed,
|
||||
showWhatWillThisDo: !state.privilegeWhatWillThisDoClicked && !permissionSetupFailed,
|
||||
showWhatWillThisDoContent: state.privilegeWhatWillThisDoClicked || permissionSetupFailed
|
||||
};
|
||||
};
|
||||
|
||||
export const runningOnNotSupportedWindows = (state: State): boolean => {
|
||||
if (!state.osVersion || state.platform !== 'win32') {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { HardwareModules } from 'uhk-common';
|
||||
import { HardwareModules, UdevRulesInfo } from 'uhk-common';
|
||||
|
||||
import {
|
||||
ActionTypes,
|
||||
ConnectionStateChangedAction,
|
||||
HardwareModulesLoadedAction,
|
||||
SaveConfigurationAction,
|
||||
HasBackupUserConfigurationAction,
|
||||
SaveConfigurationAction,
|
||||
UpdateFirmwareFailedAction,
|
||||
UpdateFirmwareSuccessAction
|
||||
} from '../actions/device';
|
||||
@@ -14,11 +14,14 @@ import { ActionTypes as AppActions, ElectronMainLogReceivedAction } from '../act
|
||||
import { initProgressButtonState, ProgressButtonState } from './progress-button-state';
|
||||
import { XtermCssClass, XtermLog } from '../../models/xterm-log';
|
||||
import { RestoreConfigurationState } from '../../models/restore-configuration-state';
|
||||
import { MissingDeviceState } from '../../models/missing-device-state';
|
||||
|
||||
export interface State {
|
||||
connected: boolean;
|
||||
hasPermission: boolean;
|
||||
bootloaderActive: boolean;
|
||||
zeroInterfaceAvailable: boolean;
|
||||
udevRuleInfo: UdevRulesInfo;
|
||||
saveToKeyboard: ProgressButtonState;
|
||||
savingToKeyboard: boolean;
|
||||
updatingFirmware: boolean;
|
||||
@@ -35,6 +38,8 @@ export const initialState: State = {
|
||||
connected: true,
|
||||
hasPermission: true,
|
||||
bootloaderActive: false,
|
||||
zeroInterfaceAvailable: true,
|
||||
udevRuleInfo: UdevRulesInfo.Unkonwn,
|
||||
saveToKeyboard: initProgressButtonState,
|
||||
savingToKeyboard: false,
|
||||
updatingFirmware: false,
|
||||
@@ -61,7 +66,9 @@ export function reducer(state = initialState, action: Action): State {
|
||||
...state,
|
||||
connected: data.connected,
|
||||
hasPermission: data.hasPermission,
|
||||
bootloaderActive: data.bootloaderActive
|
||||
zeroInterfaceAvailable: data.zeroInterfaceAvailable,
|
||||
bootloaderActive: data.bootloaderActive,
|
||||
udevRuleInfo: data.udevRulesInfo
|
||||
};
|
||||
}
|
||||
|
||||
@@ -222,7 +229,20 @@ export function reducer(state = initialState, action: Action): State {
|
||||
|
||||
export const updatingFirmware = (state: State) => state.updatingFirmware;
|
||||
export const isDeviceConnected = (state: State) => state.connected || state.updatingFirmware;
|
||||
export const hasDevicePermission = (state: State) => state.hasPermission;
|
||||
export const hasDevicePermission = (state: State) => state.udevRuleInfo === UdevRulesInfo.Ok;
|
||||
export const getMissingDeviceState = (state: State): MissingDeviceState => {
|
||||
if (state.connected && !state.zeroInterfaceAvailable) {
|
||||
return {
|
||||
header: 'Cannot find your UHK',
|
||||
subtitle: 'Please reconnect it!'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
header: 'Cannot find your UHK',
|
||||
subtitle: 'Please plug it in!'
|
||||
};
|
||||
};
|
||||
export const getSaveToKeyboardState = (state: State) => state.saveToKeyboard;
|
||||
export const xtermLog = (state: State) => state.log;
|
||||
export const getHardwareModules = (state: State) => state.modules;
|
||||
@@ -236,3 +256,4 @@ export const getBackupUserConfigurationState = (state: State): RestoreConfigurat
|
||||
export const bootloaderActive = (state: State) => state.bootloaderActive;
|
||||
export const firmwareUpgradeFailed = (state: State) => state.firmwareUpdateFailed;
|
||||
export const firmwareUpgradeSuccess = (state: State) => state.firmwareUpdateSuccess;
|
||||
export const updateUdevRules = (state: State) => state.udevRuleInfo === UdevRulesInfo.Different;
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
const fse = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
fse.copy(
|
||||
path.join(__dirname, '../packages/usb/blhost'),
|
||||
path.join(__dirname, '../tmp/packages/blhost'),
|
||||
{
|
||||
overwrite:true,
|
||||
recursive:true
|
||||
});
|
||||
27
scripts/copy-to-tmp-folder.js
Normal file
27
scripts/copy-to-tmp-folder.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const fse = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
const copyOptions = {
|
||||
overwrite: true,
|
||||
recursive: true
|
||||
};
|
||||
|
||||
const promises = [];
|
||||
|
||||
promises.push(
|
||||
fse.copy(
|
||||
path.join(__dirname, '../packages/usb/blhost'),
|
||||
path.join(__dirname, '../tmp/packages/blhost'),
|
||||
copyOptions)
|
||||
);
|
||||
|
||||
promises.push(
|
||||
fse.copy(
|
||||
path.join(__dirname, '../rules'),
|
||||
path.join(__dirname, '../tmp/rules'),
|
||||
copyOptions)
|
||||
);
|
||||
|
||||
Promise
|
||||
.all(promises)
|
||||
.catch(console.error);
|
||||
Reference in New Issue
Block a user