mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Mobx-6 migration (#2718)
* mobx-6 migration -- part 1 Signed-off-by: Roman <ixrock@gmail.com> * mobx-6 migration -- part 2 (npx mobx-undecorate --keepDecorators) Signed-off-by: Roman <ixrock@gmail.com> * mobx-6 migration -- part 3 (more fixes) Signed-off-by: Roman <ixrock@gmail.com> * unwrap possible observables from IPC-messaging Signed-off-by: Roman <ixrock@gmail.com> * mobx-6 migration -- remove @autobind as class-decorator Signed-off-by: Roman <ixrock@gmail.com> * mobx-6: replacing @autobind() as method-decorator to @boundMethod Signed-off-by: Roman <ixrock@gmail.com> * mobx-6: use toJS()-wrapper since monkey-patching require(mobx).toJS doesn't work Signed-off-by: Roman <ixrock@gmail.com> * removed `@observable static` Signed-off-by: Roman <ixrock@gmail.com> * use {useDefineForClassFields: true} in tsconfig.json Signed-off-by: Roman <ixrock@gmail.com> * remove ExtendedObservableMap Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix: removed makeObservable(this) from "terminal-tab.tsx" Signed-off-by: Roman <ixrock@gmail.com> * storage-helper refactoring Signed-off-by: Roman <ixrock@gmail.com> * normalize usages of #observable-value.toJSON() / attempt to catch the wind Signed-off-by: Roman <ixrock@gmail.com> * refactoring, more possible branch fixes + lint Signed-off-by: Roman <ixrock@gmail.com> * debugging cluster-view error -- part 1 Signed-off-by: Roman <ixrock@gmail.com> * fix: refreshing cluster-view on ready Signed-off-by: Roman <ixrock@gmail.com> * fix: various app-crashes related to KubeObject.spec.* access from "undefined" fix: config-map-details crash Signed-off-by: Roman <ixrock@gmail.com> * fix: namespace-store refactoring / saving selected-namespaces to external json-file Signed-off-by: Roman <ixrock@gmail.com> * fix: don't cache mobx.when(() => this.someObservable) cause might not work as expected due later call of makeObservable(this) in constructor Signed-off-by: Roman <ixrock@gmail.com> * fix: app-crash on editing k8s resource Signed-off-by: Roman <ixrock@gmail.com> * fix: restore "all namespaces" on page reload Signed-off-by: Roman <ixrock@gmail.com> * - fix: persist table-sort params and cluster-view's sidebar state to lens-local-storage - new-feature: auto-open main-window's devtools in development-mode (yes/no/ugly?) Signed-off-by: Roman <ixrock@gmail.com> * fix: crd definition details -> crashing with <AceEditor mode="json"> (added missing mode-file in ace-editor.tsx) Signed-off-by: Roman <ixrock@gmail.com> * fix: crd definitions -> groups selector couldn't deselect last selected option Signed-off-by: Roman <ixrock@gmail.com> * refactoring: extensions-api exports clarification for "@k8slens/extensions" Signed-off-by: Roman <ixrock@gmail.com> * fix: various app-crashes related to kube-events (events page, some details page, overview, etc.) Signed-off-by: Roman <ixrock@gmail.com> * Reverted "use {useDefineForClassFields: true} in tsconfig.json" (various app-crash fixes) This flag seems to be not possible to use with class-inheritance in some cases. Example / demo: `KubeObject` class has initial type definitions for the fields like: "metadata", "kind", etc. and constructor() has Object.assign(this, data); Meanwhile child class, e.g. KubeEvent inherited from KubeObject and has it's own extra type definitions for underlying resource, e.g. "involvedObject", "source", etc. So calling super(data) doesn't work as expected for child class as it's own type definitions overwrites data from parent's constructor with `undefined` at later point. Signed-off-by: Roman <ixrock@gmail.com> * master-merge lint-fixes Signed-off-by: Roman <ixrock@gmail.com> * catalog.tsx / catalog-entities.store.ts refactoring & fixes Signed-off-by: Roman <ixrock@gmail.com> * fix: Catalog -> Browse all tab Signed-off-by: Roman <ixrock@gmail.com> * fix: CommandPalette doesn't appear from global menu by click/hotkey Signed-off-by: Roman <ixrock@gmail.com> * - Merging interfaces & classses to avoid overwriting fields from parent's super(data)-call with Object.assign(this, data). Otherwise use "declare" keyword at class field definition. - Revamping {useDefineForClassFields: true} to avoid issues with non-observable class fields in some cases (from previous commit): ``` @observer export class CommandContainer extends React.Component<CommandContainerProps> { // without some defined initial value "commandComponent" is non-observable for some reasons // when tsconfig.ts has {useDefineForClassFields:false} @observable.ref commandComponent: React.ReactNode = null; constructor(props: CommandContainerProps) { super(props); makeObservable(this); } ``` Signed-off-by: Roman <ixrock@gmail.com> * update KubeObject class type definition Signed-off-by: Roman <ixrock@gmail.com> * clean up / responding to comments Signed-off-by: Roman <ixrock@gmail.com> * fix: app-crash when navigating to catalog from active cluster-view, refactoring `catalog-entity-store` Signed-off-by: Roman <ixrock@gmail.com> * catalog-pusher clean up, replaced .observe_() to external observe() helper from "mobx" Signed-off-by: Roman <ixrock@gmail.com> * fix: catalog's items stale/non-observable (after connection to the cluster status still "disconnected"), lint-fixes Signed-off-by: Roman <ixrock@gmail.com> * fix: Catalog is empty after closing main-window and re-opening app from Tray Signed-off-by: Roman <ixrock@gmail.com> * fix: HotBar's icon context menu items non-observable (no "disconnect cluster", etc.) Signed-off-by: Roman <ixrock@gmail.com> * lint-fix/license check Signed-off-by: Roman <ixrock@gmail.com> * fix: redirect to catalog when disconnecting active cluster Signed-off-by: Roman <ixrock@gmail.com> * fix: refresh visibility of active cluster-view on switching from hotbar/catalog Signed-off-by: Roman <ixrock@gmail.com> * updated package.json for built-in extensions to use "*" version for packages served from main app Signed-off-by: Roman <ixrock@gmail.com> * - added missing makeObservable(this) to metrics-settings.tsx - updated package-lock.json for built-in extensions - lint fixes Signed-off-by: Roman <ixrock@gmail.com> * master-merge clean up fix, updated package-lock.json for built-in extensions after `make clean-extensions && make build-extensions` Signed-off-by: Roman <ixrock@gmail.com> * fix unit-tests Signed-off-by: Roman <ixrock@gmail.com> * master-merge fixes Signed-off-by: Roman <ixrock@gmail.com> * make lint happy Signed-off-by: Roman <ixrock@gmail.com> * reverted some changes, removed auto-opening devtools in dev-mode Signed-off-by: Roman <ixrock@gmail.com> * merge fixes Signed-off-by: Roman <ixrock@gmail.com> * master-merge conflict fixes: - proper handling and navigating into catalog's active category via URL-builder Signed-off-by: Roman <ixrock@gmail.com> * reverting splitted params for catalog's page route to "/catalog/:group?/:kind?" Signed-off-by: Roman <ixrock@gmail.com> * clean-up: remove app's injecting dependencies from `extensions/kube-object-event-status/package.json` Signed-off-by: Roman <ixrock@gmail.com> * master-merge fix: added missing makeObservable(this) for extensions.tsx Signed-off-by: Roman <ixrock@gmail.com> * fix: catalog entity context menu stale/unobservable Signed-off-by: Roman <ixrock@gmail.com> Co-authored-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
1 parent
0c45dd807e
commit
2c3b510997
256 files changed
+2359
-1473
No files matched your search
@@ -23,9 +23,8 @@ import path from "path";
|
||||
import Config from "conf";
|
||||
import type { Options as ConfOptions } from "conf/dist/source/types";
|
||||
import { app, ipcMain, IpcMainEvent, ipcRenderer, IpcRendererEvent, remote } from "electron";
|
||||
import { IReactionOptions, observable, reaction, runInAction, when } from "mobx";
|
||||
import Singleton from "./utils/singleton";
|
||||
import { getAppVersion } from "./utils/app-version";
|
||||
import { IReactionOptions, makeObservable, observable, reaction, runInAction, when } from "mobx";
|
||||
import { getAppVersion, Singleton, toJS, Disposer } from "./utils";
|
||||
import logger from "../main/logger";
|
||||
import { broadcastMessage, subscribeToBroadcast, unsubscribeFromBroadcast } from "./ipc";
|
||||
import isEqual from "lodash/isEqual";
|
||||
@@ -41,13 +40,18 @@ export interface BaseStoreParams<T = any> extends ConfOptions<T> {
|
||||
*/
|
||||
export abstract class BaseStore<T = any> extends Singleton {
|
||||
protected storeConfig?: Config<T>;
|
||||
protected syncDisposers: Function[] = [];
|
||||
protected syncDisposers: Disposer[] = [];
|
||||
|
||||
whenLoaded = when(() => this.isLoaded);
|
||||
@observable isLoaded = false;
|
||||
|
||||
get whenLoaded() {
|
||||
return when(() => this.isLoaded);
|
||||
}
|
||||
|
||||
protected constructor(protected params: BaseStoreParams) {
|
||||
super();
|
||||
makeObservable(this);
|
||||
|
||||
this.params = {
|
||||
autoLoad: false,
|
||||
syncEnabled: true,
|
||||
@@ -114,7 +118,11 @@ export abstract class BaseStore<T = any> extends Singleton {
|
||||
|
||||
enableSync() {
|
||||
this.syncDisposers.push(
|
||||
reaction(() => this.toJSON(), model => this.onModelChange(model), this.params.syncOptions),
|
||||
reaction(
|
||||
() => toJS(this.toJSON()), // unwrap possible observables and react to everything
|
||||
model => this.onModelChange(model),
|
||||
this.params.syncOptions,
|
||||
),
|
||||
);
|
||||
|
||||
if (ipcMain) {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { action, computed, observable } from "mobx";
|
||||
import { action, computed, observable, makeObservable } from "mobx";
|
||||
import { Disposer, ExtendedMap } from "../utils";
|
||||
import { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./catalog-entity";
|
||||
|
||||
@@ -27,6 +27,10 @@ export class CatalogCategoryRegistry {
|
||||
protected categories = observable.set<CatalogCategory>();
|
||||
protected groupKinds = new ExtendedMap<string, ExtendedMap<string, CatalogCategory>>();
|
||||
|
||||
constructor() {
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@action add(category: CatalogCategory): Disposer {
|
||||
this.categories.add(category);
|
||||
this.updateGroupKinds(category);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import { EventEmitter } from "events";
|
||||
import { observable } from "mobx";
|
||||
import { observable, makeObservable } from "mobx";
|
||||
|
||||
type ExtractEntityMetadataType<Entity> = Entity extends CatalogEntity<infer Metadata> ? Metadata : never;
|
||||
type ExtractEntityStatusType<Entity> = Entity extends CatalogEntity<any, infer Status> ? Status : never;
|
||||
@@ -56,6 +56,12 @@ export abstract class CatalogCategory extends EventEmitter {
|
||||
};
|
||||
abstract spec: CatalogCategorySpec;
|
||||
|
||||
static parseId(id = ""): { group?: string, kind?: string } {
|
||||
const [group, kind] = id.split("/") ?? [];
|
||||
|
||||
return { group, kind };
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return `${this.spec.group}/${this.spec.names.kind}`;
|
||||
}
|
||||
@@ -147,6 +153,7 @@ export abstract class CatalogEntity<
|
||||
@observable spec: Spec;
|
||||
|
||||
constructor(data: CatalogEntityData<Metadata, Status, Spec>) {
|
||||
makeObservable(this);
|
||||
this.metadata = data.metadata;
|
||||
this.status = data.status;
|
||||
this.spec = data.spec;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
import path from "path";
|
||||
import { app, ipcMain, ipcRenderer, remote, webFrame } from "electron";
|
||||
import { unlink } from "fs-extra";
|
||||
import { action, comparer, computed, observable, reaction, toJS } from "mobx";
|
||||
import { action, comparer, computed, observable, reaction, makeObservable } from "mobx";
|
||||
import { BaseStore } from "./base-store";
|
||||
import { Cluster, ClusterState } from "../main/cluster";
|
||||
import migrations from "../migrations/cluster-store";
|
||||
@@ -33,7 +33,7 @@ import { saveToAppFiles } from "./utils/saveToAppFiles";
|
||||
import type { KubeConfig } from "@kubernetes/client-node";
|
||||
import { handleRequest, requestMain, subscribeToBroadcast, unsubscribeAllFromBroadcast } from "./ipc";
|
||||
import type { ResourceType } from "../renderer/components/cluster-settings/components/cluster-metrics-setting";
|
||||
import { disposer, noop } from "./utils";
|
||||
import { disposer, noop, toJS } from "./utils";
|
||||
|
||||
export interface ClusterIconUpload {
|
||||
clusterId: string;
|
||||
@@ -148,6 +148,8 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
migrations,
|
||||
});
|
||||
|
||||
makeObservable(this);
|
||||
|
||||
this.pushStateToViewsAutomatically();
|
||||
}
|
||||
|
||||
@@ -171,16 +173,16 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
});
|
||||
} else if (ipcMain) {
|
||||
handleRequest(ClusterStore.stateRequestChannel, (): clusterStateSync[] => {
|
||||
const states: clusterStateSync[] = [];
|
||||
const clusterStates: clusterStateSync[] = [];
|
||||
|
||||
this.clustersList.forEach((cluster) => {
|
||||
states.push({
|
||||
clusterStates.push({
|
||||
state: cluster.getState(),
|
||||
id: cluster.id
|
||||
});
|
||||
});
|
||||
|
||||
return states;
|
||||
return clusterStates;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -309,7 +311,7 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
|
||||
@action
|
||||
protected fromStore({ activeCluster, clusters = [] }: ClusterStoreModel = {}) {
|
||||
const currentClusters = this.clusters.toJS();
|
||||
const currentClusters = new Map(this.clusters);
|
||||
const newClusters = new Map<ClusterId, Cluster>();
|
||||
const removedClusters = new Map<ClusterId, Cluster>();
|
||||
|
||||
@@ -345,8 +347,6 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
return toJS({
|
||||
activeCluster: this.activeCluster,
|
||||
clusters: this.clustersList.map(cluster => cluster.toJSON()),
|
||||
}, {
|
||||
recurseEverything: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import * as Mobx from "mobx";
|
||||
import * as Immer from "immer";
|
||||
|
||||
/**
|
||||
* Setup default configuration for external npm-packages
|
||||
*/
|
||||
export default function configurePackages() {
|
||||
// Docs: https://mobx.js.org/configuration.html
|
||||
Mobx.configure({
|
||||
enforceActions: "never",
|
||||
isolateGlobalState: true,
|
||||
|
||||
// TODO: enable later (read more: https://mobx.js.org/migrating-from-4-or-5.html)
|
||||
// computedRequiresReaction: true,
|
||||
// reactionRequiresObservable: true,
|
||||
// observableRequiresReaction: true,
|
||||
});
|
||||
|
||||
// Docs: https://immerjs.github.io/immer/
|
||||
// Required in `utils/storage-helper.ts`
|
||||
Immer.setAutoFreeze(false); // allow to merge mobx observables
|
||||
Immer.enableMapSet(); // allow to merge maps and sets
|
||||
}
|
||||
@@ -19,11 +19,12 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { action, comparer, observable, toJS } from "mobx";
|
||||
import { action, comparer, observable, makeObservable } from "mobx";
|
||||
import { BaseStore } from "./base-store";
|
||||
import migrations from "../migrations/hotbar-store";
|
||||
import * as uuid from "uuid";
|
||||
import isNull from "lodash/isNull";
|
||||
import { toJS } from "./utils";
|
||||
import { CatalogEntity } from "./catalog";
|
||||
|
||||
export interface HotbarItem {
|
||||
@@ -69,6 +70,7 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
||||
},
|
||||
migrations,
|
||||
});
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
get activeHotbarId() {
|
||||
@@ -252,8 +254,6 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
||||
activeHotbarId: this.activeHotbarId
|
||||
};
|
||||
|
||||
return toJS(model, {
|
||||
recurseEverything: true,
|
||||
});
|
||||
return toJS(model);
|
||||
}
|
||||
}
|
||||
+20
-9
@@ -23,29 +23,34 @@
|
||||
// https://www.electronjs.org/docs/api/ipc-main
|
||||
// https://www.electronjs.org/docs/api/ipc-renderer
|
||||
|
||||
import { ipcMain, ipcRenderer, webContents, remote } from "electron";
|
||||
import { toJS } from "mobx";
|
||||
import { ipcMain, ipcRenderer, remote, webContents } from "electron";
|
||||
import { toJS } from "../utils/toJS";
|
||||
import logger from "../../main/logger";
|
||||
import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames";
|
||||
import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames";
|
||||
|
||||
const subFramesChannel = "ipc:get-sub-frames";
|
||||
|
||||
export function handleRequest(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any) {
|
||||
ipcMain.handle(channel, listener);
|
||||
ipcMain.handle(channel, async (event, ...args) => {
|
||||
const payload = await listener(event, ...args);
|
||||
|
||||
return sanitizePayload(payload);
|
||||
});
|
||||
}
|
||||
|
||||
export async function requestMain(channel: string, ...args: any[]) {
|
||||
return ipcRenderer.invoke(channel, ...args);
|
||||
return ipcRenderer.invoke(channel, ...args.map(sanitizePayload));
|
||||
}
|
||||
|
||||
function getSubFrames(): ClusterFrameInfo[] {
|
||||
return toJS(Array.from(clusterFrameMap.values()), { recurseEverything: true });
|
||||
return Array.from(clusterFrameMap.values());
|
||||
}
|
||||
|
||||
export function broadcastMessage(channel: string, ...args: any[]) {
|
||||
const views = (webContents || remote?.webContents)?.getAllWebContents();
|
||||
|
||||
if (!views) return;
|
||||
args = args.map(sanitizePayload);
|
||||
|
||||
ipcRenderer?.send(channel, ...args);
|
||||
ipcMain?.emit(channel, ...args);
|
||||
@@ -98,7 +103,13 @@ export function unsubscribeAllFromBroadcast(channel: string) {
|
||||
}
|
||||
|
||||
export function bindBroadcastHandlers() {
|
||||
handleRequest(subFramesChannel, () => {
|
||||
return getSubFrames();
|
||||
});
|
||||
handleRequest(subFramesChannel, () => getSubFrames());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizing data for IPC-messaging before send.
|
||||
* Removes possible observable values to avoid exceptions like "can't clone object".
|
||||
*/
|
||||
function sanitizePayload<T>(data: any): T {
|
||||
return toJS(data);
|
||||
}
|
||||
@@ -19,9 +19,9 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { action, computed, observable,reaction } from "mobx";
|
||||
import { action, computed, observable, reaction, makeObservable } from "mobx";
|
||||
import { dockStore } from "../renderer/components/dock/dock.store";
|
||||
import { autobind } from "../renderer/utils";
|
||||
import { boundMethod } from "../renderer/utils";
|
||||
|
||||
export class SearchStore {
|
||||
/**
|
||||
@@ -54,6 +54,7 @@ export class SearchStore {
|
||||
@observable activeOverlayIndex = -1;
|
||||
|
||||
constructor() {
|
||||
makeObservable(this);
|
||||
reaction(() => dockStore.selectedTabId, () => {
|
||||
searchStore.reset();
|
||||
});
|
||||
@@ -128,12 +129,12 @@ export class SearchStore {
|
||||
return prev;
|
||||
}
|
||||
|
||||
@autobind()
|
||||
@boundMethod
|
||||
public setNextOverlayActive(): void {
|
||||
this.activeOverlayIndex = this.getNextOverlay(true);
|
||||
}
|
||||
|
||||
@autobind()
|
||||
@boundMethod
|
||||
public setPrevOverlayActive(): void {
|
||||
this.activeOverlayIndex = this.getPrevOverlay(true);
|
||||
}
|
||||
@@ -159,7 +160,7 @@ export class SearchStore {
|
||||
* @param line Index of the line where overlay is located
|
||||
* @param occurrence Number of the overlay within one line
|
||||
*/
|
||||
@autobind()
|
||||
@boundMethod
|
||||
public isActiveOverlay(line: number, occurrence: number): boolean {
|
||||
const firstLineIndex = this.occurrences.findIndex(item => item === line);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import type { ThemeId } from "../renderer/theme.store";
|
||||
import { app, remote } from "electron";
|
||||
import semver from "semver";
|
||||
import { readFile } from "fs-extra";
|
||||
import { action, computed, observable, reaction, toJS } from "mobx";
|
||||
import { action, computed, observable, reaction, makeObservable } from "mobx";
|
||||
import moment from "moment-timezone";
|
||||
import { BaseStore } from "./base-store";
|
||||
import migrations from "../migrations/user-store";
|
||||
@@ -34,7 +34,7 @@ import logger from "../main/logger";
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
import { fileNameMigration } from "../migrations/user-store";
|
||||
import { ObservableToggleSet } from "../renderer/utils";
|
||||
import { ObservableToggleSet, toJS } from "../renderer/utils";
|
||||
|
||||
export interface UserStoreModel {
|
||||
kubeConfigPath: string;
|
||||
@@ -73,6 +73,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
||||
configName: "lens-user-store",
|
||||
migrations,
|
||||
});
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
@observable lastSeenAppVersion = "0.0.0";
|
||||
@@ -306,9 +307,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
||||
},
|
||||
};
|
||||
|
||||
return toJS(model, {
|
||||
recurseEverything: true,
|
||||
});
|
||||
return toJS(model);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { isObservable, observable } from "mobx";
|
||||
import { toJS } from "../toJS";
|
||||
|
||||
describe("utils/toJS(data: any)", () => {
|
||||
const y = { y: 2 };
|
||||
|
||||
const data = observable({ x: 1, y }, {}, {
|
||||
deep: false, // this will keep ref to "y"
|
||||
});
|
||||
const data2 = {
|
||||
x: 1, // partially observable
|
||||
y: observable(y),
|
||||
};
|
||||
|
||||
test("converts mobx-observable to corresponding js struct with links preserving", () => {
|
||||
expect(toJS(data).y).toBe(y);
|
||||
expect(isObservable(toJS(data).y)).toBeFalsy();
|
||||
});
|
||||
|
||||
test("converts partially observable js struct", () => {
|
||||
expect(toJS(data2).y).not.toBe(y);
|
||||
expect(toJS(data2).y).toEqual(y);
|
||||
expect(isObservable(toJS(data2).y)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -19,48 +19,22 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Decorator for binding class methods
|
||||
// Can be applied to class or single method as @autobind()
|
||||
type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
import {boundMethod, boundClass} from "autobind-decorator";
|
||||
import autoBindClass, { Options } from "auto-bind";
|
||||
import autoBindReactClass from "auto-bind/react";
|
||||
|
||||
export function autobind() {
|
||||
return function (target: Constructor | object, prop?: string, descriptor?: PropertyDescriptor) {
|
||||
if (target instanceof Function) return bindClass(target);
|
||||
else return bindMethod(target, prop, descriptor);
|
||||
};
|
||||
}
|
||||
|
||||
function bindClass<T extends Constructor>(constructor: T) {
|
||||
const proto = constructor.prototype;
|
||||
const descriptors = Object.getOwnPropertyDescriptors(proto);
|
||||
const skipMethod = (methodName: string) => {
|
||||
return methodName === "constructor"
|
||||
|| typeof descriptors[methodName].value !== "function";
|
||||
};
|
||||
|
||||
Object.keys(descriptors).forEach(prop => {
|
||||
if (skipMethod(prop)) return;
|
||||
const boundDescriptor = bindMethod(proto, prop, descriptors[prop]);
|
||||
|
||||
Object.defineProperty(proto, prop, boundDescriptor);
|
||||
});
|
||||
}
|
||||
|
||||
function bindMethod(target: object, prop?: string, descriptor?: PropertyDescriptor) {
|
||||
if (!descriptor || typeof descriptor.value !== "function") {
|
||||
throw new Error(`@autobind() must be used on class or method only`);
|
||||
// Automatically bind methods to their class instance
|
||||
export function autoBind<T extends object>(obj: T, opts?: Options): T {
|
||||
if ("componentWillUnmount" in obj) {
|
||||
return autoBindReactClass(obj as any, opts);
|
||||
}
|
||||
const { value: func, enumerable, configurable } = descriptor;
|
||||
const boundFunc = new WeakMap<object, Function>();
|
||||
|
||||
return Object.defineProperty(target, prop, {
|
||||
enumerable,
|
||||
configurable,
|
||||
get() {
|
||||
if (this === target) return func; // direct access from prototype
|
||||
if (!boundFunc.has(this)) boundFunc.set(this, func.bind(this));
|
||||
|
||||
return boundFunc.get(this);
|
||||
}
|
||||
});
|
||||
return autoBindClass(obj, opts);
|
||||
}
|
||||
|
||||
// Class/method decorators
|
||||
// Note: @boundClass doesn't work with mobx-6.x/@action decorator
|
||||
export {
|
||||
boundClass,
|
||||
boundMethod,
|
||||
};
|
||||
@@ -27,6 +27,7 @@ export * from "./app-version";
|
||||
export * from "./autobind";
|
||||
export * from "./base64";
|
||||
export * from "./camelCase";
|
||||
export * from "./toJS";
|
||||
export * from "./cloneJson";
|
||||
export * from "./debouncePromise";
|
||||
export * from "./defineGlobal";
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Wrapper for mobx.toJS() to support partially observable objects as data-input (>= mobx6).
|
||||
* Otherwise, output result won't be recursively converted to corresponding plain JS-structure.
|
||||
*
|
||||
* @example
|
||||
* mobx.toJS({one: 1, two: observable.array([2])}); // "data.two" == ObservableArray<number>
|
||||
*/
|
||||
import * as mobx from "mobx";
|
||||
import { isObservable, observable } from "mobx";
|
||||
|
||||
export function toJS<T>(data: T): T {
|
||||
// make data observable for recursive toJS()-output
|
||||
if (typeof data === "object" && !isObservable(data)) {
|
||||
return mobx.toJS(observable.box(data).get());
|
||||
}
|
||||
|
||||
return mobx.toJS(data);
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { action, ObservableSet } from "mobx";
|
||||
import { ObservableSet } from "mobx";
|
||||
|
||||
export class ToggleSet<T> extends Set<T> {
|
||||
public toggle(value: T): void {
|
||||
@@ -31,7 +31,6 @@ export class ToggleSet<T> extends Set<T> {
|
||||
}
|
||||
|
||||
export class ObservableToggleSet<T> extends ObservableSet<T> {
|
||||
@action
|
||||
public toggle(value: T): void {
|
||||
if (!this.delete(value)) {
|
||||
// Set.prototype.delete returns false if `value` was not in the set
|
||||
|
||||
Reference in new issue
Block a user