* 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>
7.3 KiB
Stores
Stores are components that persist and synchronize state data. Lens uses a number of stores to maintain various kinds of state information, including:
- The
ClusterStoremanages cluster state data (such as cluster details), and it tracks which cluster is active. - The
WorkspaceStoremanages workspace state data (such as the workspace name), and and it tracks which clusters belong to a given workspace. - The
ExtensionStoremanages custom extension state data.
This guide focuses on the ExtensionStore.
ExtensionStore
Extension developers can create their own store for managing state data by extending the ExtensionStore class.
This guide shows how to create a store for the appPreferences guide example, which demonstrates how to add a custom preference to the Preferences page.
The preference is a simple boolean that indicates whether or not something is enabled.
However, in the example, the enabled state is not stored anywhere, and it reverts to the default when Lens is restarted.
Store.ExtensionStore's child class will need to be created before being used.
It is recommended to call the inherited static method getInstanceOrCreate() only in one place, generally within you extension's onActivate() method.
It is also recommenced to delete the instance, using the inherited static method resetInstance(), in your extension's onDeactivate() method.
Everywhere else in your code you should use the getInstance() static method.
This is so that your data is kept up to date and not persisted longer than expected.
The following example code creates a store for the appPreferences guide example:
import { Store } from "@k8slens/extensions";
import { observable, makeObservable } from "mobx";
export type ExamplePreferencesModel = {
enabled: boolean;
};
export class ExamplePreferencesStore extends Store.ExtensionStore<ExamplePreferencesModel> {
@observable enabled = false;
private constructor() {
super({
configName: "example-preferences-store",
defaults: {
enabled: false
}
});
makeObservable(this);
}
protected fromStore({ enabled }: ExamplePreferencesModel): void {
this.enabled = enabled;
}
toJSON(): ExamplePreferencesModel {
return {
enabled: this.enabled
};
}
}
First, our example defines the extension's data model using the simple ExamplePreferencesModel type.
This has a single field, enabled, which represents the preference's state.
ExamplePreferencesStore extends Store.ExtensionStore, which is based on the ExamplePreferencesModel.
The enabled field is added to the ExamplePreferencesStore class to hold the "live" or current state of the preference.
Note the use of the observable decorator on the enabled field.
The appPreferences guide example uses MobX for the UI state management, ensuring the checkbox updates when it's activated by the user.
Next, our example implements the constructor and two abstract methods.
The constructor specifies the name of the store ("example-preferences-store") and the default (initial) value for the preference state (enabled: false).
Lens internals call the fromStore() method when the store loads.
It gives the extension the opportunity to retrieve the stored state data values based on the defined data model.
The enabled field of the ExamplePreferencesStore is set to the value from the store whenever fromStore() is invoked.
The toJSON() method is complementary to fromStore().
It is called when the store is being saved.
toJSON() must provide a JSON serializable object, facilitating its storage in JSON format.
Finally, ExamplePreferencesStore is created by calling ExamplePreferencesStore.getInstanceOrCreate(), and exported for use by other parts of the extension.
Note that ExamplePreferencesStore is a singleton.
Calling this function will create an instance if one has not been made before.
Through normal use you should call ExamplePreferencesStore.getInstance() as that will throw an error if an instance does not exist.
This provides some logical safety in that it limits where a new instance can be created.
Thus it prevents an instance from being created when the constructor args are not present at the call site.
If you are doing some cleanup it is recommended to call ExamplePreferencesStore.getInstance(false) which returns undefined instead of throwing when there is no instance.
The following example code, modified from the appPreferences guide demonstrates how to use the extension store.
ExamplePreferencesStore must be loaded in the main process, where loaded stores are automatically saved when exiting Lens.
This can be done in ./main.ts:
import { LensMainExtension } from "@k8slens/extensions";
import { ExamplePreferencesStore } from "./src/example-preference-store";
export default class ExampleMainExtension extends LensMainExtension {
async onActivate() {
await ExamplePreferencesStore.getInstanceOrCreate().loadExtension(this);
}
}
Here, ExamplePreferencesStore loads with ExamplePreferencesStore.getInstanceOrCreate().loadExtension(this), which is conveniently called from the onActivate() method of ExampleMainExtension.
Similarly, ExamplePreferencesStore must load in the renderer process where the appPreferences are handled.
This can be done in ./renderer.ts:
import { LensRendererExtension } from "@k8slens/extensions";
import { ExamplePreferenceHint, ExamplePreferenceInput } from "./src/example-preference";
import { ExamplePreferencesStore } from "./src/example-preference-store";
import React from "react";
export default class ExampleRendererExtension extends LensRendererExtension {
async onActivate() {
await ExamplePreferencesStore.getInstanceOrCreate().loadExtension(this);
}
appPreferences = [
{
title: "Example Preferences",
components: {
Input: () => <ExamplePreferenceInput />,
Hint: () => <ExamplePreferenceHint/>
}
}
];
}
Again, ExamplePreferencesStore.getInstanceOrCreate().loadExtension(this) is called to load ExamplePreferencesStore, this time from the onActivate() method of ExampleRendererExtension.
ExamplePreferenceInput is defined in ./src/example-preference.tsx:
import { Component } from "@k8slens/extensions";
import { observer } from "mobx-react";
import React from "react";
import { ExamplePreferencesStore } from "./example-preference-store";
@observer
export class ExamplePreferenceInput extends React.Component {
render() {
return (
<Component.Checkbox
label="I understand appPreferences"
value={ExamplePreferencesStore.getInstace().enabled}
onChange={v => { ExamplePreferencesStore.getInstace().enabled = v; }}
/>
);
}
}
export class ExamplePreferenceHint extends React.Component {
render() {
return (
<span>This is an example of an appPreference for extensions.</span>
);
}
}
The only change here is that ExamplePreferenceProps defines its preference field as an ExamplePreferencesStore type.
Everything else works as before, except that now the enabled state persists across Lens restarts because it is managed by the
ExamplePreferencesStore.