feat: show macro usage count (#1030)

* feat: show macro usage count

* feat: show macro reference count only when ALT key hold down

* fix: the macro reference help text
This commit is contained in:
Róbert Kiss
2019-09-03 21:42:27 +02:00
committed by László Monda
parent 9844645409
commit fef24613e4
10 changed files with 110 additions and 8 deletions

View File

@@ -16,6 +16,7 @@ import {
} from './store'; } from './store';
import { ProgressButtonState } from './store/reducers/progress-button-state'; import { ProgressButtonState } from './store/reducers/progress-button-state';
import { UpdateInfo } from './models/update-info'; import { UpdateInfo } from './models/update-info';
import { KeyUpAction, KeyDownAction } from './store/actions/app';
@Component({ @Component({
selector: 'main-app', selector: 'main-app',
@@ -95,6 +96,13 @@ export class MainAppComponent implements OnDestroy {
this.enableUsbStackTest(); this.enableUsbStackTest();
event.preventDefault(); event.preventDefault();
} }
this.store.dispatch(new KeyDownAction(event));
}
@HostListener('document:keyup', ['$event'])
onKeyUp(event: KeyboardEvent) {
this.store.dispatch(new KeyUpAction(event));
} }
updateApp() { updateApp() {

View File

@@ -20,6 +20,7 @@
<li>Right click on a key: Capture key</li> <li>Right click on a key: Capture key</li>
<li>Hold Shift while clicking on a key: Remap on all keymaps</li> <li>Hold Shift while clicking on a key: Remap on all keymaps</li>
<li>Hold Alt while clicking on a key: Remap on all layers</li> <li>Hold Alt while clicking on a key: Remap on all layers</li>
<li>Hold Alt to see macro reference counts in the side menu</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -91,6 +91,12 @@
<div class="sidebar__level-2" [routerLinkActive]="['active']"> <div class="sidebar__level-2" [routerLinkActive]="['active']">
<a [routerLink]="['/macro', macro.id]" <a [routerLink]="['/macro', macro.id]"
[class.disabled]="state.updatingFirmware">{{macro.name}}</a> [class.disabled]="state.updatingFirmware">{{macro.name}}</a>
<span *ngIf="state.macroUsageCountVisible"
class="sidebar__macro_count badge"
title="This is the number of times the macro is used across all keymaps."
data-toggle="tooltip"
data-placement="bottom"
data-container="body">{{ macro.usageCount }}</span>
</div> </div>
</li> </li>
</ul> </ul>

View File

@@ -148,6 +148,12 @@ ul {
right: 19px; right: 19px;
top: 3px; top: 3px;
} }
&__macro_count {
position: absolute;
right: 11px;
top: 1px;
}
} }
.menu--bottom { .menu--bottom {

View File

@@ -1 +1,3 @@
export * from './last-edited-key'; export * from './last-edited-key';
export * from './macro-menu-item';
export * from './side-menu-page-state';

View File

@@ -0,0 +1,5 @@
export interface MacroMenuItem {
id: number;
name: string;
usageCount: number;
}

View File

@@ -1,4 +1,5 @@
import { Keymap, Macro } from 'uhk-common'; import { Keymap, Macro } from 'uhk-common';
import { MacroMenuItem } from './macro-menu-item';
export interface SideMenuPageState { export interface SideMenuPageState {
showAddonMenu: boolean; showAddonMenu: boolean;
@@ -6,6 +7,7 @@ export interface SideMenuPageState {
updatingFirmware: boolean; updatingFirmware: boolean;
deviceName: string; deviceName: string;
keymaps: Keymap[]; keymaps: Keymap[];
macros: Macro[]; macros: MacroMenuItem[];
restoreUserConfiguration: boolean; restoreUserConfiguration: boolean;
macroUsageCountVisible: boolean;
} }

View File

@@ -19,7 +19,9 @@ export enum ActionTypes {
SetupPermissionError = '[app] Setup permission error', SetupPermissionError = '[app] Setup permission error',
LoadAppStartInfo = '[app] Load app start info', LoadAppStartInfo = '[app] Load app start info',
StartKeypressCapturing = '[app] Start keypress capturing', StartKeypressCapturing = '[app] Start keypress capturing',
StopKeypressCapturing = '[app] Stop keypress capturing' StopKeypressCapturing = '[app] Stop keypress capturing',
KeyDown = '[app] Key down',
KeyUp = '[app] Key up'
} }
export class AppBootstrappedAction implements Action { export class AppBootstrappedAction implements Action {
@@ -110,6 +112,18 @@ export class StopKeypressCapturingAction implements Action {
type = ActionTypes.StopKeypressCapturing; type = ActionTypes.StopKeypressCapturing;
} }
export class KeyDownAction implements Action {
readonly type = ActionTypes.KeyDown;
constructor(public payload: KeyboardEvent) {}
}
export class KeyUpAction implements Action {
readonly type = ActionTypes.KeyUp;
constructor(public payload: KeyboardEvent) {}
}
export type Actions export type Actions
= AppStartedAction = AppStartedAction
| AppBootstrappedAction | AppBootstrappedAction
@@ -127,4 +141,6 @@ export type Actions
| LoadAppStartInfoAction | LoadAppStartInfoAction
| StartKeypressCapturingAction | StartKeypressCapturingAction
| StopKeypressCapturingAction | StopKeypressCapturingAction
| KeyDownAction
| KeyUpAction
; ;

View File

@@ -1,7 +1,7 @@
import { ActionReducerMap, createSelector, MetaReducer } from '@ngrx/store'; import { ActionReducerMap, createSelector, MetaReducer } from '@ngrx/store';
import { routerReducer, RouterReducerState } from '@ngrx/router-store'; import { routerReducer, RouterReducerState } from '@ngrx/router-store';
import { storeFreeze } from 'ngrx-store-freeze'; import { storeFreeze } from 'ngrx-store-freeze';
import { HardwareModules, Keymap, UserConfiguration } from 'uhk-common'; import { HardwareModules, Keymap, UserConfiguration, PlayMacroAction } from 'uhk-common';
import * as fromUserConfig from './reducers/user-configuration'; import * as fromUserConfig from './reducers/user-configuration';
import * as fromPreset from './reducers/preset'; import * as fromPreset from './reducers/preset';
@@ -16,6 +16,7 @@ import { environment } from '../../environments/environment';
import { RouterStateUrl } from './router-util'; import { RouterStateUrl } from './router-util';
import { PrivilagePageSate } from '../models/privilage-page-sate'; import { PrivilagePageSate } from '../models/privilage-page-sate';
import { isVersionGte } from '../util'; import { isVersionGte } from '../util';
import { SideMenuPageState, MacroMenuItem } from '../models';
// State interface for the application // State interface for the application
export interface AppState { export interface AppState {
@@ -67,6 +68,7 @@ export const deviceConfigurationLoaded = createSelector(appState, fromApp.device
export const getAgentVersionInfo = createSelector(appState, fromApp.getAgentVersionInfo); export const getAgentVersionInfo = createSelector(appState, fromApp.getAgentVersionInfo);
export const getOperatingSystem = createSelector(appState, fromSelectors.getOperatingSystem); export const getOperatingSystem = createSelector(appState, fromSelectors.getOperatingSystem);
export const keypressCapturing = createSelector(appState, fromApp.keypressCapturing); export const keypressCapturing = createSelector(appState, fromApp.keypressCapturing);
export const getMacroUsageCountVisible = createSelector(appState, fromApp.macroUsageCountVisible);
export const runningOnNotSupportedWindows = createSelector(appState, fromApp.runningOnNotSupportedWindows); export const runningOnNotSupportedWindows = createSelector(appState, fromApp.runningOnNotSupportedWindows);
export const contributors = (state: AppState) => state.contributors; export const contributors = (state: AppState) => state.contributors;
export const firmwareUpgradeAllowed = createSelector(runningOnNotSupportedWindows, notSupportedOs => !notSupportedOs); export const firmwareUpgradeAllowed = createSelector(runningOnNotSupportedWindows, notSupportedOs => !notSupportedOs);
@@ -123,25 +125,57 @@ export const getPrivilegePageState = createSelector(appState, getUpdateUdevRules
}; };
}); });
export const getMacroMenuItems = (userConfiguration: UserConfiguration): MacroMenuItem[] => {
const macroMap = userConfiguration.macros.reduce((map, macro) => {
return map.set(macro.id, {
id: macro.id,
name: macro.name,
usageCount: 0
});
}, new Map<number, MacroMenuItem>());
for (const keymap of userConfiguration.keymaps) {
for (const layer of keymap.layers) {
for (const module of layer.modules) {
for (const keyAction of module.keyActions) {
if (!(keyAction instanceof PlayMacroAction)) {
continue;
}
const menuItem = macroMap.get(keyAction.macroId);
menuItem.usageCount++;
}
}
}
}
return Array
.from(macroMap.values())
.sort((first: MacroMenuItem, second: MacroMenuItem) => first.name.localeCompare(second.name));
};
export const getSideMenuPageState = createSelector( export const getSideMenuPageState = createSelector(
showAddonMenu, showAddonMenu,
runningInElectron, runningInElectron,
updatingFirmware, updatingFirmware,
getUserConfiguration, getUserConfiguration,
getRestoreUserConfiguration, getRestoreUserConfiguration,
getMacroUsageCountVisible,
(showAddonMenuValue: boolean, (showAddonMenuValue: boolean,
runningInElectronValue: boolean, runningInElectronValue: boolean,
updatingFirmwareValue: boolean, updatingFirmwareValue: boolean,
userConfiguration: UserConfiguration, userConfiguration: UserConfiguration,
restoreUserConfiguration: boolean) => { restoreUserConfiguration: boolean,
macroUsageCountVisible): SideMenuPageState => {
return { return {
showAddonMenu: showAddonMenuValue, showAddonMenu: showAddonMenuValue,
runInElectron: runningInElectronValue, runInElectron: runningInElectronValue,
updatingFirmware: updatingFirmwareValue, updatingFirmware: updatingFirmwareValue,
deviceName: userConfiguration.deviceName, deviceName: userConfiguration.deviceName,
keymaps: userConfiguration.keymaps, keymaps: userConfiguration.keymaps,
macros: userConfiguration.macros, macros: getMacroMenuItems(userConfiguration),
restoreUserConfiguration restoreUserConfiguration,
macroUsageCountVisible
}; };
} }
); );

View File

@@ -30,6 +30,7 @@ export interface State {
platform?: string; platform?: string;
osVersion?: string; osVersion?: string;
keypressCapturing: boolean; keypressCapturing: boolean;
macroUsageCountVisible: boolean;
} }
export const initialState: State = { export const initialState: State = {
@@ -40,7 +41,8 @@ export const initialState: State = {
configLoading: true, configLoading: true,
agentVersionInfo: getVersions(), agentVersionInfo: getVersions(),
privilegeWhatWillThisDoClicked: false, privilegeWhatWillThisDoClicked: false,
keypressCapturing: false keypressCapturing: false,
macroUsageCountVisible: false
}; };
export function reducer( export function reducer(
@@ -156,7 +158,8 @@ export function reducer(
case App.ActionTypes.StartKeypressCapturing: case App.ActionTypes.StartKeypressCapturing:
return { return {
...state, ...state,
keypressCapturing: true keypressCapturing: true,
macroUsageCountVisible: false
}; };
case App.ActionTypes.StopKeypressCapturing: case App.ActionTypes.StopKeypressCapturing:
@@ -165,6 +168,24 @@ export function reducer(
keypressCapturing: false keypressCapturing: false
}; };
case App.ActionTypes.KeyDown: {
const event = (action as App.KeyDownAction).payload;
return {
...state,
macroUsageCountVisible: !state.keypressCapturing && !event.defaultPrevented && event.altKey
};
}
case App.ActionTypes.KeyUp: {
const event = (action as App.KeyDownAction).payload;
return {
...state,
macroUsageCountVisible: event.altKey
};
}
default: default:
return state; return state;
} }
@@ -197,3 +218,4 @@ export const runningOnNotSupportedWindows = (state: State): boolean => {
}; };
export const keypressCapturing = (state: State): boolean => state.keypressCapturing; export const keypressCapturing = (state: State): boolean => state.keypressCapturing;
export const macroUsageCountVisible = (state: State): boolean => state.macroUsageCountVisible;