diff --git a/packages/uhk-agent/src/services/device.service.ts b/packages/uhk-agent/src/services/device.service.ts index fd2c0a278c3..c5246741619 100644 --- a/packages/uhk-agent/src/services/device.service.ts +++ b/packages/uhk-agent/src/services/device.service.ts @@ -744,7 +744,7 @@ export class DeviceService { this._checkStatusBuffer = true; if (data.saveInHistory) { - await saveUserConfigHistoryAsync(buffer); + await saveUserConfigHistoryAsync(buffer, data.deviceId, data.uniqueId); await this.loadUserConfigFromHistory(event); } diff --git a/packages/uhk-agent/src/util/backup-user-confoguration.ts b/packages/uhk-agent/src/util/backup-user-confoguration.ts index 42cc41a4684..58d66ea40f0 100644 --- a/packages/uhk-agent/src/util/backup-user-confoguration.ts +++ b/packages/uhk-agent/src/util/backup-user-confoguration.ts @@ -4,16 +4,14 @@ import * as path from 'path'; import { BackupUserConfiguration, BackupUserConfigurationInfo, - convertHistoryFilenameToDisplayText, LogService, SaveUserConfigurationData, shouldUpgradeAgent, - UhkBuffer, UserConfiguration, VersionInformation } from 'uhk-common'; -import { getUserConfigFromHistoryAsync } from './get-user-config-from-history-async'; +import { loadUserConfigFromBinaryFile } from './load-user-config-from-binary-file'; import { loadUserConfigHistoryAsync } from './load-user-config-history-async'; export const getBackupUserConfigurationPath = (uniqueId: number): string => { @@ -49,7 +47,7 @@ export async function getBackupUserConfigurationContent(logService: LogService, } } - const fromHistory = await getCompatibleUserConfigFromHistory(logService, versionInformation); + const fromHistory = await getCompatibleUserConfigFromHistory(logService, versionInformation, uniqueId); if (fromHistory) { logService.config('Backup user configuration from history', fromHistory.userConfiguration); return fromHistory; @@ -65,17 +63,18 @@ export async function getBackupUserConfigurationContent(logService: LogService, } } -export async function getCompatibleUserConfigFromHistory(logService: LogService, versionInformation: VersionInformation): Promise { - let files = await loadUserConfigHistoryAsync(); - files = files - .filter(file => path.extname(file) === '.bin') - .sort((a, b) => a.localeCompare(b) * -1); +export async function getCompatibleUserConfigFromHistory(logService: LogService, versionInformation: VersionInformation, uniqueId: number): Promise { + let history = await loadUserConfigHistoryAsync(); + + const deviceHistory = history.devices.find(device => device.uniqueId === uniqueId); + + const files = deviceHistory + ? [...deviceHistory.files, ...history.commonFiles] + : history.commonFiles; for (const file of files) { try { - const content = await getUserConfigFromHistoryAsync(file); - const userConfig = new UserConfiguration(); - userConfig.fromBinary(UhkBuffer.fromArray(content)); + const userConfig = await loadUserConfigFromBinaryFile(file.filePath); if (shouldUpgradeAgent(userConfig.getSemanticVersion(), false, versionInformation?.userConfigVersion)) { continue; @@ -84,7 +83,7 @@ export async function getCompatibleUserConfigFromHistory(logService: LogService, return { info: BackupUserConfigurationInfo.EarlierCompatible, userConfiguration: userConfig.toJsonObject(), - date: convertHistoryFilenameToDisplayText(file) + date: file.timestamp, }; } catch (error) { logService.error('Cannot parse backup user config from history', error); diff --git a/packages/uhk-agent/src/util/get-user-config-from-history-async.ts b/packages/uhk-agent/src/util/get-user-config-from-history-async.ts index 89685627b6c..c4f06430944 100644 --- a/packages/uhk-agent/src/util/get-user-config-from-history-async.ts +++ b/packages/uhk-agent/src/util/get-user-config-from-history-async.ts @@ -1,14 +1,7 @@ -import { readFile } from 'fs'; -import { join } from 'path'; -import { promisify } from 'util'; - -import { getUserConfigHistoryDirAsync } from './get-user-config-history-dir-async'; - -const readFileAsync = promisify(readFile); +import { readFile } from 'node:fs/promises'; export async function getUserConfigFromHistoryAsync(filename: string): Promise> { - const filePath = join(await getUserConfigHistoryDirAsync(), filename); - const buffer = await readFileAsync(filePath); + const buffer = await readFile(filename); return [...buffer]; } diff --git a/packages/uhk-agent/src/util/index.ts b/packages/uhk-agent/src/util/index.ts index 277ae158736..526837a9b30 100644 --- a/packages/uhk-agent/src/util/index.ts +++ b/packages/uhk-agent/src/util/index.ts @@ -9,6 +9,7 @@ export * from './get-updater-logger'; export * from './get-user-config-from-history-async'; export * from './get-user-config-history-dir-async'; export * from './get-window-background-color'; +export * from './load-user-config-from-binary-file'; export * from './load-user-config-history-async'; export * from './make-folder-writeable-to-user-on-linux'; export * from './print-usb-devices'; diff --git a/packages/uhk-agent/src/util/load-user-config-from-binary-file.ts b/packages/uhk-agent/src/util/load-user-config-from-binary-file.ts new file mode 100644 index 00000000000..60a842e6de2 --- /dev/null +++ b/packages/uhk-agent/src/util/load-user-config-from-binary-file.ts @@ -0,0 +1,17 @@ +import { readFile } from 'node:fs/promises'; +import { UhkBuffer, UserConfiguration } from "uhk-common"; + +/** + * Load user configuration history from a binary file. + * + * @param filePath - The path to the binary file. + * @returns The user configuration. + */ +export async function loadUserConfigFromBinaryFile(filePath:string): Promise { + const buffer = await readFile(filePath); + const userConfig = new UserConfiguration(); + + userConfig.fromBinary(UhkBuffer.fromArray([...buffer])); + + return userConfig; +} diff --git a/packages/uhk-agent/src/util/load-user-config-history-async.ts b/packages/uhk-agent/src/util/load-user-config-history-async.ts index 768767f5e61..e8eaac7ac0d 100644 --- a/packages/uhk-agent/src/util/load-user-config-history-async.ts +++ b/packages/uhk-agent/src/util/load-user-config-history-async.ts @@ -1,12 +1,84 @@ -import { readdir } from 'fs'; -import { promisify } from 'util'; +import { readdir, stat } from 'node:fs/promises'; +import path from 'node:path'; +import { convertHistoryFilenameToDisplayText } from 'uhk-common'; +import { getMd5HashFromFilename } from 'uhk-common'; +import { + DeviceUserConfigHistory, + sortStringDesc, + UHK_DEVICES, + UserConfigHistory, +} from 'uhk-common'; import { getUserConfigHistoryDirAsync } from './get-user-config-history-dir-async'; +import { loadUserConfigFromBinaryFile } from './load-user-config-from-binary-file'; -const readdirAsync = promisify(readdir); +export async function loadUserConfigHistoryAsync(): Promise { + const history: UserConfigHistory = { + commonFiles: [], + devices: [] + }; -export async function loadUserConfigHistoryAsync(): Promise> { - const files = await readdirAsync(await getUserConfigHistoryDirAsync()); + const userConfigHistoryDir = await getUserConfigHistoryDirAsync(); + const entries = await readdir(userConfigHistoryDir); - return files; + for (const entry of entries) { + const filePath = path.join(userConfigHistoryDir, entry); + const entryStat = await stat(filePath); + + if (entryStat.isFile()) { + if (path.extname(entry) === '.bin') { + history.commonFiles.push({ + filePath, + md5Hash: getMd5HashFromFilename(entry), + timestamp: convertHistoryFilenameToDisplayText(entry), + }); + } + } else if (entryStat.isDirectory()) { + const entrySplit = entry.split('-'); + + if (entrySplit.length !== 2) { + continue; + } + + const deviceId = Number.parseInt(entrySplit[1], 10); + + if (isNaN(deviceId)) { + continue; + } + + const deviceHistoryDir = path.join(userConfigHistoryDir, entry); + const deviceHistory: DeviceUserConfigHistory = { + uniqueId: Number.parseInt(entrySplit[0], 10), + device: UHK_DEVICES.find(device => device.id === deviceId), + deviceName: '', + files: (await readdir(deviceHistoryDir)) + .filter(file => path.extname(file) === '.bin') + .sort(sortStringDesc) + .map(file => { + return { + filePath: path.join(deviceHistoryDir, file), + md5Hash: getMd5HashFromFilename(file), + timestamp: convertHistoryFilenameToDisplayText(file), + }; + }), + }; + + for (const file of deviceHistory.files) { + try { + const userConfig = await loadUserConfigFromBinaryFile(file.filePath); + deviceHistory.deviceName = userConfig.deviceName; + break; + } catch { + // Maybe the user config is newer than Agent supports, or corrupted. + } + } + + history.devices.push(deviceHistory); + } + } + + history.commonFiles + .sort((a, b) => sortStringDesc(a.timestamp, b.timestamp)); + + return history; } diff --git a/packages/uhk-agent/src/util/save-user-config-history-async.ts b/packages/uhk-agent/src/util/save-user-config-history-async.ts index 36cb9f08d67..6f7b4aa05e1 100644 --- a/packages/uhk-agent/src/util/save-user-config-history-async.ts +++ b/packages/uhk-agent/src/util/save-user-config-history-async.ts @@ -1,16 +1,18 @@ -import { writeFile } from 'fs'; -import { join } from 'path'; -import { promisify } from 'util'; +import { ensureDir } from 'fs-extra'; +import { writeFile } from 'node:fs/promises'; +import { join } from 'node:path'; import { createMd5Hash, getUserConfigHistoryFilename, Buffer } from 'uhk-common'; import { getUserConfigHistoryDirAsync } from './get-user-config-history-dir-async'; -const writeFileAsync = promisify(writeFile); -export async function saveUserConfigHistoryAsync(buffer: Buffer): Promise { +export async function saveUserConfigHistoryAsync(buffer: Buffer, deviceId: number, uniqueId: number): Promise { + const deviceDir = `${uniqueId}-${deviceId}`; + const deviceDirPath = join(await getUserConfigHistoryDirAsync(), deviceDir); + await ensureDir(deviceDirPath); const md5Hash = createMd5Hash(buffer); const filename = getUserConfigHistoryFilename(md5Hash); - const filePath = join(await getUserConfigHistoryDirAsync(), filename); + const filePath = join(deviceDirPath, filename); - return writeFileAsync(filePath, buffer, { encoding: 'ascii' }); + return writeFile(filePath, buffer, { encoding: 'ascii' }); } diff --git a/packages/uhk-common/src/models/index.ts b/packages/uhk-common/src/models/index.ts index 95e6c0d4e5b..db6109c2842 100644 --- a/packages/uhk-common/src/models/index.ts +++ b/packages/uhk-common/src/models/index.ts @@ -29,4 +29,5 @@ export * from './udev-rules-info.js'; export * from './uhk-products.js'; export * from './update-firmware-data.js'; export * from './upload-file-data.js'; +export * from './user-config-history.js'; export * from './halves-info.js'; diff --git a/packages/uhk-common/src/models/save-user-configuration-data.ts b/packages/uhk-common/src/models/save-user-configuration-data.ts index ec0a3ed83aa..79a186bd8e3 100644 --- a/packages/uhk-common/src/models/save-user-configuration-data.ts +++ b/packages/uhk-common/src/models/save-user-configuration-data.ts @@ -1,4 +1,11 @@ export interface SaveUserConfigurationData { + /** + * UHK device product id. + */ + deviceId: number; + /** + * The unique identifier of the UHK keyboard. + */ uniqueId: number; configuration: string; saveInHistory: boolean; diff --git a/packages/uhk-common/src/models/user-config-history.ts b/packages/uhk-common/src/models/user-config-history.ts new file mode 100644 index 00000000000..67c653334a5 --- /dev/null +++ b/packages/uhk-common/src/models/user-config-history.ts @@ -0,0 +1,27 @@ +import { UhkDeviceProduct } from './uhk-products'; + +export interface HistoryFileInfo { + filePath: string; + md5Hash: string; + timestamp: string; +} + +export interface DeviceUserConfigHistory { + uniqueId: number; + device: UhkDeviceProduct; + /** + * Device name from the latest user configuration. + */ + deviceName: string; + files: HistoryFileInfo[]; +} + +export interface UserConfigHistory { + /** + * Files in the root of the history directory. + * These files are common for all devices, because we introduced the device specific history directories later. + * We show the common files in the UI for every device. + */ + commonFiles: HistoryFileInfo[]; + devices: DeviceUserConfigHistory[]; +} diff --git a/packages/uhk-common/src/util/get-md5-hash-from-file-name.ts b/packages/uhk-common/src/util/get-md5-hash-from-file-name.ts index bee775d5c23..d77a36f13b1 100644 --- a/packages/uhk-common/src/util/get-md5-hash-from-file-name.ts +++ b/packages/uhk-common/src/util/get-md5-hash-from-file-name.ts @@ -1,3 +1,3 @@ export function getMd5HashFromFilename(filename: string): string { - return filename.substr(16, 32); + return filename.substring(16, 48); } diff --git a/packages/uhk-web/src/app/components/popover/popover.component.scss b/packages/uhk-web/src/app/components/popover/popover.component.scss index 6cfc219d2c7..3891f1fd26a 100644 --- a/packages/uhk-web/src/app/components/popover/popover.component.scss +++ b/packages/uhk-web/src/app/components/popover/popover.component.scss @@ -29,15 +29,6 @@ } } -.nav-tabs > li { - overflow: hidden; - cursor: pointer; - - &.disabled { - cursor: not-allowed; - } -} - .arrowCustom { position: absolute; top: -15px; diff --git a/packages/uhk-web/src/app/components/user-configuration-history/user-configuration-history.component.html b/packages/uhk-web/src/app/components/user-configuration-history/user-configuration-history.component.html index 2f66647bd05..dacb1c6ca41 100644 --- a/packages/uhk-web/src/app/components/user-configuration-history/user-configuration-history.component.html +++ b/packages/uhk-web/src/app/components/user-configuration-history/user-configuration-history.component.html @@ -10,16 +10,29 @@

Loading... -
+
No configurations were saved yet.
- + +
    +
  • - {{ fileInfo.file | userConfigHistory }} + {{ fileInfo.timestamp }} (); + selectedTabIndex = 0; + trackByFn(index: number, key: HistoryFileInfo): string { return key.file; } + + onSelectTab(index: number): void { + this.selectedTabIndex = index; + } } diff --git a/packages/uhk-web/src/app/models/user-config-history-component-state.ts b/packages/uhk-web/src/app/models/user-config-history-component-state.ts index 32aa5d0fd06..d50323c2456 100644 --- a/packages/uhk-web/src/app/models/user-config-history-component-state.ts +++ b/packages/uhk-web/src/app/models/user-config-history-component-state.ts @@ -11,10 +11,19 @@ export interface HistoryFileInfo { */ displayText: string; showRestore: boolean; + /** + * The timestamp of the saved user configuration + */ + timestamp: string; +} + +export interface Tab { + displayText: string; + files: HistoryFileInfo[]; } export interface UserConfigHistoryComponentState { - files: Array; + tabs: Tab[]; loading: boolean; disabled: boolean; } diff --git a/packages/uhk-web/src/app/pipes/index.ts b/packages/uhk-web/src/app/pipes/index.ts index f0fc342d7f4..f027fa24850 100644 --- a/packages/uhk-web/src/app/pipes/index.ts +++ b/packages/uhk-web/src/app/pipes/index.ts @@ -3,4 +3,3 @@ export { NewLineToBrPipe } from './new-line-to-br.pipe'; export { SafeHtmlPipe } from './safe-html.pipe'; export { SafeStylePipe } from './safe-style.pipe'; export { SafeUrlPipe } from './safe-url.pipe'; -export { UserConfigHistoryDisplayTextPipe } from './user-config-history-display-text.pipe'; diff --git a/packages/uhk-web/src/app/pipes/user-config-history-display-text.pipe.ts b/packages/uhk-web/src/app/pipes/user-config-history-display-text.pipe.ts deleted file mode 100644 index afa1c1ef279..00000000000 --- a/packages/uhk-web/src/app/pipes/user-config-history-display-text.pipe.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core'; -import { convertHistoryFilenameToDisplayText } from 'uhk-common'; - -@Pipe({ - name: 'userConfigHistory' -}) -export class UserConfigHistoryDisplayTextPipe implements PipeTransform { - - transform(filename: string): string { - return convertHistoryFilenameToDisplayText(filename); - } -} diff --git a/packages/uhk-web/src/app/services/device-renderer.service.ts b/packages/uhk-web/src/app/services/device-renderer.service.ts index 35dd7bb8ccc..57e1071eee7 100644 --- a/packages/uhk-web/src/app/services/device-renderer.service.ts +++ b/packages/uhk-web/src/app/services/device-renderer.service.ts @@ -15,6 +15,7 @@ import { UpdateFirmwareData, UploadFileData, UserConfiguration, + UserConfigHistory, VersionInformation } from 'uhk-common'; @@ -162,7 +163,7 @@ export class DeviceRendererService { this.dispachStoreAction(new ReadConfigSizesReplyAction(JSON.parse(response))); }); - this.ipcRenderer.on(IpcEvents.device.loadUserConfigHistoryReply, (event: string, response: Array) => { + this.ipcRenderer.on(IpcEvents.device.loadUserConfigHistoryReply, (event: string, response: UserConfigHistory) => { this.dispachStoreAction(new LoadUserConfigurationHistorySuccessAction(response)); }); diff --git a/packages/uhk-web/src/app/shared.module.ts b/packages/uhk-web/src/app/shared.module.ts index 5727552e4c0..a34ad6a2736 100644 --- a/packages/uhk-web/src/app/shared.module.ts +++ b/packages/uhk-web/src/app/shared.module.ts @@ -94,7 +94,7 @@ import { appRoutingProviders, routing } from './app.routes'; import { UhkAgentIconComponent } from './components/uhk-icon/uhk-agent-icon.component'; import { CancelableDirective, ExternalUrlDirective } from './directives'; -import { AsHexColorPipe, NewLineToBrPipe, SafeHtmlPipe, SafeStylePipe, SafeUrlPipe, UserConfigHistoryDisplayTextPipe } from './pipes'; +import { AsHexColorPipe, NewLineToBrPipe, SafeHtmlPipe, SafeStylePipe, SafeUrlPipe } from './pipes'; import { CaptureService } from './services/capture.service'; import { MapperService } from './services/mapper.service'; @@ -268,7 +268,6 @@ import appInitFactory from './services/app-init-factory'; OutOfSpaceWarningComponent, SmartMacroDocDirective, UserConfigurationHistoryComponent, - UserConfigHistoryDisplayTextPipe ], imports: [ AngularSplitModule, diff --git a/packages/uhk-web/src/app/store/actions/user-configuration-history.actions.ts b/packages/uhk-web/src/app/store/actions/user-configuration-history.actions.ts index c69e73d740b..ffb3cfcf58b 100644 --- a/packages/uhk-web/src/app/store/actions/user-configuration-history.actions.ts +++ b/packages/uhk-web/src/app/store/actions/user-configuration-history.actions.ts @@ -1,4 +1,5 @@ import { Action } from '@ngrx/store'; +import { UserConfigHistory } from 'uhk-common'; export enum ActionTypes { LoadUserConfigurationHistory = '[user-config] Load user configuration history', @@ -13,7 +14,7 @@ export class LoadUserConfigurationHistoryAction implements Action { export class LoadUserConfigurationHistorySuccessAction implements Action { type = ActionTypes.LoadUserConfigurationHistorySuccess; - constructor(public payload: Array) { + constructor(public payload: UserConfigHistory) { } } diff --git a/packages/uhk-web/src/app/store/effects/device.ts b/packages/uhk-web/src/app/store/effects/device.ts index dfd82015048..08eb281bd72 100644 --- a/packages/uhk-web/src/app/store/effects/device.ts +++ b/packages/uhk-web/src/app/store/effects/device.ts @@ -477,6 +477,7 @@ export class DeviceEffects { saveInHistory: boolean): void { this.deviceRendererService.saveUserConfiguration({ saveInHistory, + deviceId: hardwareConfig && hardwareConfig.deviceId, uniqueId: hardwareConfig && hardwareConfig.uniqueId, configuration: userConfiguration.toJsonObject() }); diff --git a/packages/uhk-web/src/app/store/index.ts b/packages/uhk-web/src/app/store/index.ts index e74fb439b98..62d967371ca 100644 --- a/packages/uhk-web/src/app/store/index.ts +++ b/packages/uhk-web/src/app/store/index.ts @@ -5,7 +5,11 @@ import { gt } from 'semver'; import { BacklightingMode, Constants, + UHK_DEVICES, + UHK_60_V2_DEVICE, FirmwareRepoInfo, + HardwareConfiguration, + HistoryFileInfo as CommonHistoryFileInfo, LayerName, LEFT_KEY_CLUSTER_MODULE, RIGHT_TRACKPOINT_MODULE, @@ -36,6 +40,7 @@ import { ConfigSizeState, DeviceUiStates, FirmwareUpgradeState, + HistoryFileInfo, MacroMenuItem, ModuleFirmwareUpgradeStates, OutOfSpaceWarningData, @@ -169,6 +174,7 @@ export const appState = (state: AppState) => state.app; export const disableUpdateAgentProtection = createSelector(appState, fromApp.disableUpdateAgentProtection); export const getErrorPanelHeight = createSelector(appState, fromApp.getErrorPanelHeight); export const getUndoableNotification = createSelector(appState, fromApp.getUndoableNotification); +export const getHardwareConfiguration = createSelector(appState, fromApp.getHardwareConfiguration); export const getPrevUserConfiguration = createSelector(appState, fromApp.getPrevUserConfiguration); export const runningInElectron = createSelector(appState, fromApp.runningInElectron); export const getDeviceId = createSelector(appState, fromApp.getDeviceId); @@ -539,38 +545,69 @@ export const getShowFirmwareUpgradePanel = createSelector( export const getUserConfigHistoryState = (state: AppState) => state.userConfigurationHistory; export const getUserConfigHistoryComponentState = createSelector( runningInElectron, + getHardwareConfiguration, getUserConfigHistoryState, + getUserConfiguration, getMd5HasOfUserConfig, isUserConfigSaving, (inElectron, + hardwareConfig: HardwareConfiguration, state: fromUserConfigHistory.State, + userConfig: UserConfiguration, md5Hash: string, saving: boolean): UserConfigHistoryComponentState => { let foundFirstCurrent = false; - return { - loading: inElectron && state.loading, - files: state.files.map(x => { - const showRestore = getMd5HashFromFilename(x) !== md5Hash; - let displayText: string; - - if (showRestore) { - displayText = 'Restore'; - } else if (foundFirstCurrent) { - displayText = 'Same as current'; - } else { - displayText = 'Current'; - foundFirstCurrent = true; - } + function fileMapper(file: CommonHistoryFileInfo): HistoryFileInfo { + const showRestore = file.md5Hash !== md5Hash; + let displayText: string; + + if (showRestore) { + displayText = 'Restore'; + } else if (foundFirstCurrent) { + displayText = 'Same as current'; + } else { + displayText = 'Current'; + foundFirstCurrent = true; + } + return { + timestamp: file.timestamp, + displayText, + showRestore, + file: file.filePath + }; + } + + const result = { + loading: inElectron && state.loading, + tabs: state.userConfigHistory.devices.map(device => { return { - displayText, - showRestore, - file: x + displayText: `${device.deviceName} (${device.device.name})`, + files: [...device.files, ...state.userConfigHistory.commonFiles].map(fileMapper), }; }), disabled: saving }; + + const currentDeviceHasHistory = state.userConfigHistory.devices.find(device => device.uniqueId === hardwareConfig?.uniqueId); + + if (result.tabs.length === 0 || !currentDeviceHasHistory) { + let deviceName = UHK_60_V2_DEVICE.name; + if (hardwareConfig) { + const uhkDevice = UHK_DEVICES.find(device => device.id === hardwareConfig.deviceId); + deviceName = uhkDevice ? uhkDevice.name : deviceName; + } + + result.tabs.push({ + displayText: `${userConfig.deviceName} (${deviceName})`, + files: state.userConfigHistory.commonFiles.map(fileMapper) + }); + } + + result.tabs.sort((a, b) => a.displayText.localeCompare(b.displayText)); + + return result; }); export const getSupportedThemes = (): AppThemeSelect[] => { diff --git a/packages/uhk-web/src/app/store/reducers/app.reducer.ts b/packages/uhk-web/src/app/store/reducers/app.reducer.ts index 8a13bb0bc57..6995726c923 100644 --- a/packages/uhk-web/src/app/store/reducers/app.reducer.ts +++ b/packages/uhk-web/src/app/store/reducers/app.reducer.ts @@ -256,4 +256,5 @@ export const getEverAttemptedSavingToKeyboard = (state: State): boolean => state export const getUdevFileContent = (state: State): string => state.udevFileContent; export const getAnimationEnabled = (state: State): boolean => state.animationEnabled; export const getAppTheme = (state: State): AppTheme => state.appTheme; +export const getHardwareConfiguration = (state: State): HardwareConfiguration => state.hardwareConfig; export const getPlatform = (state: State): string => state.platform; diff --git a/packages/uhk-web/src/app/store/reducers/user-configuration-history.reducer.ts b/packages/uhk-web/src/app/store/reducers/user-configuration-history.reducer.ts index 2ecf62d42ff..13e78397eed 100644 --- a/packages/uhk-web/src/app/store/reducers/user-configuration-history.reducer.ts +++ b/packages/uhk-web/src/app/store/reducers/user-configuration-history.reducer.ts @@ -1,14 +1,23 @@ -import { sortStringDesc } from 'uhk-common'; +import { + UserConfigHistory, +} from 'uhk-common'; import { Actions, ActionTypes, LoadUserConfigurationHistorySuccessAction } from '../actions/user-configuration-history.actions'; export interface State { - files: Array; + userConfigHistory: UserConfigHistory; loading: boolean; } +function defaultUserConfigHistory(): UserConfigHistory { + return { + commonFiles: [], + devices: [] + }; +} + export const initialState: State = { - files: [], + userConfigHistory: defaultUserConfigHistory(), loading: false }; @@ -19,14 +28,14 @@ export function reducer(state = initialState, action: Actions) { return { ...state, loading: true, - files: [] + userConfigHistory: defaultUserConfigHistory() }; case ActionTypes.LoadUserConfigurationHistorySuccess: return { ...state, loading: false, - files: [...(action as LoadUserConfigurationHistorySuccessAction).payload].sort(sortStringDesc) + userConfigHistory: (action as LoadUserConfigurationHistorySuccessAction).payload }; default: diff --git a/packages/uhk-web/src/styles/_global.scss b/packages/uhk-web/src/styles/_global.scss index 5a52c306499..0d189662260 100644 --- a/packages/uhk-web/src/styles/_global.scss +++ b/packages/uhk-web/src/styles/_global.scss @@ -307,3 +307,12 @@ mwl-confirmation-popover-window { text-decoration: underline; text-decoration-style: dotted; } + +.nav-tabs > li { + overflow: hidden; + cursor: pointer; + + &.disabled { + cursor: not-allowed; + } +}