mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Remove BaseStore<T> and replace with composition of dependencies (#7002)
* Rename ExtensionStore -> BaseExtensionStore - The name was too close to ExtensionsStore Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move ExtensionsStore to new format Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move ClusterStore to new format Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move UserStore to new format Signed-off-by: Sebastian Malton <sebastian@malton.name> * Cleanup types to remove multiple cast locations Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move HotbarStore to new format Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move WeblinkStore to new format Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move FileSystemProvisionerStore to new format Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update snapshots Signed-off-by: Sebastian Malton <sebastian@malton.name> * Clean up impl and rename to better describe intent Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix remaining type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully split apart the enabled extensions storage Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully split apart the clusters storage Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully split apart the hotbar storage Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully split apart the weblinks storage Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully split apart the user preferences storage Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix crashing Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix tests and snapshots Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix integration test failures Signed-off-by: Sebastian Malton <sebastian@malton.name> * Improve typing to prevent errors in the future. Signed-off-by: Sebastian Malton <sebastian@malton.name> * Cleanup @k8slens/messaging and friends - To fix type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix lint issue Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix global override not being complete enough causing tests to fail Signed-off-by: Sebastian Malton <sebastian@malton.name> * Bump memory for unit tests on CI Signed-off-by: Sebastian Malton <sebastian@malton.name> * Attempt to fix memory issue on CI again Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fixup test because of new injectables Signed-off-by: Sebastian Malton <sebastian@malton.name> * Upgrade Jest Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix unit tests falling over Signed-off-by: Sebastian Malton <sebastian@malton.name> * Back out jest config change Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove console log Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update snapshot Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix tests by matching equality instead of snapshots Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix tests by forcing specific snapshot style - Ubuntu CI seems to format arrays in snapshots differently than macOS locally Signed-off-by: Sebastian Malton <sebastian@malton.name> --------- Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
ba4a283af9
commit
49f0a1af9c
365 files changed
+6733
-3972
No files matched your search
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import userPreferencesStateInjectable from "./state.injectable";
|
||||
|
||||
const httpsProxyConfigurationInjectable = getInjectable({
|
||||
id: "https-proxy-configuration",
|
||||
instantiate: (di) => {
|
||||
const userStore = di.inject(userPreferencesStateInjectable);
|
||||
|
||||
return computed(() => userStore.httpsProxy);
|
||||
},
|
||||
});
|
||||
|
||||
export default httpsProxyConfigurationInjectable;
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import userPreferencesStateInjectable from "./state.injectable";
|
||||
|
||||
/**
|
||||
* Checks if a column (by ID) for a table (by ID) is configured to be hidden
|
||||
* @param tableId The ID of the table to be checked against
|
||||
* @param columnIds The list of IDs the check if one is hidden
|
||||
* @returns true if at least one column under the table is set to hidden
|
||||
*/
|
||||
export type IsTableColumnHidden = (tableId: string, ...columnIds: (string | undefined)[]) => boolean;
|
||||
|
||||
const isTableColumnHiddenInjectable = getInjectable({
|
||||
id: "is-table-column-hidden",
|
||||
instantiate: (di): IsTableColumnHidden => {
|
||||
const state = di.inject(userPreferencesStateInjectable);
|
||||
|
||||
return (tableId, ...columnIds) => {
|
||||
if (columnIds.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const config = state.hiddenTableColumns.get(tableId);
|
||||
|
||||
if (!config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return columnIds.some(columnId => columnId && config.has(columnId));
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default isTableColumnHiddenInjectable;
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import userPreferencesStateInjectable from "./state.injectable";
|
||||
|
||||
const kubeconfigSyncsInjectable = getInjectable({
|
||||
id: "kubeconfig-syncs",
|
||||
instantiate: (di) => di.inject(userPreferencesStateInjectable).syncKubeconfigEntries,
|
||||
});
|
||||
|
||||
export default kubeconfigSyncsInjectable;
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import userPreferencesStateInjectable from "./state.injectable";
|
||||
|
||||
export type LensColorThemePreference = {
|
||||
useSystemTheme: true;
|
||||
} | {
|
||||
useSystemTheme: false;
|
||||
lensThemeId: string;
|
||||
};
|
||||
|
||||
const lensColorThemePreferenceInjectable = getInjectable({
|
||||
id: "lens-color-theme-preference",
|
||||
instantiate: (di) => {
|
||||
const state = di.inject(userPreferencesStateInjectable);
|
||||
|
||||
return computed((): LensColorThemePreference => {
|
||||
// TODO: remove magic strings
|
||||
if (state.colorTheme === "system") {
|
||||
return {
|
||||
useSystemTheme: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
useSystemTheme: false,
|
||||
lensThemeId: state.colorTheme,
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default lensColorThemePreferenceInjectable;
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { MigrationDeclaration } from "../../../common/persistent-storage/migrations.injectable";
|
||||
|
||||
export const userPreferencesMigrationInjectionToken = getInjectionToken<MigrationDeclaration>({
|
||||
id: "user-preferences-migration-token",
|
||||
});
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { merge } from "lodash";
|
||||
import type { ObservableMap } from "mobx";
|
||||
import { observable } from "mobx";
|
||||
import homeDirectoryPathInjectable from "../../../common/os/home-directory-path.injectable";
|
||||
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
|
||||
import { defaultThemeId } from "../../../common/vars";
|
||||
import currentTimezoneInjectable from "../../../common/vars/current-timezone.injectable";
|
||||
import type { EditorConfiguration, ExtensionRegistry, KubeconfigSyncEntry, KubeconfigSyncValue, TerminalConfig } from "./preferences-helpers";
|
||||
import { defaultExtensionRegistryUrlLocation, defaultEditorConfig, defaultTerminalConfig, defaultPackageMirror, getPreferenceDescriptor, packageMirrors } from "./preferences-helpers";
|
||||
|
||||
export type PreferenceDescriptors = ReturnType<typeof userPreferenceDescriptorsInjectable["instantiate"]>;
|
||||
|
||||
const userPreferenceDescriptorsInjectable = getInjectable({
|
||||
id: "user-preference-descriptors",
|
||||
instantiate: (di) => {
|
||||
const currentTimezone = di.inject(currentTimezoneInjectable);
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
const homeDirectoryPath = di.inject(homeDirectoryPathInjectable);
|
||||
|
||||
const mainKubeFolderPath = joinPaths(homeDirectoryPath, ".kube");
|
||||
|
||||
return ({
|
||||
httpsProxy: getPreferenceDescriptor<string | undefined>({
|
||||
fromStore: val => val,
|
||||
toStore: val => val || undefined,
|
||||
}),
|
||||
shell: getPreferenceDescriptor<string | undefined>({
|
||||
fromStore: val => val,
|
||||
toStore: val => val || undefined,
|
||||
}),
|
||||
colorTheme: getPreferenceDescriptor<string>({
|
||||
fromStore: val => val || defaultThemeId,
|
||||
toStore: val => !val || val === defaultThemeId
|
||||
? undefined
|
||||
: val,
|
||||
}),
|
||||
terminalTheme: getPreferenceDescriptor<string>({
|
||||
fromStore: val => val || "",
|
||||
toStore: val => val || undefined,
|
||||
}),
|
||||
localeTimezone: getPreferenceDescriptor<string>({
|
||||
fromStore: val => val || currentTimezone,
|
||||
toStore: val => !val || val === currentTimezone
|
||||
? undefined
|
||||
: val,
|
||||
}),
|
||||
allowUntrustedCAs: getPreferenceDescriptor<boolean>({
|
||||
fromStore: val => val ?? false,
|
||||
toStore: val => !val
|
||||
? undefined
|
||||
: val,
|
||||
}),
|
||||
allowErrorReporting: getPreferenceDescriptor<boolean>({
|
||||
fromStore: val => val ?? true,
|
||||
toStore: val => val
|
||||
? undefined
|
||||
: val,
|
||||
}),
|
||||
downloadMirror: getPreferenceDescriptor<string>({
|
||||
fromStore: val => !val || !packageMirrors.has(val)
|
||||
? defaultPackageMirror
|
||||
: val,
|
||||
toStore: val => val === defaultPackageMirror
|
||||
? undefined
|
||||
: val,
|
||||
}),
|
||||
downloadKubectlBinaries: getPreferenceDescriptor<boolean>({
|
||||
fromStore: val => val ?? true,
|
||||
toStore: val => val
|
||||
? undefined
|
||||
: val,
|
||||
}),
|
||||
downloadBinariesPath: getPreferenceDescriptor<string | undefined>({
|
||||
fromStore: val => val,
|
||||
toStore: val => val || undefined,
|
||||
}),
|
||||
kubectlBinariesPath: getPreferenceDescriptor<string | undefined>({
|
||||
fromStore: val => val,
|
||||
toStore: val => val || undefined,
|
||||
}),
|
||||
openAtLogin: getPreferenceDescriptor<boolean>({
|
||||
fromStore: val => val ?? false,
|
||||
toStore: val => !val
|
||||
? undefined
|
||||
: val,
|
||||
}),
|
||||
terminalCopyOnSelect: getPreferenceDescriptor<boolean>({
|
||||
fromStore: val => val ?? false,
|
||||
toStore: val => !val
|
||||
? undefined
|
||||
: val,
|
||||
}),
|
||||
hiddenTableColumns: getPreferenceDescriptor<[string, string[]][], Map<string, Set<string>>>({
|
||||
fromStore: (val = []) => new Map(
|
||||
val.map(([tableId, columnIds]) => [tableId, new Set(columnIds)]),
|
||||
),
|
||||
toStore: (val) => {
|
||||
const res: [string, string[]][] = [];
|
||||
|
||||
for (const [table, columns] of val) {
|
||||
if (columns.size) {
|
||||
res.push([table, Array.from(columns)]);
|
||||
}
|
||||
}
|
||||
|
||||
return res.length ? res : undefined;
|
||||
},
|
||||
}),
|
||||
syncKubeconfigEntries: getPreferenceDescriptor<KubeconfigSyncEntry[], ObservableMap<string, KubeconfigSyncValue>>({
|
||||
fromStore: val => observable.map(
|
||||
val?.map(({ filePath, ...rest }) => [filePath, rest])
|
||||
?? [[mainKubeFolderPath, {}]],
|
||||
),
|
||||
toStore: val => val.size === 1 && val.has(mainKubeFolderPath)
|
||||
? undefined
|
||||
: Array.from(val, ([filePath, rest]) => ({ filePath, ...rest })),
|
||||
}),
|
||||
editorConfiguration: getPreferenceDescriptor<Partial<EditorConfiguration>, EditorConfiguration>({
|
||||
fromStore: val => merge(defaultEditorConfig, val),
|
||||
toStore: val => val,
|
||||
}),
|
||||
terminalConfig: getPreferenceDescriptor<Partial<TerminalConfig>, TerminalConfig>({
|
||||
fromStore: val => merge(defaultTerminalConfig, val),
|
||||
toStore: val => val,
|
||||
}),
|
||||
extensionRegistryUrl: getPreferenceDescriptor<ExtensionRegistry>({
|
||||
fromStore: val => val ?? {
|
||||
location: defaultExtensionRegistryUrlLocation,
|
||||
},
|
||||
toStore: val => val.location === defaultExtensionRegistryUrlLocation
|
||||
? undefined
|
||||
: val,
|
||||
}),
|
||||
}) as const;
|
||||
},
|
||||
});
|
||||
|
||||
export default userPreferenceDescriptorsInjectable;
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { editor } from "monaco-editor";
|
||||
import { defaultFontSize, defaultTerminalFontFamily, defaultEditorFontFamily } from "../../../common/vars";
|
||||
import type { PreferenceDescriptors } from "./preference-descriptors.injectable";
|
||||
|
||||
export interface KubeconfigSyncEntry extends KubeconfigSyncValue {
|
||||
filePath: string;
|
||||
}
|
||||
|
||||
export interface KubeconfigSyncValue {
|
||||
}
|
||||
export interface TerminalConfig {
|
||||
fontSize: number;
|
||||
fontFamily: string;
|
||||
}
|
||||
|
||||
export const defaultTerminalConfig: TerminalConfig = {
|
||||
fontSize: defaultFontSize,
|
||||
fontFamily: defaultTerminalFontFamily,
|
||||
};
|
||||
|
||||
export interface BaseEditorConfiguration extends Required<Pick<editor.IStandaloneEditorConstructionOptions, "minimap" | "tabSize" | "fontSize" | "fontFamily">> {
|
||||
lineNumbers: NonNullable<Exclude<editor.IStandaloneEditorConstructionOptions["lineNumbers"], Function>>;
|
||||
}
|
||||
|
||||
export type EditorConfiguration = Required<BaseEditorConfiguration>;
|
||||
|
||||
export const defaultEditorConfig: EditorConfiguration = {
|
||||
tabSize: 2,
|
||||
lineNumbers: "on",
|
||||
fontSize: defaultFontSize,
|
||||
fontFamily: defaultEditorFontFamily,
|
||||
minimap: {
|
||||
enabled: true,
|
||||
side: "right",
|
||||
},
|
||||
};
|
||||
|
||||
export type StoreType<P> = P extends PreferenceDescription<unknown, infer Store>
|
||||
? Store
|
||||
: never;
|
||||
|
||||
export interface PreferenceDescription<T, R = T> {
|
||||
fromStore(val: T | undefined): R;
|
||||
toStore(val: R): T | undefined;
|
||||
}
|
||||
|
||||
export const getPreferenceDescriptor = <T, R = T>(desc: PreferenceDescription<T, R>) => desc;
|
||||
|
||||
|
||||
export interface DownloadMirror {
|
||||
url: string;
|
||||
label: string;
|
||||
platforms: Set<NodeJS.Platform>;
|
||||
}
|
||||
|
||||
export const defaultPackageMirror = "default";
|
||||
const defaultDownloadMirrorData: DownloadMirror = {
|
||||
url: "https://storage.googleapis.com/kubernetes-release/release",
|
||||
label: "Default (Google)",
|
||||
platforms: new Set(["darwin", "win32", "linux"]),
|
||||
};
|
||||
|
||||
export const packageMirrors = new Map<string, DownloadMirror>([
|
||||
[defaultPackageMirror, defaultDownloadMirrorData],
|
||||
["china", {
|
||||
url: "https://mirror.azure.cn/kubernetes/kubectl",
|
||||
label: "China (Azure)",
|
||||
platforms: new Set(["win32", "linux"]),
|
||||
}],
|
||||
]);
|
||||
|
||||
export type ExtensionRegistryLocation = "default" | "npmrc" | "custom";
|
||||
|
||||
export type ExtensionRegistry = {
|
||||
location: "default" | "npmrc";
|
||||
customUrl?: undefined;
|
||||
} | {
|
||||
location: "custom";
|
||||
customUrl: string;
|
||||
};
|
||||
|
||||
export const defaultExtensionRegistryUrlLocation = "default";
|
||||
export const defaultExtensionRegistryUrl = "https://registry.npmjs.org";
|
||||
|
||||
type PreferencesModelType<field extends keyof PreferenceDescriptors> = PreferenceDescriptors[field] extends PreferenceDescription<infer T, any> ? T : never;
|
||||
type UserStoreModelType<field extends keyof PreferenceDescriptors> = PreferenceDescriptors[field] extends PreferenceDescription<any, infer T> ? T : never;
|
||||
|
||||
export type UserStoreFlatModel = {
|
||||
[field in keyof PreferenceDescriptors]: UserStoreModelType<field>;
|
||||
};
|
||||
|
||||
export type UserPreferencesModel = {
|
||||
[field in keyof PreferenceDescriptors]?: PreferencesModelType<field>;
|
||||
} & { updateChannel: string };
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { action } from "mobx";
|
||||
import userPreferenceDescriptorsInjectable from "./preference-descriptors.injectable";
|
||||
import userPreferencesStateInjectable from "./state.injectable";
|
||||
|
||||
export type ResetTheme = () => void;
|
||||
|
||||
const resetThemeInjectable = getInjectable({
|
||||
id: "reset-theme",
|
||||
instantiate: (di): ResetTheme => {
|
||||
const state = di.inject(userPreferencesStateInjectable);
|
||||
const preferenceDescriptors = di.inject(userPreferenceDescriptorsInjectable);
|
||||
|
||||
return action(() => {
|
||||
state.colorTheme = preferenceDescriptors.colorTheme.fromStore(undefined);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default resetThemeInjectable;
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import userInfoInjectable from "../../../common/vars/user-info.injectable";
|
||||
import userPreferencesStateInjectable from "./state.injectable";
|
||||
|
||||
const userShellSettingInjectable = getInjectable({
|
||||
id: "user-shell-setting",
|
||||
instantiate: (di) => {
|
||||
const state = di.inject(userPreferencesStateInjectable);
|
||||
const userInfo = di.inject(userInfoInjectable);
|
||||
|
||||
return computed(() => state.shell || userInfo.shell);
|
||||
},
|
||||
});
|
||||
|
||||
export default userShellSettingInjectable;
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { observable } from "mobx";
|
||||
import type { PreferenceDescriptors } from "./preference-descriptors.injectable";
|
||||
import type { StoreType } from "./preferences-helpers";
|
||||
|
||||
export type UserPreferencesState = {
|
||||
-readonly [Field in keyof PreferenceDescriptors]: StoreType<PreferenceDescriptors[Field]>;
|
||||
};
|
||||
|
||||
const userPreferencesStateInjectable = getInjectable({
|
||||
id: "user-preferences-state",
|
||||
instantiate: () => observable.object({} as UserPreferencesState),
|
||||
});
|
||||
|
||||
export default userPreferencesStateInjectable;
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { action } from "mobx";
|
||||
import prefixedLoggerInjectable from "../../../common/logger/prefixed-logger.injectable";
|
||||
import createPersistentStorageInjectable from "../../../common/persistent-storage/create.injectable";
|
||||
import persistentStorageMigrationsInjectable from "../../../common/persistent-storage/migrations.injectable";
|
||||
import { userPreferencesMigrationInjectionToken } from "./migrations-token";
|
||||
import { toJS } from "../../../common/utils";
|
||||
import storeMigrationVersionInjectable from "../../../common/vars/store-migration-version.injectable";
|
||||
import selectedUpdateChannelInjectable from "../../application-update/common/selected-update-channel/selected-update-channel.injectable";
|
||||
import type { ReleaseChannel } from "../../application-update/common/update-channels";
|
||||
import userPreferencesStateInjectable from "./state.injectable";
|
||||
import userPreferenceDescriptorsInjectable from "./preference-descriptors.injectable";
|
||||
import type { UserPreferencesModel } from "./preferences-helpers";
|
||||
|
||||
export interface UserStoreModel {
|
||||
preferences: UserPreferencesModel;
|
||||
}
|
||||
|
||||
const userPreferencesPersistentStorageInjectable = getInjectable({
|
||||
id: "user-preferences-persistent-storage",
|
||||
instantiate: (di) => {
|
||||
const createPersistentStorage = di.inject(createPersistentStorageInjectable);
|
||||
const logger = di.inject(prefixedLoggerInjectable, "USER-PREFERENCES");
|
||||
const descriptors = di.inject(userPreferenceDescriptorsInjectable);
|
||||
const selectedUpdateChannel = di.inject(selectedUpdateChannelInjectable);
|
||||
const state = di.inject(userPreferencesStateInjectable);
|
||||
|
||||
return createPersistentStorage<UserStoreModel>({
|
||||
configName: "lens-user-store",
|
||||
projectVersion: di.inject(storeMigrationVersionInjectable),
|
||||
migrations: di.inject(persistentStorageMigrationsInjectable, userPreferencesMigrationInjectionToken),
|
||||
fromStore: action(({ preferences = {}}) => {
|
||||
logger.debug("fromStore()", { preferences });
|
||||
|
||||
state.allowErrorReporting = descriptors.allowErrorReporting.fromStore(preferences.allowErrorReporting);
|
||||
state.allowUntrustedCAs = descriptors.allowUntrustedCAs.fromStore(preferences.allowUntrustedCAs);
|
||||
state.colorTheme = descriptors.colorTheme.fromStore(preferences.colorTheme);
|
||||
state.downloadBinariesPath = descriptors.downloadBinariesPath.fromStore(preferences.downloadBinariesPath);
|
||||
state.downloadKubectlBinaries = descriptors.downloadKubectlBinaries.fromStore(preferences.downloadKubectlBinaries);
|
||||
state.downloadMirror = descriptors.downloadMirror.fromStore(preferences.downloadMirror);
|
||||
state.editorConfiguration = descriptors.editorConfiguration.fromStore(preferences.editorConfiguration);
|
||||
state.extensionRegistryUrl = descriptors.extensionRegistryUrl.fromStore(preferences.extensionRegistryUrl);
|
||||
state.hiddenTableColumns = descriptors.hiddenTableColumns.fromStore(preferences.hiddenTableColumns);
|
||||
state.httpsProxy = descriptors.httpsProxy.fromStore(preferences.httpsProxy);
|
||||
state.kubectlBinariesPath = descriptors.kubectlBinariesPath.fromStore(preferences.kubectlBinariesPath);
|
||||
state.localeTimezone = descriptors.localeTimezone.fromStore(preferences.localeTimezone);
|
||||
state.openAtLogin = descriptors.openAtLogin.fromStore(preferences.openAtLogin);
|
||||
state.shell = descriptors.shell.fromStore(preferences.shell);
|
||||
state.syncKubeconfigEntries = descriptors.syncKubeconfigEntries.fromStore(preferences.syncKubeconfigEntries);
|
||||
state.terminalConfig = descriptors.terminalConfig.fromStore(preferences.terminalConfig);
|
||||
state.terminalCopyOnSelect = descriptors.terminalCopyOnSelect.fromStore(preferences.terminalCopyOnSelect);
|
||||
state.terminalTheme = descriptors.terminalTheme.fromStore(preferences.terminalTheme);
|
||||
|
||||
// TODO: Switch to action-based saving instead saving stores by reaction
|
||||
selectedUpdateChannel.setValue(preferences?.updateChannel as ReleaseChannel);
|
||||
}),
|
||||
toJSON: () => toJS({
|
||||
preferences: {
|
||||
allowErrorReporting: descriptors.allowErrorReporting.toStore(state.allowErrorReporting),
|
||||
allowUntrustedCAs: descriptors.allowUntrustedCAs.toStore(state.allowUntrustedCAs),
|
||||
colorTheme: descriptors.colorTheme.toStore(state.colorTheme),
|
||||
downloadBinariesPath: descriptors.downloadBinariesPath.toStore(state.downloadBinariesPath),
|
||||
downloadKubectlBinaries: descriptors.downloadKubectlBinaries.toStore(state.downloadKubectlBinaries),
|
||||
downloadMirror: descriptors.downloadMirror.toStore(state.downloadMirror),
|
||||
editorConfiguration: descriptors.editorConfiguration.toStore(state.editorConfiguration),
|
||||
extensionRegistryUrl: descriptors.extensionRegistryUrl.toStore(state.extensionRegistryUrl),
|
||||
hiddenTableColumns: descriptors.hiddenTableColumns.toStore(state.hiddenTableColumns),
|
||||
httpsProxy: descriptors.httpsProxy.toStore(state.httpsProxy),
|
||||
kubectlBinariesPath: descriptors.kubectlBinariesPath.toStore(state.kubectlBinariesPath),
|
||||
localeTimezone: descriptors.localeTimezone.toStore(state.localeTimezone),
|
||||
openAtLogin: descriptors.openAtLogin.toStore(state.openAtLogin),
|
||||
shell: descriptors.shell.toStore(state.shell),
|
||||
syncKubeconfigEntries: descriptors.syncKubeconfigEntries.toStore(state.syncKubeconfigEntries),
|
||||
terminalConfig: descriptors.terminalConfig.toStore(state.terminalConfig),
|
||||
terminalCopyOnSelect: descriptors.terminalCopyOnSelect.toStore(state.terminalCopyOnSelect),
|
||||
terminalTheme: descriptors.terminalTheme.toStore(state.terminalTheme),
|
||||
updateChannel: selectedUpdateChannel.value.get().id,
|
||||
},
|
||||
}),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default userPreferencesPersistentStorageInjectable;
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed, toJS } from "mobx";
|
||||
import userPreferencesStateInjectable from "./state.injectable";
|
||||
|
||||
const terminalConfigInjectable = getInjectable({
|
||||
id: "terminal-config",
|
||||
instantiate: (di) => {
|
||||
const state = di.inject(userPreferencesStateInjectable);
|
||||
|
||||
return computed(() => toJS(state.terminalConfig));
|
||||
},
|
||||
});
|
||||
|
||||
export default terminalConfigInjectable;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import userPreferencesStateInjectable from "./state.injectable";
|
||||
|
||||
const terminalCopyOnSelectInjectable = getInjectable({
|
||||
id: "terminal-copy-on-select",
|
||||
instantiate: (di) => {
|
||||
const state = di.inject(userPreferencesStateInjectable);
|
||||
|
||||
return computed(() => state.terminalCopyOnSelect);
|
||||
},
|
||||
});
|
||||
|
||||
export default terminalCopyOnSelectInjectable;
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import userPreferencesStateInjectable from "./state.injectable";
|
||||
|
||||
export type TerminalThemePreference = {
|
||||
matchLensTheme: true;
|
||||
} | {
|
||||
matchLensTheme: false;
|
||||
themeId: string;
|
||||
};
|
||||
|
||||
const terminalThemePreferenceInjectable = getInjectable({
|
||||
id: "terminal-theme-preference",
|
||||
instantiate: (di) => {
|
||||
const state = di.inject(userPreferencesStateInjectable);
|
||||
|
||||
return computed((): TerminalThemePreference => {
|
||||
// NOTE: remove use of magic strings
|
||||
if (!state.terminalTheme) {
|
||||
return {
|
||||
matchLensTheme: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
matchLensTheme: false,
|
||||
themeId: state.terminalTheme,
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default terminalThemePreferenceInjectable;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getOrInsertSet, toggle } from "@k8slens/utilities";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { action } from "mobx";
|
||||
import userPreferencesStateInjectable from "./state.injectable";
|
||||
|
||||
export type ToggleTableColumnVisibility = (tableId: string, columnId: string) => void;
|
||||
|
||||
const toggleTableColumnVisibilityInjectable = getInjectable({
|
||||
id: "toggle-table-column-visibility",
|
||||
instantiate: (di): ToggleTableColumnVisibility => {
|
||||
const state = di.inject(userPreferencesStateInjectable);
|
||||
|
||||
return action((tableId, columnId) => {
|
||||
toggle(getOrInsertSet(state.hiddenTableColumns, tableId), columnId);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default toggleTableColumnVisibilityInjectable;
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
// Switch representation of hiddenTableColumns in store
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { userPreferencesMigrationInjectionToken } from "../common/migrations-token";
|
||||
|
||||
interface PreV500Alpha3UserPreferencesModel {
|
||||
hiddenTableColumns?: Record<string, string[]>;
|
||||
}
|
||||
|
||||
const v500Alpha3UserPreferencesStorageMigrationInjectable = getInjectable({
|
||||
id: "v5.0.0-alpha.3-preferences-storage-migration",
|
||||
instantiate: () => ({
|
||||
version: "5.0.0-alpha.3",
|
||||
run(store) {
|
||||
const preferences = (store.get("preferences") ?? {}) as PreV500Alpha3UserPreferencesModel;
|
||||
const oldHiddenTableColumns = preferences.hiddenTableColumns;
|
||||
|
||||
if (!oldHiddenTableColumns) {
|
||||
return;
|
||||
}
|
||||
|
||||
store.set("preferences", {
|
||||
...preferences,
|
||||
hiddenTableColumns: Object.entries(oldHiddenTableColumns),
|
||||
});
|
||||
},
|
||||
}),
|
||||
injectionToken: userPreferencesMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default v500Alpha3UserPreferencesStorageMigrationInjectable;
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { isErrnoException } from "@k8slens/utilities";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
|
||||
import isLogicalChildPathInjectable from "../../../common/path/is-logical-child-path.injectable";
|
||||
import getDirnameOfPathInjectable from "../../../common/path/get-dirname.injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { userPreferencesMigrationInjectionToken } from "../../../features/user-preferences/common/migrations-token";
|
||||
import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable";
|
||||
import homeDirectoryPathInjectable from "../../../common/os/home-directory-path.injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable";
|
||||
import type { ClusterStoreModel } from "../../../features/cluster/storage/common/storage.injectable";
|
||||
import type { UserPreferencesModel, KubeconfigSyncEntry } from "../common/preferences-helpers";
|
||||
|
||||
const v503Beta1UserPreferencesStorageMigrationInjectable = getInjectable({
|
||||
id: "v5.0.3-beta.1-preferences-storage-migration",
|
||||
instantiate: (di) => {
|
||||
const userDataPath = di.inject(directoryForUserDataInjectable);
|
||||
const kubeConfigsPath = di.inject(directoryForKubeConfigsInjectable);
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const isLogicalChildPath = di.inject(isLogicalChildPathInjectable);
|
||||
const getDirnameOfPath = di.inject(getDirnameOfPathInjectable);
|
||||
const readJsonSync = di.inject(readJsonSyncInjectable);
|
||||
const homeDirectoryPath = di.inject(homeDirectoryPathInjectable);
|
||||
const pathExistsSync = di.inject(pathExistsSyncInjectable);
|
||||
|
||||
return {
|
||||
version: "5.0.3-beta.1",
|
||||
run(store) {
|
||||
try {
|
||||
const { syncKubeconfigEntries = [], ...preferences } = (store.get("preferences") ?? {}) as UserPreferencesModel;
|
||||
const { clusters = [] }: ClusterStoreModel = readJsonSync(joinPaths(userDataPath, "lens-cluster-store.json"), "utf-8") ?? {};
|
||||
const extensionDataDir = joinPaths(userDataPath, "extension_data");
|
||||
const syncPaths = new Set(syncKubeconfigEntries.map(s => s.filePath));
|
||||
|
||||
syncPaths.add(joinPaths(homeDirectoryPath, ".kube"));
|
||||
|
||||
for (const cluster of clusters) {
|
||||
if (!cluster.kubeConfigPath) {
|
||||
continue;
|
||||
}
|
||||
const dirOfKubeconfig = getDirnameOfPath(cluster.kubeConfigPath);
|
||||
|
||||
if (dirOfKubeconfig === kubeConfigsPath) {
|
||||
logger.info(`Skipping ${cluster.id} because kubeConfigPath is under the stored KubeConfig folder`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (syncPaths.has(cluster.kubeConfigPath) || syncPaths.has(dirOfKubeconfig)) {
|
||||
logger.info(`Skipping ${cluster.id} because kubeConfigPath is already being synced`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isLogicalChildPath(extensionDataDir, cluster.kubeConfigPath)) {
|
||||
logger.info(`Skipping ${cluster.id} because kubeConfigPath is placed under an extension_data folder`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pathExistsSync(cluster.kubeConfigPath)) {
|
||||
logger.info(`Skipping ${cluster.id} because kubeConfigPath no longer exists`);
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.info(`Adding ${cluster.kubeConfigPath} from ${cluster.id} to sync paths`);
|
||||
syncPaths.add(cluster.kubeConfigPath);
|
||||
}
|
||||
|
||||
const updatedSyncEntries: KubeconfigSyncEntry[] = [...syncPaths].map(filePath => ({ filePath }));
|
||||
|
||||
logger.info("Final list of synced paths", updatedSyncEntries);
|
||||
store.set("preferences", { ...preferences, syncKubeconfigEntries: updatedSyncEntries });
|
||||
} catch (error) {
|
||||
if (isErrnoException(error) && error.code !== "ENOENT") {
|
||||
// ignore files being missing
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: userPreferencesMigrationInjectionToken,
|
||||
});
|
||||
|
||||
export default v503Beta1UserPreferencesStorageMigrationInjectable;
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import { isErrnoException } from "@k8slens/utilities";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
|
||||
import fsInjectable from "../../../common/fs/fs.injectable";
|
||||
|
||||
export type UserStoreFileNameMigration = () => Promise<void>;
|
||||
|
||||
const userPreferencesStorageFileNameMigrationInjectable = getInjectable({
|
||||
id: "preferences-storage-file-name-migration",
|
||||
instantiate: (di): UserStoreFileNameMigration => {
|
||||
const userDataPath = di.inject(directoryForUserDataInjectable);
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
const configJsonPath = joinPaths(userDataPath, "config.json");
|
||||
const lensUserStoreJsonPath = joinPaths(userDataPath, "lens-user-store.json");
|
||||
const { rename, rm } = di.inject(fsInjectable);
|
||||
|
||||
return async () => {
|
||||
try {
|
||||
await rename(configJsonPath, lensUserStoreJsonPath);
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message === "dest already exists.") {
|
||||
await rm(configJsonPath);
|
||||
} else if (isErrnoException(error) && error.code === "ENOENT" && error.path === configJsonPath) {
|
||||
// (No such file or directory)
|
||||
return; // file already moved
|
||||
} else {
|
||||
// pass other errors along
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default userPreferencesStorageFileNameMigrationInjectable;
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { beforeApplicationIsLoadingInjectionToken } from "@k8slens/application";
|
||||
import initDefaultUpdateChannelInjectable from "../../../main/vars/default-update-channel/init.injectable";
|
||||
import userPreferencesPersistentStorageInjectable from "../common/storage.injectable";
|
||||
import userPreferencesStorageFileNameMigrationInjectable from "./file-name-migration.injectable";
|
||||
|
||||
const loadUserPreferencesStorageInjectable = getInjectable({
|
||||
id: "load-user-preferences-storage",
|
||||
instantiate: (di) => ({
|
||||
run: async () => {
|
||||
const storage = di.inject(userPreferencesPersistentStorageInjectable);
|
||||
const userStoreFileNameMigration = di.inject(userPreferencesStorageFileNameMigrationInjectable);
|
||||
|
||||
await userStoreFileNameMigration();
|
||||
storage.loadAndStartSyncing();
|
||||
},
|
||||
runAfter: initDefaultUpdateChannelInjectable,
|
||||
}),
|
||||
injectionToken: beforeApplicationIsLoadingInjectionToken,
|
||||
});
|
||||
|
||||
export default loadUserPreferencesStorageInjectable;
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { reaction } from "mobx";
|
||||
import setLoginItemSettingsInjectable from "../../../main/electron-app/features/set-login-item-settings.injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "@k8slens/application";
|
||||
import userPreferencesStateInjectable from "../common/state.injectable";
|
||||
|
||||
const setupSyncOpenAtLoginWithOsInjectable = getInjectable({
|
||||
id: "setup-sync-open-at-login-with-os",
|
||||
instantiate: (di) => ({
|
||||
run: () => {
|
||||
const setLoginItemSettings = di.inject(setLoginItemSettingsInjectable);
|
||||
const state = di.inject(userPreferencesStateInjectable);
|
||||
|
||||
reaction(() => state.openAtLogin, openAtLogin => {
|
||||
setLoginItemSettings({
|
||||
openAtLogin,
|
||||
openAsHidden: true,
|
||||
args: ["--hidden"],
|
||||
});
|
||||
}, {
|
||||
fireImmediately: true,
|
||||
});
|
||||
},
|
||||
}),
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default setupSyncOpenAtLoginWithOsInjectable;
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { beforeFrameStartsSecondInjectionToken } from "../../../renderer/before-frame-starts/tokens";
|
||||
import initDefaultUpdateChannelInjectable from "../../../renderer/vars/default-update-channel/init.injectable";
|
||||
import userPreferencesPersistentStorageInjectable from "../common/storage.injectable";
|
||||
|
||||
const loadUserPreferencesStorageInjectable = getInjectable({
|
||||
id: "load-user-preferences-storage",
|
||||
instantiate: (di) => ({
|
||||
run: () => {
|
||||
const storage = di.inject(userPreferencesPersistentStorageInjectable);
|
||||
|
||||
return storage.loadAndStartSyncing();
|
||||
},
|
||||
runAfter: initDefaultUpdateChannelInjectable,
|
||||
}),
|
||||
injectionToken: beforeFrameStartsSecondInjectionToken,
|
||||
});
|
||||
|
||||
export default loadUserPreferencesStorageInjectable;
|
||||
Reference in new issue
Block a user