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:
RomanandJari Kolehmainen authored and GitHub committed 2021-05-25 10:24:31 +03:00
1 parent 0c45dd807e
commit 2c3b510997
256 files changed
+2359 -1473

No files matched your search

@@ -22,7 +22,7 @@
import "./add-cluster.scss";
import React from "react";
import { observer } from "mobx-react";
import { action, observable, runInAction } from "mobx";
import { action, observable, runInAction, makeObservable } from "mobx";
import { KubeConfig } from "@kubernetes/client-node";
import { AceEditor } from "../ace-editor";
import { Button } from "../button";
@@ -50,6 +50,11 @@ export class AddCluster extends React.Component {
kubeContexts = observable.map<string, KubeConfig>();
constructor(props: {}) {
super(props);
makeObservable(this);
}
componentDidMount() {
appEventBus.emit({ name: "cluster-add", action: "start" });
}
@@ -23,10 +23,10 @@ import "./helm-chart-details.scss";
import React, { Component } from "react";
import { getChartDetails, HelmChart } from "../../api/endpoints/helm-charts.api";
import { observable, autorun } from "mobx";
import { observable, autorun, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Drawer, DrawerItem } from "../drawer";
import { autobind, stopPropagation } from "../../utils";
import { boundMethod, stopPropagation } from "../../utils";
import { MarkdownViewer } from "../markdown-viewer";
import { Spinner } from "../spinner";
import { Button } from "../button";
@@ -48,6 +48,11 @@ export class HelmChartDetails extends Component<Props> {
private abortController?: AbortController;
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentWillUnmount() {
this.abortController?.abort();
}
@@ -67,7 +72,7 @@ export class HelmChartDetails extends Component<Props> {
});
});
@autobind()
@boundMethod
async onVersionChange({ value: version }: SelectOption<string>) {
this.selectedChart = this.chartVersions.find(chart => chart.version === version);
this.readme = null;
@@ -84,7 +89,7 @@ export class HelmChartDetails extends Component<Props> {
}
}
@autobind()
@boundMethod
install() {
createInstallChartTab(this.selectedChart);
this.props.hideDetails();
@@ -20,8 +20,8 @@
*/
import semver from "semver";
import { observable } from "mobx";
import { autobind } from "../../utils";
import { observable, makeObservable } from "mobx";
import { autoBind } from "../../utils";
import { getChartDetails, HelmChart, listCharts } from "../../api/endpoints/helm-charts.api";
import { ItemStore } from "../../item.store";
import flatten from "lodash/flatten";
@@ -31,10 +31,16 @@ export interface IChartVersion {
version: string;
}
@autobind()
export class HelmChartStore extends ItemStore<HelmChart> {
@observable versions = observable.map<string, IChartVersion[]>();
constructor() {
super();
makeObservable(this);
autoBind(this);
}
async loadAll() {
try {
const res = await this.loadItems(() => listCharts());
@@ -57,10 +57,10 @@ export class HelmCharts extends Component<Props> {
showDetails = (chart: HelmChart) => {
if (!chart) {
navigation.merge(helmChartsURL());
navigation.push(helmChartsURL());
}
else {
navigation.merge(helmChartsURL({
navigation.push(helmChartsURL({
params: {
chartName: chart.getName(),
repo: chart.getRepository(),
@@ -24,7 +24,7 @@ import "./release-details.scss";
import React, { Component } from "react";
import groupBy from "lodash/groupBy";
import isEqual from "lodash/isEqual";
import { observable, reaction } from "mobx";
import { observable, reaction, makeObservable } from "mobx";
import { Link } from "react-router-dom";
import kebabCase from "lodash/kebabCase";
import { getRelease, getReleaseValues, HelmRelease, IReleaseDetails } from "../../api/endpoints/helm-releases.api";
@@ -72,7 +72,7 @@ export class ReleaseDetails extends Component<Props> {
);
@disposeOnUnmount
secretWatcher = reaction(() => secretsStore.items.toJS(), () => {
secretWatcher = reaction(() => secretsStore.getItems(), () => {
if (!this.props.release) return;
const { getReleaseSecret } = releaseStore;
const { release } = this.props;
@@ -85,6 +85,11 @@ export class ReleaseDetails extends Component<Props> {
this.releaseSecret = secret;
});
constructor(props: Props) {
super(props);
makeObservable(this);
}
async loadDetails() {
const { release } = this.props;
@@ -21,7 +21,7 @@
import React from "react";
import type { HelmRelease } from "../../api/endpoints/helm-releases.api";
import { autobind, cssNames } from "../../utils";
import { boundMethod, cssNames } from "../../utils";
import { releaseStore } from "./release.store";
import { MenuActions, MenuActionsProps } from "../menu/menu-actions";
import { MenuItem } from "../menu";
@@ -35,12 +35,12 @@ interface Props extends MenuActionsProps {
}
export class HelmReleaseMenu extends React.Component<Props> {
@autobind()
@boundMethod
remove() {
return releaseStore.remove(this.props.release);
}
@autobind()
@boundMethod
upgrade() {
const { release, hideDetails } = this.props;
@@ -48,7 +48,7 @@ export class HelmReleaseMenu extends React.Component<Props> {
hideDetails && hideDetails();
}
@autobind()
@boundMethod
rollback() {
ReleaseRollbackDialog.open(this.props.release);
}
@@ -22,7 +22,7 @@
import "./release-rollback-dialog.scss";
import React from "react";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
@@ -35,26 +35,33 @@ import orderBy from "lodash/orderBy";
interface Props extends DialogProps {
}
const dialogState = observable.object({
isOpen: false,
release: null as HelmRelease,
});
@observer
export class ReleaseRollbackDialog extends React.Component<Props> {
@observable static isOpen = false;
@observable.ref static release: HelmRelease = null;
@observable isLoading = false;
@observable revision: IReleaseRevision;
@observable revisions = observable.array<IReleaseRevision>();
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open(release: HelmRelease) {
ReleaseRollbackDialog.isOpen = true;
ReleaseRollbackDialog.release = release;
dialogState.isOpen = true;
dialogState.release = release;
}
static close() {
ReleaseRollbackDialog.isOpen = false;
dialogState.isOpen = false;
}
get release(): HelmRelease {
return ReleaseRollbackDialog.release;
return dialogState.release;
}
onOpen = async () => {
@@ -115,7 +122,7 @@ export class ReleaseRollbackDialog extends React.Component<Props> {
<Dialog
{...dialogProps}
className="ReleaseRollbackDialog"
isOpen={ReleaseRollbackDialog.isOpen}
isOpen={dialogState.isOpen}
onOpen={this.onOpen}
close={this.close}
>
@@ -20,8 +20,8 @@
*/
import isEqual from "lodash/isEqual";
import { action, observable, reaction, when } from "mobx";
import { autobind } from "../../utils";
import { action, observable, reaction, when, makeObservable } from "mobx";
import { autoBind } from "../../utils";
import { createRelease, deleteRelease, HelmRelease, IReleaseCreatePayload, IReleaseUpdatePayload, listReleases, rollbackRelease, updateRelease } from "../../api/endpoints/helm-releases.api";
import { ItemStore } from "../../item.store";
import type { Secret } from "../../api/endpoints";
@@ -29,19 +29,21 @@ import { secretsStore } from "../+config-secrets/secrets.store";
import { namespaceStore } from "../+namespaces/namespace.store";
import { Notifications } from "../notifications";
@autobind()
export class ReleaseStore extends ItemStore<HelmRelease> {
releaseSecrets = observable.map<string, Secret>();
constructor() {
super();
makeObservable(this);
autoBind(this);
when(() => secretsStore.isLoaded, () => {
this.releaseSecrets.replace(this.getReleaseSecrets());
});
}
watchAssociatedSecrets(): (() => void) {
return reaction(() => secretsStore.items.toJS(), () => {
return reaction(() => secretsStore.getItems(), () => {
if (this.isLoading) return;
const newSecrets = this.getReleaseSecrets();
const amountChanged = newSecrets.length !== this.releaseSecrets.size;
@@ -67,7 +67,7 @@ export class HelmReleases extends Component<Props> {
}
showDetails = (item: HelmRelease) => {
navigation.merge(releaseURL({
navigation.push(releaseURL({
params: {
name: item.getName(),
namespace: item.getNs()
@@ -76,7 +76,7 @@ export class HelmReleases extends Component<Props> {
};
hideDetails = () => {
navigation.merge(releaseURL());
navigation.push(releaseURL());
};
renderRemoveDialogMessage(selectedItems: HelmRelease[]) {
+1 -3
View File
@@ -24,12 +24,10 @@ import { observer } from "mobx-react";
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
import { HelmCharts, helmChartsRoute, helmChartsURL } from "../+apps-helm-charts";
import { HelmReleases, releaseRoute, releaseURL } from "../+apps-releases";
import { namespaceUrlParam } from "../+namespaces/namespace.store";
@observer
export class Apps extends React.Component {
static get tabRoutes(): TabLayoutRoute[] {
const query = namespaceUrlParam.toObjectParam();
return [
{
@@ -41,7 +39,7 @@ export class Apps extends React.Component {
{
title: "Releases",
component: HelmReleases,
url: releaseURL({ query }),
url: releaseURL(),
routePath: releaseRoute.path.toString(),
},
];
@@ -24,8 +24,8 @@ import React from "react";
import { SpeedDial, SpeedDialAction } from "@material-ui/lab";
import { Icon } from "../icon";
import { disposeOnUnmount, observer } from "mobx-react";
import { observable, reaction } from "mobx";
import { autobind } from "../../../common/utils";
import { observable, reaction, makeObservable } from "mobx";
import { boundMethod } from "../../../common/utils";
import type { CatalogCategory, CatalogEntityAddMenuContext, CatalogEntityAddMenu } from "../../api/catalog-entity";
import { EventEmitter } from "events";
import { navigate } from "../../navigation";
@@ -39,6 +39,11 @@ export class CatalogAddButton extends React.Component<CatalogAddButtonProps> {
@observable protected isOpen = false;
protected menuItems = observable.array<CatalogEntityAddMenu>([]);
constructor(props: CatalogAddButtonProps) {
super(props);
makeObservable(this);
}
componentDidMount() {
disposeOnUnmount(this, [
reaction(() => this.props.category, (category) => {
@@ -56,17 +61,17 @@ export class CatalogAddButton extends React.Component<CatalogAddButtonProps> {
]);
}
@autobind()
@boundMethod
onOpen() {
this.isOpen = true;
}
@autobind()
@boundMethod
onClose() {
this.isOpen = false;
}
@autobind()
@boundMethod
onButtonClick() {
if (this.menuItems.length == 1) {
this.menuItems[0].onClick();
@@ -19,12 +19,12 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from "mobx";
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
import type { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity";
import { ItemObject, ItemStore } from "../../item.store";
import { autobind } from "../../utils";
import { CatalogCategory } from "../../../common/catalog";
import { autoBind } from "../../../common/utils";
export class CatalogEntityItem implements ItemObject {
constructor(public entity: CatalogEntity) {}
@@ -84,8 +84,13 @@ export class CatalogEntityItem implements ItemObject {
}
}
@autobind()
export class CatalogEntityStore extends ItemStore<CatalogEntityItem> {
constructor() {
super();
makeObservable(this);
autoBind(this);
}
@observable activeCategory?: CatalogCategory;
@computed get entities() {
+10 -6
View File
@@ -23,7 +23,7 @@ import "./catalog.scss";
import React from "react";
import { disposeOnUnmount, observer } from "mobx-react";
import { ItemListLayout } from "../item-object-list";
import { action, observable, reaction, when } from "mobx";
import { action, makeObservable, observable, reaction, when } from "mobx";
import { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store";
import { navigate } from "../../navigation";
import { kebabCase } from "lodash";
@@ -32,7 +32,6 @@ import { MenuItem, MenuActions } from "../menu";
import { CatalogEntityContextMenu, CatalogEntityContextMenuContext, catalogEntityRunContext } from "../../api/catalog-entity";
import { Badge } from "../badge";
import { HotbarStore } from "../../../common/hotbar-store";
import { autobind } from "../../utils";
import { ConfirmDialog } from "../confirm-dialog";
import { Tab, Tabs } from "../tabs";
import { catalogCategoryRegistry } from "../../../common/catalog";
@@ -49,12 +48,18 @@ enum sortBy {
}
interface Props extends RouteComponentProps<ICatalogViewRouteParam> {}
@observer
export class Catalog extends React.Component<Props> {
@observable private catalogEntityStore?: CatalogEntityStore;
@observable.deep private contextMenu: CatalogEntityContextMenuContext;
@observable private contextMenu: CatalogEntityContextMenuContext;
@observable activeTab?: string;
constructor(props: Props) {
super(props);
makeObservable(this);
}
get routeActiveTab(): string | undefined {
const { group, kind } = this.props.match.params ?? {};
@@ -155,8 +160,7 @@ export class Catalog extends React.Component<Props> {
);
}
@autobind()
renderItemMenu(item: CatalogEntityItem) {
renderItemMenu = (item: CatalogEntityItem) => {
const menuItems = this.contextMenu.menuItems.filter((menuItem) => !menuItem.onlyVisibleForSource || menuItem.onlyVisibleForSource === item.entity.metadata.source);
return (
@@ -173,7 +177,7 @@ export class Catalog extends React.Component<Props> {
</MenuItem>
</MenuActions>
);
}
};
renderIcon(item: CatalogEntityItem) {
const category = catalogCategoryRegistry.getCategoryForEntity(item.entity);
@@ -23,13 +23,13 @@ import "./cluster-issues.scss";
import React from "react";
import { observer } from "mobx-react";
import { computed } from "mobx";
import { computed, makeObservable } from "mobx";
import { Icon } from "../icon";
import { SubHeader } from "../layout/sub-header";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { nodesStore } from "../+nodes/nodes.store";
import { eventStore } from "../+events/event.store";
import { autobind, cssNames, prevDefault } from "../../utils";
import { boundMethod, cssNames, prevDefault } from "../../utils";
import type { ItemObject } from "../../item.store";
import { Spinner } from "../spinner";
import { ThemeStore } from "../../theme.store";
@@ -62,6 +62,11 @@ export class ClusterIssues extends React.Component<Props> {
[sortBy.age]: (warning: IWarning) => warning.timeDiffFromNow,
};
constructor(props: Props) {
super(props);
makeObservable(this);
}
@computed get warnings() {
const warnings: IWarning[] = [];
@@ -103,7 +108,7 @@ export class ClusterIssues extends React.Component<Props> {
return warnings;
}
@autobind()
@boundMethod
getTableRow(uid: string) {
const { warnings } = this;
const warning = warnings.find(warn => warn.getId() == uid);
@@ -19,10 +19,10 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { action, observable, reaction, when } from "mobx";
import { action, observable, reaction, when, makeObservable } from "mobx";
import { KubeObjectStore } from "../../kube-object.store";
import { Cluster, clusterApi, IClusterMetrics } from "../../api/endpoints";
import { autobind, createStorage } from "../../utils";
import { autoBind, createStorage } from "../../utils";
import { IMetricsReqParams, normalizeMetrics } from "../../api/endpoints/metrics.api";
import { nodesStore } from "../+nodes/nodes.store";
import { apiManager } from "../../api/api-manager";
@@ -42,7 +42,6 @@ export interface ClusterOverviewStorageState {
metricNodeRole: MetricNodeRole,
}
@autobind()
export class ClusterOverviewStore extends KubeObjectStore<Cluster> implements ClusterOverviewStorageState {
api = clusterApi;
@@ -72,6 +71,9 @@ export class ClusterOverviewStore extends KubeObjectStore<Cluster> implements Cl
constructor() {
super();
makeObservable(this);
autoBind(this);
this.init();
}
@@ -19,12 +19,10 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store";
import { HorizontalPodAutoscaler, hpaApi } from "../../api/endpoints/hpa.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class HPAStore extends KubeObjectStore<HorizontalPodAutoscaler> {
api = hpaApi;
}
@@ -19,12 +19,10 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../../common/utils/autobind";
import { KubeObjectStore } from "../../kube-object.store";
import { apiManager } from "../../api/api-manager";
import { LimitRange, limitRangeApi } from "../../api/endpoints/limit-range.api";
@autobind()
export class LimitRangesStore extends KubeObjectStore<LimitRange> {
api = limitRangeApi;
}
@@ -22,7 +22,7 @@
import "./config-map-details.scss";
import React from "react";
import { autorun, observable } from "mobx";
import { autorun, makeObservable, observable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { DrawerTitle } from "../drawer";
import { Notifications } from "../notifications";
@@ -41,7 +41,12 @@ interface Props extends KubeObjectDetailsProps<ConfigMap> {
@observer
export class ConfigMapDetails extends React.Component<Props> {
@observable isSaving = false;
@observable data = observable.map();
@observable data = observable.map<string, string>();
constructor(props: Props) {
super(props);
makeObservable(this);
}
async componentDidMount() {
disposeOnUnmount(this, [
@@ -60,7 +65,10 @@ export class ConfigMapDetails extends React.Component<Props> {
try {
this.isSaving = true;
await configMapsStore.update(configMap, { ...configMap, data: this.data.toJSON() });
await configMapsStore.update(configMap, {
...configMap,
data: Object.fromEntries(this.data),
});
Notifications.ok(
<p>
<>ConfigMap <b>{configMap.getName()}</b> successfully updated.</>
@@ -75,7 +83,7 @@ export class ConfigMapDetails extends React.Component<Props> {
const { object: configMap } = this.props;
if (!configMap) return null;
const data = Object.entries(this.data.toJSON());
const data = Array.from(this.data.entries());
return (
<div className="ConfigMapDetails">
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { ConfigMap, configMapApi } from "../../api/endpoints/configmap.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class ConfigMapsStore extends KubeObjectStore<ConfigMap> {
api = configMapApi;
}
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { PodDisruptionBudget, pdbApi } from "../../api/endpoints/poddisruptionbudget.api";
import { pdbApi, PodDisruptionBudget } from "../../api/endpoints/poddisruptionbudget.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class PodDisruptionBudgetsStore extends KubeObjectStore<PodDisruptionBudget> {
api = pdbApi;
}
@@ -22,7 +22,7 @@
import "./add-quota-dialog.scss";
import React from "react";
import { computed, observable } from "mobx";
import { computed, observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
@@ -39,10 +39,12 @@ import { SubTitle } from "../layout/sub-title";
interface Props extends DialogProps {
}
const dialogState = observable.object({
isOpen: false,
});
@observer
export class AddQuotaDialog extends React.Component<Props> {
@observable static isOpen = false;
static defaultQuotas: IResourceQuotaValues = {
"limits.cpu": "",
"limits.memory": "",
@@ -72,12 +74,17 @@ export class AddQuotaDialog extends React.Component<Props> {
@observable namespace = this.defaultNamespace;
@observable quotas = AddQuotaDialog.defaultQuotas;
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open() {
AddQuotaDialog.isOpen = true;
dialogState.isOpen = true;
}
static close() {
AddQuotaDialog.isOpen = false;
dialogState.isOpen = false;
}
@computed get quotaEntries() {
@@ -154,7 +161,7 @@ export class AddQuotaDialog extends React.Component<Props> {
<Dialog
{...dialogProps}
className="AddQuotaDialog"
isOpen={AddQuotaDialog.isOpen}
isOpen={dialogState.isOpen}
close={this.close}
>
<Wizard header={header} done={this.close}>
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { ResourceQuota, resourceQuotaApi } from "../../api/endpoints/resource-quota.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class ResourceQuotasStore extends KubeObjectStore<ResourceQuota> {
api = resourceQuotaApi;
}
@@ -22,7 +22,7 @@
import "./add-secret-dialog.scss";
import React from "react";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
@@ -57,16 +57,23 @@ interface ISecretTemplate {
type ISecretField = keyof ISecretTemplate;
const dialogState = observable.object({
isOpen: false,
});
@observer
export class AddSecretDialog extends React.Component<Props> {
@observable static isOpen = false;
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open() {
AddSecretDialog.isOpen = true;
dialogState.isOpen = true;
}
static close() {
AddSecretDialog.isOpen = false;
dialogState.isOpen = false;
}
private secretTemplate: { [p: string]: ISecretTemplate } = {
@@ -205,7 +212,7 @@ export class AddSecretDialog extends React.Component<Props> {
<Dialog
{...dialogProps}
className="AddSecretDialog"
isOpen={AddSecretDialog.isOpen}
isOpen={dialogState.isOpen}
onOpen={this.reset}
close={this.close}
>
@@ -23,7 +23,7 @@ import "./secret-details.scss";
import React from "react";
import isEmpty from "lodash/isEmpty";
import { autorun, observable } from "mobx";
import { autorun, observable, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { DrawerItem, DrawerTitle } from "../drawer";
import { Input } from "../input";
@@ -46,6 +46,11 @@ export class SecretDetails extends React.Component<Props> {
@observable data: { [name: string]: string } = {};
@observable revealSecret: { [name: string]: boolean } = {};
constructor(props: Props) {
super(props);
makeObservable(this);
}
async componentDidMount() {
disposeOnUnmount(this, [
autorun(() => {
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { Secret, secretsApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
@autobind()
export class SecretsStore extends KubeObjectStore<Secret> {
api = secretsApi;
}
+6 -8
View File
@@ -24,7 +24,6 @@ import { observer } from "mobx-react";
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
import { ConfigMaps, configMapsRoute, configMapsURL } from "../+config-maps";
import { Secrets, secretsRoute, secretsURL } from "../+config-secrets";
import { namespaceUrlParam } from "../+namespaces/namespace.store";
import { resourceQuotaRoute, ResourceQuotas, resourceQuotaURL } from "../+config-resource-quotas";
import { pdbRoute, pdbURL, PodDisruptionBudgets } from "../+config-pod-disruption-budgets";
import { HorizontalPodAutoscalers, hpaRoute, hpaURL } from "../+config-autoscalers";
@@ -34,14 +33,13 @@ import { LimitRanges, limitRangesRoute, limitRangeURL } from "../+config-limit-r
@observer
export class Config extends React.Component {
static get tabRoutes(): TabLayoutRoute[] {
const query = namespaceUrlParam.toObjectParam();
const routes: TabLayoutRoute[] = [];
if (isAllowedResource("configmaps")) {
routes.push({
title: "ConfigMaps",
component: ConfigMaps,
url: configMapsURL({ query }),
url: configMapsURL(),
routePath: configMapsRoute.path.toString(),
});
}
@@ -50,7 +48,7 @@ export class Config extends React.Component {
routes.push({
title: "Secrets",
component: Secrets,
url: secretsURL({ query }),
url: secretsURL(),
routePath: secretsRoute.path.toString(),
});
}
@@ -59,7 +57,7 @@ export class Config extends React.Component {
routes.push({
title: "Resource Quotas",
component: ResourceQuotas,
url: resourceQuotaURL({ query }),
url: resourceQuotaURL(),
routePath: resourceQuotaRoute.path.toString(),
});
}
@@ -68,7 +66,7 @@ export class Config extends React.Component {
routes.push({
title: "Limit Ranges",
component: LimitRanges,
url: limitRangeURL({ query }),
url: limitRangeURL(),
routePath: limitRangesRoute.path.toString(),
});
}
@@ -77,7 +75,7 @@ export class Config extends React.Component {
routes.push({
title: "HPA",
component: HorizontalPodAutoscalers,
url: hpaURL({ query }),
url: hpaURL(),
routePath: hpaRoute.path.toString(),
});
}
@@ -86,7 +84,7 @@ export class Config extends React.Component {
routes.push({
title: "Pod Disruption Budgets",
component: PodDisruptionBudgets,
url: pdbURL({ query }),
url: pdbURL(),
routePath: pdbRoute.path.toString(),
});
}
@@ -22,7 +22,7 @@
import "./crd-list.scss";
import React from "react";
import { computed } from "mobx";
import { computed, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Link } from "react-router-dom";
import { stopPropagation } from "../../utils";
@@ -35,8 +35,6 @@ import { Icon } from "../icon";
export const crdGroupsUrlParam = createPageParam<string[]>({
name: "groups",
multiValues: true,
isSystem: true,
defaultValue: [],
});
@@ -50,6 +48,11 @@ enum columnId {
@observer
export class CrdList extends React.Component {
constructor(props: {}) {
super(props);
makeObservable(this);
}
get selectedGroups(): string[] {
return crdGroupsUrlParam.get();
}
@@ -24,7 +24,7 @@ import "./crd-resource-details.scss";
import React from "react";
import jsonPath from "jsonpath";
import { observer } from "mobx-react";
import { computed } from "mobx";
import { computed, makeObservable } from "mobx";
import { cssNames } from "../../utils";
import { Badge } from "../badge";
import { DrawerItem } from "../drawer";
@@ -60,6 +60,11 @@ function convertSpecValue(value: any): any {
@observer
export class CrdResourceDetails extends React.Component<Props> {
constructor(props: Props) {
super(props);
makeObservable(this);
}
@computed get crd() {
return crdStore.getByObject(this.props.object);
}
@@ -19,12 +19,10 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../utils";
import { KubeApi } from "../../api/kube-api";
import type { KubeApi } from "../../api/kube-api";
import { KubeObjectStore } from "../../kube-object.store";
import type { KubeObject } from "../../api/kube-object";
@autobind()
export class CRDResourceStore<T extends KubeObject = any> extends KubeObjectStore<T> {
api: KubeApi;
@@ -28,7 +28,7 @@ import type { RouteComponentProps } from "react-router";
import { KubeObjectListLayout } from "../kube-object";
import type { KubeObject } from "../../api/kube-object";
import type { ICRDRouteParams } from "./crd.route";
import { autorun, computed } from "mobx";
import { autorun, computed, makeObservable } from "mobx";
import { crdStore } from "./crd.store";
import type { TableSortCallback } from "../table";
import { apiManager } from "../../api/api-manager";
@@ -45,6 +45,11 @@ enum columnId {
@observer
export class CrdResources extends React.Component<Props> {
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentDidMount() {
disposeOnUnmount(this, [
autorun(() => {
@@ -19,9 +19,9 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { computed, reaction } from "mobx";
import { computed, reaction, makeObservable } from "mobx";
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { crdApi, CustomResourceDefinition } from "../../api/endpoints/crd.api";
import { apiManager } from "../../api/api-manager";
import { KubeApi } from "../../api/kube-api";
@@ -39,15 +39,17 @@ function initStore(crd: CustomResourceDefinition) {
}
}
@autobind()
export class CRDStore extends KubeObjectStore<CustomResourceDefinition> {
api = crdApi;
constructor() {
super();
makeObservable(this);
autoBind(this);
// auto-init stores for crd-s
reaction(() => this.items.toJS(), items => items.forEach(initStore));
reaction(() => this.getItems(), items => items.forEach(initStore));
}
protected sortItems(items: CustomResourceDefinition[]) {
@@ -22,7 +22,7 @@
import "./entity-settings.scss";
import React from "react";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import type { RouteComponentProps } from "react-router";
import { observer } from "mobx-react";
import { PageLayout } from "../layout/page-layout";
@@ -41,6 +41,11 @@ interface Props extends RouteComponentProps<EntitySettingsRouteParams> {
export class EntitySettings extends React.Component<Props> {
@observable activeTab: string;
constructor(props: Props) {
super(props);
makeObservable(this);
}
get entityId() {
return this.props.match.params.entityId;
}
@@ -22,19 +22,23 @@
import groupBy from "lodash/groupBy";
import compact from "lodash/compact";
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { eventApi, KubeEvent } from "../../api/endpoints/events.api";
import type { KubeObject } from "../../api/kube-object";
import { Pod } from "../../api/endpoints/pods.api";
import { podsStore } from "../+workloads-pods/pods.store";
import { apiManager } from "../../api/api-manager";
@autobind()
export class EventStore extends KubeObjectStore<KubeEvent> {
api = eventApi;
limit = 1000;
saveLimit = 50000;
constructor() {
super();
autoBind(this);
}
protected bindWatchEventsUpdater() {
return super.bindWatchEventsUpdater(5000);
}
+6 -1
View File
@@ -22,7 +22,7 @@
import "./events.scss";
import React, { Fragment } from "react";
import { computed, observable } from "mobx";
import { computed, observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { orderBy } from "lodash";
import { TabLayout } from "../layout/tab-layout";
@@ -81,6 +81,11 @@ export class Events extends React.Component<Props> {
onSort: params => this.sorting = params,
};
constructor(props: Props) {
super(props);
makeObservable(this);
}
get store(): EventStore {
return eventStore;
}
@@ -24,33 +24,17 @@ import "./extensions.scss";
import { remote, shell } from "electron";
import fse from "fs-extra";
import _ from "lodash";
import { observable, reaction, when } from "mobx";
import { makeObservable, observable, reaction, when } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import os from "os";
import path from "path";
import React from "react";
import { SemVer } from "semver";
import URLParse from "url-parse";
import {
Disposer,
disposer,
downloadFile,
downloadJson,
ExtendableDisposer,
extractTar,
listTarEntries,
noop,
readFileFromTar,
} from "../../../common/utils";
import { Disposer, disposer, downloadFile, downloadJson, ExtendableDisposer, extractTar, listTarEntries, noop, readFileFromTar, } from "../../../common/utils";
import { ExtensionDiscovery, InstalledExtension, manifestFilename } from "../../../extensions/extension-discovery";
import { ExtensionLoader } from "../../../extensions/extension-loader";
import {
extensionDisplayName,
LensExtensionId,
LensExtensionManifest,
sanitizeExtensionName,
} from "../../../extensions/lens-extension";
import { extensionDisplayName, LensExtensionId, LensExtensionManifest, sanitizeExtensionName, } from "../../../extensions/lens-extension";
import logger from "../../../main/logger";
import { Button } from "../button";
import { ConfirmDialog } from "../confirm-dialog";
@@ -193,7 +177,7 @@ async function validatePackage(filePath: string): Promise<LensExtensionManifest>
// tarball from npm contains single root folder "package/*"
const firstFile = tarFiles[0];
if(!firstFile) {
if (!firstFile) {
throw new Error(`invalid extension bundle, ${manifestFilename} not found`);
}
@@ -201,7 +185,7 @@ async function validatePackage(filePath: string): Promise<LensExtensionManifest>
const packedInRootFolder = tarFiles.every(entry => entry.startsWith(rootFolder));
const manifestLocation = packedInRootFolder ? path.join(rootFolder, manifestFilename) : manifestFilename;
if(!tarFiles.includes(manifestLocation)) {
if (!tarFiles.includes(manifestLocation)) {
throw new Error(`invalid extension bundle, ${manifestFilename} not found`);
}
@@ -415,7 +399,7 @@ async function attemptInstall(request: InstallRequest, d?: ExtendableDisposer):
} else {
dispose();
}
}} />
}}/>
</div>,
{
onClose: dispose,
@@ -460,7 +444,7 @@ async function installFromInput(input: string) {
await attemptInstall({ fileName, dataP: readFileNotify(input) });
} else if (InputValidators.isExtensionNameInstall.validate(input)) {
const [{ groups: { name, version }}] = [...input.matchAll(InputValidators.isExtensionNameInstallRegex)];
const [{ groups: { name, version } }] = [...input.matchAll(InputValidators.isExtensionNameInstallRegex)];
await attemptInstallByInfo({ name, version });
}
@@ -493,10 +477,18 @@ async function installFromSelectFileDialog() {
}
}
interface Props {
}
@observer
export class Extensions extends React.Component {
export class Extensions extends React.Component<Props> {
@observable installPath = "";
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentDidMount() {
// TODO: change this after upgrading to mobx6 as that versions' reactions have this functionality
let prevSize = ExtensionLoader.getInstance().userExtensions.size;
@@ -22,7 +22,7 @@
import "./add-namespace-dialog.scss";
import React from "react";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
@@ -37,17 +37,25 @@ interface Props extends DialogProps {
onError?(error: any): void;
}
const dialogState = observable.object({
isOpen: false,
});
@observer
export class AddNamespaceDialog extends React.Component<Props> {
@observable static isOpen = false;
@observable namespace = "";
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open() {
AddNamespaceDialog.isOpen = true;
dialogState.isOpen = true;
}
static close() {
AddNamespaceDialog.isOpen = false;
dialogState.isOpen = false;
}
reset = () => {
@@ -78,7 +86,7 @@ export class AddNamespaceDialog extends React.Component<Props> {
<Dialog
{...dialogProps}
className="AddNamespaceDialog"
isOpen={AddNamespaceDialog.isOpen}
isOpen={dialogState.isOpen}
onOpen={this.reset}
close={AddNamespaceDialog.close}
>
@@ -22,7 +22,7 @@
import "./namespace-details.scss";
import React from "react";
import { computed } from "mobx";
import { computed, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { DrawerItem } from "../drawer";
import { cssNames } from "../../utils";
@@ -40,6 +40,11 @@ interface Props extends KubeObjectDetailsProps<Namespace> {
@observer
export class NamespaceDetails extends React.Component<Props> {
constructor(props: Props) {
super(props);
makeObservable(this);
}
@computed get quotas() {
const namespace = this.props.object.getName();
@@ -22,7 +22,7 @@
import "./namespace-select.scss";
import React from "react";
import { computed } from "mobx";
import { computed, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { Select, SelectOption, SelectProps } from "../select";
import { cssNames } from "../../utils";
@@ -46,6 +46,11 @@ const defaultProps: Partial<Props> = {
export class NamespaceSelect extends React.Component<Props> {
static defaultProps = defaultProps as object;
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentDidMount() {
disposeOnUnmount(this, [
kubeWatchApi.subscribeStores([namespaceStore], {
@@ -19,71 +19,39 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { action, comparer, computed, IReactionDisposer, IReactionOptions, observable, reaction } from "mobx";
import { autobind, createStorage } from "../../utils";
import { action, comparer, computed, IReactionDisposer, IReactionOptions, makeObservable, reaction, } from "mobx";
import { autoBind, createStorage } from "../../utils";
import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store";
import { Namespace, namespacesApi } from "../../api/endpoints/namespaces.api";
import { createPageParam } from "../../navigation";
import { apiManager } from "../../api/api-manager";
const selectedNamespaces = createStorage<string[] | undefined>("selected_namespaces", undefined);
export const namespaceUrlParam = createPageParam<string[]>({
name: "namespaces",
isSystem: true,
multiValues: true,
defaultValue: [],
});
export function getDummyNamespace(name: string) {
return new Namespace({
kind: Namespace.kind,
apiVersion: "v1",
metadata: {
name,
uid: "",
resourceVersion: "",
selfLink: `/api/v1/namespaces/${name}`
}
});
}
@autobind()
export class NamespaceStore extends KubeObjectStore<Namespace> {
api = namespacesApi;
@observable private contextNs = observable.set<string>();
private storage = createStorage<string[] | undefined>("selected_namespaces", undefined);
constructor() {
super();
makeObservable(this);
autoBind(this);
this.init();
}
private async init() {
await this.contextReady;
await selectedNamespaces.whenReady;
await this.storage.whenReady;
this.setContext(this.initialNamespaces);
this.selectNamespaces(this.initialNamespaces);
this.autoLoadAllowedNamespaces();
this.autoUpdateUrlAndLocalStorage();
}
public onContextChange(callback: (contextNamespaces: string[]) => void, opts: IReactionOptions = {}): IReactionDisposer {
return reaction(() => Array.from(this.contextNs), callback, {
public onContextChange(callback: (namespaces: string[]) => void, opts: IReactionOptions = {}): IReactionDisposer {
return reaction(() => Array.from(this.contextNamespaces), callback, {
equals: comparer.shallow,
...opts,
});
}
private autoUpdateUrlAndLocalStorage(): IReactionDisposer {
return this.onContextChange(namespaces => {
selectedNamespaces.set(namespaces); // save to local-storage
namespaceUrlParam.set(namespaces, { replaceHistory: true }); // update url
}, {
fireImmediately: true,
});
}
private autoLoadAllowedNamespaces(): IReactionDisposer {
return reaction(() => this.allowedNamespaces, namespaces => this.loadAll({ namespaces }), {
fireImmediately: true,
@@ -92,24 +60,28 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
}
private get initialNamespaces(): string[] {
const namespaces = new Set(this.allowedNamespaces);
const prevSelectedNamespaces = selectedNamespaces.get();
const { allowedNamespaces } = this;
const selectedNamespaces = this.storage.get(); // raw namespaces, undefined on first load
// return previously saved namespaces from local-storage (if any)
if (prevSelectedNamespaces) {
return prevSelectedNamespaces.filter(namespace => namespaces.has(namespace));
if (Array.isArray(selectedNamespaces)) {
return selectedNamespaces.filter(namespace => allowedNamespaces.includes(namespace));
}
// otherwise select "default" or first allowed namespace
if (namespaces.has("default")) {
if (allowedNamespaces.includes("default")) {
return ["default"];
} else if (namespaces.size) {
return [Array.from(namespaces)[0]];
} else if (allowedNamespaces.length) {
return [allowedNamespaces[0]];
}
return [];
}
@computed get selectedNamespaces(): string[] {
return this.storage.get() ?? [];
}
@computed get allowedNamespaces(): string[] {
return Array.from(new Set([
...(this.context?.allNamespaces ?? []), // allowed namespaces from cluster (main), updating every 30s
@@ -118,13 +90,11 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
}
@computed get contextNamespaces(): string[] {
const namespaces = Array.from(this.contextNs);
if (!namespaces.length) {
if (!this.selectedNamespaces.length) {
return this.allowedNamespaces; // show all namespaces when nothing selected
}
return namespaces;
return this.selectedNamespaces;
}
getSubscribeApis() {
@@ -151,31 +121,40 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
}
@action
setContext(namespace: string | string[]) {
const namespaces = [namespace].flat();
selectNamespaces(namespace: string | string[]) {
const namespaces = Array.from(new Set([namespace].flat()));
this.contextNs.replace(namespaces);
this.storage.set(namespaces);
}
@action
resetContext() {
this.contextNs.clear();
clearSelected(namespaces?: string | string[]) {
if (namespaces) {
const resettingNamespaces = [namespaces].flat();
const newNamespaces = this.storage.get().filter(ns => !resettingNamespaces.includes(ns));
this.storage.set(newNamespaces);
} else {
this.storage.reset();
}
}
hasContext(namespaces: string | string[]) {
return [namespaces].flat().every(namespace => this.contextNs.has(namespace));
hasContext(namespaces: string | string[]): boolean {
return [namespaces]
.flat()
.every(namespace => this.selectedNamespaces.includes(namespace));
}
@computed get hasAllContexts(): boolean {
return this.contextNs.size === this.allowedNamespaces.length;
return this.selectedNamespaces.length === this.allowedNamespaces.length;
}
@action
toggleContext(namespace: string) {
if (this.hasContext(namespace)) {
this.contextNs.delete(namespace);
toggleContext(namespaces: string | string[]) {
if (this.hasContext(namespaces)) {
this.clearSelected(namespaces);
} else {
this.contextNs.add(namespace);
this.selectNamespaces([this.selectedNamespaces, namespaces].flat());
}
}
@@ -183,9 +162,9 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
toggleAll(showAll?: boolean) {
if (typeof showAll === "boolean") {
if (showAll) {
this.setContext(this.allowedNamespaces);
this.selectNamespaces(this.allowedNamespaces);
} else {
this.resetContext(); // empty context considered as "All namespaces"
this.selectNamespaces([]); // empty context considered as "All namespaces"
}
} else {
this.toggleAll(!this.hasAllContexts);
@@ -195,9 +174,22 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
@action
async remove(item: Namespace) {
await super.remove(item);
this.contextNs.delete(item.getName());
this.clearSelected(item.getName());
}
}
export const namespaceStore = new NamespaceStore();
apiManager.registerStore(namespaceStore);
export function getDummyNamespace(name: string) {
return new Namespace({
kind: Namespace.kind,
apiVersion: "v1",
metadata: {
name,
uid: "",
resourceVersion: "",
selfLink: `/api/v1/namespaces/${name}`
}
});
}
@@ -25,7 +25,7 @@ import React from "react";
import { observer } from "mobx-react";
import { EndpointSubset, Endpoint, EndpointAddress} from "../../api/endpoints";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { autobind } from "../../utils";
import { boundMethod } from "../../utils";
import { lookupApiLink } from "../../api/kube-api";
import { Link } from "react-router-dom";
import { getDetailsUrl } from "../kube-object";
@@ -45,7 +45,7 @@ export class EndpointSubsetList extends React.Component<Props> {
return this.renderAddressTableRow(address);
}
@autobind()
@boundMethod
getNotReadyAddressTableRow(ip: string) {
const { subset} = this.props;
const address = subset.getNotReadyAddresses().find(address => address.getId() == ip);
@@ -53,7 +53,7 @@ export class EndpointSubsetList extends React.Component<Props> {
return this.renderAddressTableRow(address);
}
@autobind()
@boundMethod
renderAddressTable(addresses: EndpointAddress[], virtual: boolean) {
return (
<div>
@@ -79,7 +79,7 @@ export class EndpointSubsetList extends React.Component<Props> {
);
}
@autobind()
@boundMethod
renderAddressTableRow(address: EndpointAddress) {
const { endpoint } = this.props;
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { Endpoint, endpointApi } from "../../api/endpoints/endpoint.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class EndpointStore extends KubeObjectStore<Endpoint> {
api = endpointApi;
}
@@ -19,17 +19,23 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { IIngressMetrics, Ingress, ingressApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
@autobind()
export class IngressStore extends KubeObjectStore<Ingress> {
api = ingressApi;
@observable metrics: IIngressMetrics = null;
constructor() {
super();
makeObservable(this);
autoBind(this);
}
async loadMetrics(ingress: Ingress) {
this.metrics = await this.api.getMetrics(ingress.getName(), ingress.getNs());
}
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { NetworkPolicy, networkPolicyApi } from "../../api/endpoints/network-policy.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class NetworkPolicyStore extends KubeObjectStore<NetworkPolicy> {
api = networkPolicyApi;
}
@@ -25,7 +25,7 @@ import React from "react";
import { observer } from "mobx-react";
import type { Service, ServicePort } from "../../api/endpoints";
import { apiBase } from "../../api";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { cssNames } from "../../utils";
import { Notifications } from "../notifications";
import { Spinner } from "../spinner";
@@ -39,6 +39,11 @@ interface Props {
export class ServicePortComponent extends React.Component<Props> {
@observable waiting = false;
constructor(props: Props) {
super(props);
makeObservable(this);
}
async portForward() {
const { service, port } = this.props;
@@ -20,11 +20,9 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { Service, serviceApi } from "../../api/endpoints/service.api";
import { apiManager } from "../../api/api-manager";
@autobind()
export class ServiceStore extends KubeObjectStore<Service> {
api = serviceApi;
}
+4 -6
View File
@@ -28,20 +28,18 @@ import { Services, servicesRoute, servicesURL } from "../+network-services";
import { endpointRoute, Endpoints, endpointURL } from "../+network-endpoints";
import { Ingresses, ingressRoute, ingressURL } from "../+network-ingresses";
import { NetworkPolicies, networkPoliciesRoute, networkPoliciesURL } from "../+network-policies";
import { namespaceUrlParam } from "../+namespaces/namespace.store";
import { isAllowedResource } from "../../../common/rbac";
@observer
export class Network extends React.Component {
static get tabRoutes(): TabLayoutRoute[] {
const query = namespaceUrlParam.toObjectParam();
const routes: TabLayoutRoute[] = [];
if (isAllowedResource("services")) {
routes.push({
title: "Services",
component: Services,
url: servicesURL({ query }),
url: servicesURL(),
routePath: servicesRoute.path.toString(),
});
}
@@ -50,7 +48,7 @@ export class Network extends React.Component {
routes.push({
title: "Endpoints",
component: Endpoints,
url: endpointURL({ query }),
url: endpointURL(),
routePath: endpointRoute.path.toString(),
});
}
@@ -59,7 +57,7 @@ export class Network extends React.Component {
routes.push({
title: "Ingresses",
component: Ingresses,
url: ingressURL({ query }),
url: ingressURL(),
routePath: ingressRoute.path.toString(),
});
}
@@ -68,7 +66,7 @@ export class Network extends React.Component {
routes.push({
title: "Network Policies",
component: NetworkPolicies,
url: networkPoliciesURL({ query }),
url: networkPoliciesURL(),
routePath: networkPoliciesRoute.path.toString(),
});
}
@@ -20,13 +20,12 @@
*/
import { sum } from "lodash";
import { action, computed, observable } from "mobx";
import { action, computed, observable, makeObservable } from "mobx";
import { clusterApi, IClusterMetrics, INodeMetrics, Node, nodesApi } from "../../api/endpoints";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store";
import { apiManager } from "../../api/api-manager";
@autobind()
export class NodesStore extends KubeObjectStore<Node> {
api = nodesApi;
@@ -35,6 +34,13 @@ export class NodesStore extends KubeObjectStore<Node> {
@observable metricsLoading = false;
@observable metricsLoaded = false;
constructor() {
super();
makeObservable(this);
autoBind(this);
}
@action
async loadUsageMetrics() {
this.metricsLoading = true;
@@ -20,11 +20,9 @@
*/
import { PodSecurityPolicy, pspApi } from "../../api/endpoints";
import { autobind } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store";
import { apiManager } from "../../api/api-manager";
@autobind()
export class PodSecurityPoliciesStore extends KubeObjectStore<PodSecurityPolicy> {
api = pspApi;
}
@@ -23,7 +23,7 @@ import "./add-helm-repo-dialog.scss";
import React from "react";
import { remote, FileFilter } from "electron";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
@@ -46,6 +46,10 @@ enum FileType {
CertFile = "certFile",
}
const dialogState = observable.object({
isOpen: false,
});
@observer
export class AddHelmRepoDialog extends React.Component<Props> {
private emptyRepo = {name: "", url: "", username: "", password: "", insecureSkipTlsVerify: false, caFile:"", keyFile: "", certFile: ""};
@@ -53,14 +57,17 @@ export class AddHelmRepoDialog extends React.Component<Props> {
private static keyExtensions = ["key", "keystore", "jks", "p12", "pfx", "pem"];
private static certExtensions = ["crt", "cer", "ca-bundle", "p7b", "p7c" , "p7s", "p12", "pfx", "pem"];
@observable static isOpen = false;
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open() {
AddHelmRepoDialog.isOpen = true;
dialogState.isOpen = true;
}
static close() {
AddHelmRepoDialog.isOpen = false;
dialogState.isOpen = false;
}
@observable helmRepo : HelmRepo = this.emptyRepo;
@@ -161,7 +168,7 @@ export class AddHelmRepoDialog extends React.Component<Props> {
<Dialog
{...dialogProps}
className="AddHelmRepoDialog"
isOpen={AddHelmRepoDialog.isOpen}
isOpen={dialogState.isOpen}
close={this.close}
>
<Wizard header={header} done={this.close}>
@@ -22,7 +22,7 @@
import "./helm-charts.scss";
import React from "react";
import { action, computed, observable } from "mobx";
import { action, computed, observable, makeObservable } from "mobx";
import { HelmRepo, HelmRepoManager } from "../../../main/helm/helm-repo-manager";
import { Button } from "../button";
@@ -38,6 +38,11 @@ export class HelmCharts extends React.Component {
@observable repos: HelmRepo[] = [];
@observable addedRepos = observable.map<string, HelmRepo>();
constructor(props: {}) {
super(props);
makeObservable(this);
}
@computed get options(): SelectOption<HelmRepo>[] {
return this.repos.map(repo => ({
label: repo.name,
@@ -23,7 +23,7 @@ import React from "react";
import { remote } from "electron";
import { Avatar, IconButton, List, ListItem, ListItemAvatar, ListItemSecondaryAction, ListItemText, Paper } from "@material-ui/core";
import { Description, Folder, Delete, HelpOutline } from "@material-ui/icons";
import { action, computed, observable, reaction } from "mobx";
import { action, computed, observable, reaction, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import fse from "fs-extra";
import { KubeconfigSyncEntry, KubeconfigSyncValue, UserStore } from "../../../common/user-store";
@@ -74,6 +74,11 @@ export class KubeconfigSyncs extends React.Component {
syncs = observable.map<string, Value>();
@observable loaded = false;
constructor(props: {}) {
super(props);
makeObservable(this);
}
async componentDidMount() {
const mapEntries = await Promise.all(
iter.map(
@@ -23,7 +23,7 @@ import "./preferences.scss";
import React from "react";
import moment from "moment-timezone";
import { computed, observable, reaction } from "mobx";
import { computed, observable, reaction, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { isWindows } from "../../../common/vars";
@@ -56,6 +56,11 @@ export class Preferences extends React.Component {
@observable shell = UserStore.getInstance().shell || "";
@observable activeTab = Pages.Application;
constructor(props: {}) {
super(props);
makeObservable(this);
}
@computed get themeOptions(): SelectOption<string>[] {
return ThemeStore.getInstance().themes.map(theme => ({
label: theme.name,
@@ -20,15 +20,19 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { StorageClass, storageClassApi } from "../../api/endpoints/storage-class.api";
import { apiManager } from "../../api/api-manager";
import { volumesStore } from "../+storage-volumes/volumes.store";
@autobind()
export class StorageClassStore extends KubeObjectStore<StorageClass> {
api = storageClassApi;
constructor() {
super();
autoBind(this);
}
getPersistentVolumes(storageClass: StorageClass) {
return volumesStore.getByStorageClass(storageClass);
}
@@ -19,17 +19,23 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { action, observable } from "mobx";
import { action, observable, makeObservable } from "mobx";
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { IPvcMetrics, PersistentVolumeClaim, pvcApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
@autobind()
export class VolumeClaimStore extends KubeObjectStore<PersistentVolumeClaim> {
api = pvcApi;
@observable metrics: IPvcMetrics = null;
constructor() {
super();
makeObservable(this);
autoBind(this);
}
@action
async loadMetrics(pvc: PersistentVolumeClaim) {
this.metrics = await pvcApi.getMetrics(pvc.getName(), pvc.getNs());
@@ -24,7 +24,7 @@ import "./volume-details-list.scss";
import React from "react";
import { observer } from "mobx-react";
import type { PersistentVolume } from "../../api/endpoints/persistent-volume.api";
import { autobind } from "../../../common/utils/autobind";
import { boundMethod } from "../../../common/utils/autobind";
import { TableRow } from "../table/table-row";
import { cssNames, prevDefault } from "../../utils";
import { showDetails } from "../kube-object/kube-object-details";
@@ -54,7 +54,7 @@ export class VolumeDetailsList extends React.Component<Props> {
[sortBy.status]: (volume: PersistentVolume) => volume.getStatus(),
};
@autobind()
@boundMethod
getTableRow(uid: string) {
const { persistentVolumes } = this.props;
const volume = persistentVolumes.find(volume => volume.getId() === uid);
@@ -20,15 +20,19 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { PersistentVolume, persistentVolumeApi } from "../../api/endpoints/persistent-volume.api";
import { apiManager } from "../../api/api-manager";
import type { StorageClass } from "../../api/endpoints/storage-class.api";
@autobind()
export class PersistentVolumesStore extends KubeObjectStore<PersistentVolume> {
api = persistentVolumeApi;
constructor() {
super();
autoBind(this);
}
getByStorageClass(storageClass: StorageClass): PersistentVolume[] {
return this.items.filter(volume =>
volume.getStorageClassName() === storageClass.getName()
+1 -3
View File
@@ -27,20 +27,18 @@ import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
import { PersistentVolumes, volumesRoute, volumesURL } from "../+storage-volumes";
import { StorageClasses, storageClassesRoute, storageClassesURL } from "../+storage-classes";
import { PersistentVolumeClaims, volumeClaimsRoute, volumeClaimsURL } from "../+storage-volume-claims";
import { namespaceUrlParam } from "../+namespaces/namespace.store";
import { isAllowedResource } from "../../../common/rbac";
@observer
export class Storage extends React.Component {
static get tabRoutes() {
const tabRoutes: TabLayoutRoute[] = [];
const query = namespaceUrlParam.toObjectParam();
if (isAllowedResource("persistentvolumeclaims")) {
tabRoutes.push({
title: "Persistent Volume Claims",
component: PersistentVolumeClaims,
url: volumeClaimsURL({ query }),
url: volumeClaimsURL(),
routePath: volumeClaimsRoute.path.toString(),
});
}
@@ -22,13 +22,13 @@
import "./add-role-binding-dialog.scss";
import React from "react";
import { computed, observable } from "mobx";
import { computed, observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
import { Select, SelectOption } from "../select";
import { SubTitle } from "../layout/sub-title";
import { IRoleBindingSubject, Role, RoleBinding, ServiceAccount } from "../../api/endpoints";
import type { IRoleBindingSubject, Role, RoleBinding, ServiceAccount } from "../../api/endpoints";
import { Icon } from "../icon";
import { Input } from "../input";
import { NamespaceSelect } from "../+namespaces/namespace-select";
@@ -51,22 +51,29 @@ interface BindingSelectOption extends SelectOption {
interface Props extends Partial<DialogProps> {
}
const dialogState = observable.object({
isOpen: false,
data: null as RoleBinding,
});
@observer
export class AddRoleBindingDialog extends React.Component<Props> {
@observable static isOpen = false;
@observable static data: RoleBinding = null;
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open(roleBinding?: RoleBinding) {
AddRoleBindingDialog.isOpen = true;
AddRoleBindingDialog.data = roleBinding;
dialogState.isOpen = true;
dialogState.data = roleBinding;
}
static close() {
AddRoleBindingDialog.isOpen = false;
dialogState.isOpen = false;
}
get roleBinding(): RoleBinding {
return AddRoleBindingDialog.data;
return dialogState.data;
}
@observable isLoading = false;
@@ -292,7 +299,7 @@ export class AddRoleBindingDialog extends React.Component<Props> {
<Dialog
{...dialogProps}
className="AddRoleBindingDialog"
isOpen={AddRoleBindingDialog.isOpen}
isOpen={dialogState.isOpen}
onOpen={this.onOpen}
close={this.close}
>
@@ -24,13 +24,13 @@ import "./role-binding-details.scss";
import React from "react";
import { AddRemoveButtons } from "../add-remove-buttons";
import type { IRoleBindingSubject, RoleBinding } from "../../api/endpoints";
import { autobind, prevDefault } from "../../utils";
import { boundMethod, prevDefault } from "../../utils";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { ConfirmDialog } from "../confirm-dialog";
import { DrawerTitle } from "../drawer";
import { KubeEventDetails } from "../+events/kube-event-details";
import { disposeOnUnmount, observer } from "mobx-react";
import { observable, reaction } from "mobx";
import { observable, reaction, makeObservable } from "mobx";
import { roleBindingsStore } from "./role-bindings.store";
import { AddRoleBindingDialog } from "./add-role-binding-dialog";
import type { KubeObjectDetailsProps } from "../kube-object";
@@ -44,6 +44,11 @@ interface Props extends KubeObjectDetailsProps<RoleBinding> {
export class RoleBindingDetails extends React.Component<Props> {
@observable selectedSubjects = observable.array<IRoleBindingSubject>([], { deep: false });
constructor(props: Props) {
super(props);
makeObservable(this);
}
async componentDidMount() {
disposeOnUnmount(this, [
reaction(() => this.props.object, () => {
@@ -63,7 +68,7 @@ export class RoleBindingDetails extends React.Component<Props> {
);
}
@autobind()
@boundMethod
removeSelectedSubjects() {
const { object: roleBinding } = this.props;
const { selectedSubjects } = this;
@@ -23,13 +23,17 @@ import difference from "lodash/difference";
import uniqBy from "lodash/uniqBy";
import { clusterRoleBindingApi, IRoleBindingSubject, RoleBinding, roleBindingApi } from "../../api/endpoints";
import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { apiManager } from "../../api/api-manager";
@autobind()
export class RoleBindingsStore extends KubeObjectStore<RoleBinding> {
api = clusterRoleBindingApi;
constructor() {
super();
autoBind(this);
}
getSubscribeApis() {
return [clusterRoleBindingApi, roleBindingApi];
}
@@ -22,7 +22,7 @@
import "./add-role-dialog.scss";
import React from "react";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
@@ -36,19 +36,26 @@ import { showDetails } from "../kube-object";
interface Props extends Partial<DialogProps> {
}
const dialogState = observable.object({
isOpen: false,
});
@observer
export class AddRoleDialog extends React.Component<Props> {
@observable static isOpen = false;
@observable roleName = "";
@observable namespace = "";
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open() {
AddRoleDialog.isOpen = true;
dialogState.isOpen = true;
}
static close() {
AddRoleDialog.isOpen = false;
dialogState.isOpen = false;
}
close = () => {
@@ -80,7 +87,7 @@ export class AddRoleDialog extends React.Component<Props> {
<Dialog
{...dialogProps}
className="AddRoleDialog"
isOpen={AddRoleDialog.isOpen}
isOpen={dialogState.isOpen}
close={this.close}
>
<Wizard header={header} done={this.close}>
@@ -20,14 +20,18 @@
*/
import { clusterRoleApi, Role, roleApi } from "../../api/endpoints";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store";
import { apiManager } from "../../api/api-manager";
@autobind()
export class RolesStore extends KubeObjectStore<Role> {
api = clusterRoleApi;
constructor() {
super();
autoBind(this);
}
getSubscribeApis() {
return [roleApi, clusterRoleApi];
}
@@ -22,7 +22,7 @@
import "./create-service-account-dialog.scss";
import React from "react";
import { observable } from "mobx";
import { makeObservable, observable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
@@ -37,19 +37,26 @@ import { showDetails } from "../kube-object";
interface Props extends Partial<DialogProps> {
}
const dialogState = observable.object({
isOpen: false,
});
@observer
export class CreateServiceAccountDialog extends React.Component<Props> {
@observable static isOpen = false;
@observable name = "";
@observable namespace = "default";
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open() {
CreateServiceAccountDialog.isOpen = true;
dialogState.isOpen = true;
}
static close() {
CreateServiceAccountDialog.isOpen = false;
dialogState.isOpen = false;
}
close = () => {
@@ -79,7 +86,7 @@ export class CreateServiceAccountDialog extends React.Component<Props> {
<Dialog
{...dialogProps}
className="CreateServiceAccountDialog"
isOpen={CreateServiceAccountDialog.isOpen}
isOpen={dialogState.isOpen}
close={this.close}
>
<Wizard header={header} done={this.close}>
@@ -22,7 +22,7 @@
import "./service-accounts-details.scss";
import React from "react";
import { autorun, observable } from "mobx";
import { autorun, observable, makeObservable } from "mobx";
import { Spinner } from "../spinner";
import { ServiceAccountsSecret } from "./service-accounts-secret";
import { DrawerItem, DrawerTitle } from "../drawer";
@@ -66,6 +66,11 @@ export class ServiceAccountsDetails extends React.Component<Props> {
this.imagePullSecrets = await Promise.all(imagePullSecrets);
});
constructor(props: Props) {
super(props);
makeObservable(this);
}
renderSecrets() {
const { secrets } = this;
@@ -19,15 +19,19 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { ServiceAccount, serviceAccountsApi } from "../../api/endpoints";
import { KubeObjectStore } from "../../kube-object.store";
import { apiManager } from "../../api/api-manager";
@autobind()
export class ServiceAccountsStore extends KubeObjectStore<ServiceAccount> {
api = serviceAccountsApi;
constructor() {
super();
autoBind(this);
}
protected async createItem(params: { name: string; namespace?: string }) {
await super.createItem(params);
@@ -27,21 +27,19 @@ import { Roles } from "../+user-management-roles";
import { RoleBindings } from "../+user-management-roles-bindings";
import { ServiceAccounts } from "../+user-management-service-accounts";
import { podSecurityPoliciesRoute, podSecurityPoliciesURL, roleBindingsRoute, roleBindingsURL, rolesRoute, rolesURL, serviceAccountsRoute, serviceAccountsURL } from "./user-management.route";
import { namespaceUrlParam } from "../+namespaces/namespace.store";
import { PodSecurityPolicies } from "../+pod-security-policies";
import { isAllowedResource } from "../../../common/rbac";
@observer
export class UserManagement extends React.Component {
static get tabRoutes() {
const query = namespaceUrlParam.toObjectParam();
const tabRoutes: TabLayoutRoute[] = [];
if (isAllowedResource("serviceaccounts")) {
tabRoutes.push({
title: "Service Accounts",
component: ServiceAccounts,
url: serviceAccountsURL({ query }),
url: serviceAccountsURL(),
routePath: serviceAccountsRoute.path.toString(),
});
}
@@ -51,7 +49,7 @@ export class UserManagement extends React.Component {
tabRoutes.push({
title: "Role Bindings",
component: RoleBindings,
url: roleBindingsURL({ query }),
url: roleBindingsURL(),
routePath: roleBindingsRoute.path.toString(),
});
}
@@ -61,7 +59,7 @@ export class UserManagement extends React.Component {
tabRoutes.push({
title: "Roles",
component: Roles,
url: rolesURL({ query }),
url: rolesURL(),
routePath: rolesRoute.path.toString(),
});
}
@@ -22,7 +22,7 @@
import "./cronjob-trigger-dialog.scss";
import React, { Component } from "react";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
@@ -35,26 +35,32 @@ import { systemName, maxLength } from "../input/input_validators";
interface Props extends Partial<DialogProps> {
}
const dialogState = observable.object({
isOpen: false,
data: null as CronJob,
});
@observer
export class CronJobTriggerDialog extends Component<Props> {
@observable static isOpen = false;
@observable static data: CronJob = null;
@observable jobName = "";
@observable ready = false;
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open(cronjob: CronJob) {
CronJobTriggerDialog.isOpen = true;
CronJobTriggerDialog.data = cronjob;
dialogState.isOpen = true;
dialogState.data = cronjob;
}
static close() {
CronJobTriggerDialog.isOpen = false;
dialogState.isOpen = false;
}
get cronjob() {
return CronJobTriggerDialog.data;
return dialogState.data;
}
close = () => {
@@ -128,7 +134,7 @@ export class CronJobTriggerDialog extends Component<Props> {
return (
<Dialog
{...dialogProps}
isOpen={CronJobTriggerDialog.isOpen}
isOpen={dialogState.isOpen}
className={cssNames("CronJobTriggerDialog", className)}
onOpen={this.onOpen}
onClose={this.onClose}
@@ -20,15 +20,19 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { CronJob, cronJobApi } from "../../api/endpoints/cron-job.api";
import { jobStore } from "../+workloads-jobs/job.store";
import { apiManager } from "../../api/api-manager";
@autobind()
export class CronJobStore extends KubeObjectStore<CronJob> {
api = cronJobApi;
constructor() {
super();
autoBind(this);
}
getStatuses(cronJobs?: CronJob[]) {
const status = { suspended: 0, scheduled: 0 };
@@ -19,19 +19,25 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { DaemonSet, daemonSetApi, IPodMetrics, Pod, podsApi, PodStatus } from "../../api/endpoints";
import { podsStore } from "../+workloads-pods/pods.store";
import { apiManager } from "../../api/api-manager";
@autobind()
export class DaemonSetStore extends KubeObjectStore<DaemonSet> {
api = daemonSetApi;
@observable metrics: IPodMetrics = null;
constructor() {
super();
makeObservable(this);
autoBind(this);
}
async loadMetrics(daemonSet: DaemonSet) {
const pods = this.getChildPods(daemonSet);
@@ -22,7 +22,7 @@
import "./deployment-scale-dialog.scss";
import React, { Component } from "react";
import { computed, observable } from "mobx";
import { computed, observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
@@ -35,26 +35,33 @@ import { cssNames } from "../../utils";
interface Props extends Partial<DialogProps> {
}
const dialogState = observable.object({
isOpen: false,
data: null as Deployment,
});
@observer
export class DeploymentScaleDialog extends Component<Props> {
@observable static isOpen = false;
@observable static data: Deployment = null;
@observable ready = false;
@observable currentReplicas = 0;
@observable desiredReplicas = 0;
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open(deployment: Deployment) {
DeploymentScaleDialog.isOpen = true;
DeploymentScaleDialog.data = deployment;
dialogState.isOpen = true;
dialogState.data = deployment;
}
static close() {
DeploymentScaleDialog.isOpen = false;
dialogState.isOpen = false;
}
get deployment() {
return DeploymentScaleDialog.data;
return dialogState.data;
}
close = () => {
@@ -165,7 +172,7 @@ export class DeploymentScaleDialog extends Component<Props> {
return (
<Dialog
{...dialogProps}
isOpen={DeploymentScaleDialog.isOpen}
isOpen={dialogState.isOpen}
className={cssNames("DeploymentScaleDialog", className)}
onOpen={this.onOpen}
onClose={this.onClose}
@@ -19,18 +19,24 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { Deployment, deploymentApi, IPodMetrics, podsApi, PodStatus } from "../../api/endpoints";
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { podsStore } from "../+workloads-pods/pods.store";
import { apiManager } from "../../api/api-manager";
@autobind()
export class DeploymentStore extends KubeObjectStore<Deployment> {
api = deploymentApi;
@observable metrics: IPodMetrics = null;
constructor() {
super();
makeObservable(this);
autoBind(this);
}
protected sortItems(items: Deployment[]) {
return super.sortItems(items, [
item => item.getReplicas(),
@@ -20,16 +20,20 @@
*/
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { Job, jobApi } from "../../api/endpoints/job.api";
import { CronJob, Pod, PodStatus } from "../../api/endpoints";
import { podsStore } from "../+workloads-pods/pods.store";
import { apiManager } from "../../api/api-manager";
@autobind()
export class JobStore extends KubeObjectStore<Job> {
api = jobApi;
constructor() {
super();
autoBind(this);
}
getChildPods(job: Job): Pod[] {
return podsStore.getPodsByOwnerId(job.getId());
}
@@ -30,7 +30,7 @@ import { namespaceStore } from "../+namespaces/namespace.store";
import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter";
import { isAllowedResource, KubeResource } from "../../../common/rbac";
import { ResourceNames } from "../../utils/rbac";
import { autobind } from "../../utils";
import { boundMethod } from "../../utils";
const resources: KubeResource[] = [
"pods",
@@ -44,7 +44,7 @@ const resources: KubeResource[] = [
@observer
export class OverviewStatuses extends React.Component {
@autobind()
@boundMethod
renderWorkload(resource: KubeResource): React.ReactElement {
const store = workloadStores[resource];
const items = store.getAllByNs(namespaceStore.contextNamespaces);
@@ -24,7 +24,7 @@ import "./overview-workload-status.scss";
import React from "react";
import capitalize from "lodash/capitalize";
import { findDOMNode } from "react-dom";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { PieChart } from "../chart";
import { cssVar } from "../../utils";
@@ -45,6 +45,11 @@ interface Props {
export class OverviewWorkloadStatus extends React.Component<Props> {
@observable elem: HTMLElement;
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentDidMount() {
// eslint-disable-next-line react/no-find-dom-node
this.elem = findDOMNode(this) as HTMLElement;
@@ -25,7 +25,7 @@ import React from "react";
import { observer } from "mobx-react";
import type { Pod } from "../../api/endpoints";
import { apiBase } from "../../api";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { cssNames } from "../../utils";
import { Notifications } from "../notifications";
import { Spinner } from "../spinner";
@@ -43,6 +43,11 @@ interface Props {
export class PodContainerPort extends React.Component<Props> {
@observable waiting = false;
constructor(props: Props) {
super(props);
makeObservable(this);
}
async portForward() {
const { pod, port } = this.props;
@@ -27,7 +27,7 @@ import { reaction } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { podsStore } from "./pods.store";
import type { Pod } from "../../api/endpoints";
import { autobind, bytesToUnits, cssNames, interval, prevDefault } from "../../utils";
import { boundMethod, bytesToUnits, cssNames, interval, prevDefault } from "../../utils";
import { LineProgress } from "../line-progress";
import type { KubeObject } from "../../api/kube-object";
import { Table, TableCell, TableHead, TableRow } from "../table";
@@ -119,7 +119,7 @@ export class PodDetailsList extends React.Component<Props> {
);
}
@autobind()
@boundMethod
getTableRow(uid: string) {
const { pods } = this.props;
const pod = pods.find(pod => pod.getId() == uid);
@@ -23,7 +23,7 @@ import "./pod-details-secrets.scss";
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { autorun, observable } from "mobx";
import { autorun, observable, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { Pod, Secret, secretsApi } from "../../api/endpoints";
import { getDetailsUrl } from "../kube-object";
@@ -50,6 +50,11 @@ export class PodDetailsSecrets extends Component<Props> {
secrets.forEach(secret => secret && this.secrets.set(secret.getName(), secret));
});
constructor(props: Props) {
super(props);
makeObservable(this);
}
render() {
const { pod } = this.props;
@@ -25,11 +25,11 @@ import React from "react";
import kebabCase from "lodash/kebabCase";
import { disposeOnUnmount, observer } from "mobx-react";
import { Link } from "react-router-dom";
import { autorun, observable, reaction, toJS } from "mobx";
import { autorun, observable, reaction, makeObservable } from "mobx";
import { IPodMetrics, nodesApi, Pod, pvcApi, configMapApi } from "../../api/endpoints";
import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge";
import { autobind, cssNames, interval } from "../../utils";
import { boundMethod, cssNames, interval, toJS } from "../../utils";
import { PodDetailsContainer } from "./pod-details-container";
import { PodDetailsAffinities } from "./pod-details-affinities";
import { PodDetailsTolerations } from "./pod-details-tolerations";
@@ -55,6 +55,11 @@ export class PodDetails extends React.Component<Props> {
private watcher = interval(60, () => this.loadMetrics());
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentDidMount() {
disposeOnUnmount(this, [
autorun(() => {
@@ -72,7 +77,7 @@ export class PodDetails extends React.Component<Props> {
podsStore.reset();
}
@autobind()
@boundMethod
async loadMetrics() {
const { object: pod } = this.props;
@@ -20,20 +20,26 @@
*/
import countBy from "lodash/countBy";
import { action, observable } from "mobx";
import { action, observable, makeObservable } from "mobx";
import { KubeObjectStore } from "../../kube-object.store";
import { autobind, cpuUnitsToNumber, unitsToBytes } from "../../utils";
import { autoBind, cpuUnitsToNumber, unitsToBytes } from "../../utils";
import { IPodMetrics, Pod, PodMetrics, podMetricsApi, podsApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
import type { WorkloadKubeObject } from "../../api/workload-kube-object";
@autobind()
export class PodsStore extends KubeObjectStore<Pod> {
api = podsApi;
@observable metrics: IPodMetrics = null;
@observable kubeMetrics = observable.array<PodMetrics>([]);
constructor() {
super();
makeObservable(this);
autoBind(this);
}
@action
async loadMetrics(pod: Pod) {
this.metrics = await podsApi.getMetrics([pod], pod.getNs());
@@ -22,7 +22,7 @@
import "./replicaset-scale-dialog.scss";
import React, { Component } from "react";
import { computed, observable } from "mobx";
import { computed, observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
@@ -35,26 +35,33 @@ import { ReplicaSet, replicaSetApi } from "../../api/endpoints/replica-set.api";
interface Props extends Partial<DialogProps> {
}
const dialogState = observable.object({
isOpen: false,
data: null as ReplicaSet,
});
@observer
export class ReplicaSetScaleDialog extends Component<Props> {
@observable static isOpen = false;
@observable static data: ReplicaSet = null;
@observable ready = false;
@observable currentReplicas = 0;
@observable desiredReplicas = 0;
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open(replicaSet: ReplicaSet) {
ReplicaSetScaleDialog.isOpen = true;
ReplicaSetScaleDialog.data = replicaSet;
dialogState.isOpen = true;
dialogState.data = replicaSet;
}
static close() {
ReplicaSetScaleDialog.isOpen = false;
dialogState.isOpen = false;
}
get replicaSet() {
return ReplicaSetScaleDialog.data;
return dialogState.data;
}
close = () => {
@@ -167,7 +174,7 @@ export class ReplicaSetScaleDialog extends Component<Props> {
return (
<Dialog
{...dialogProps}
isOpen={ReplicaSetScaleDialog.isOpen}
isOpen={dialogState.isOpen}
className={cssNames("ReplicaSetScaleDialog", className)}
onOpen={this.onOpen}
onClose={this.onClose}
@@ -19,19 +19,25 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { observable } from "mobx";
import { autobind } from "../../utils";
import { observable, makeObservable } from "mobx";
import { autoBind } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store";
import { Deployment, IPodMetrics, podsApi, ReplicaSet, replicaSetApi } from "../../api/endpoints";
import { podsStore } from "../+workloads-pods/pods.store";
import { apiManager } from "../../api/api-manager";
import { PodStatus } from "../../api/endpoints/pods.api";
@autobind()
export class ReplicaSetStore extends KubeObjectStore<ReplicaSet> {
api = replicaSetApi;
@observable metrics: IPodMetrics = null;
constructor() {
super();
makeObservable(this);
autoBind(this);
}
async loadMetrics(replicaSet: ReplicaSet) {
const pods = this.getChildPods(replicaSet);
@@ -23,7 +23,7 @@ import "./statefulset-scale-dialog.scss";
import { StatefulSet, statefulSetApi } from "../../api/endpoints";
import React, { Component } from "react";
import { computed, observable } from "mobx";
import { computed, makeObservable, observable } from "mobx";
import { observer } from "mobx-react";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
@@ -35,26 +35,33 @@ import { cssNames } from "../../utils";
interface Props extends Partial<DialogProps> {
}
const dialogState = observable.object({
isOpen: false,
data: null as StatefulSet,
});
@observer
export class StatefulSetScaleDialog extends Component<Props> {
@observable static isOpen = false;
@observable static data: StatefulSet = null;
@observable ready = false;
@observable currentReplicas = 0;
@observable desiredReplicas = 0;
constructor(props: Props) {
super(props);
makeObservable(this);
}
static open(statefulSet: StatefulSet) {
StatefulSetScaleDialog.isOpen = true;
StatefulSetScaleDialog.data = statefulSet;
dialogState.isOpen = true;
dialogState.data = statefulSet;
}
static close() {
StatefulSetScaleDialog.isOpen = false;
dialogState.isOpen = false;
}
get statefulSet() {
return StatefulSetScaleDialog.data;
return dialogState.data;
}
close = () => {
@@ -167,7 +174,7 @@ export class StatefulSetScaleDialog extends Component<Props> {
return (
<Dialog
{...dialogProps}
isOpen={StatefulSetScaleDialog.isOpen}
isOpen={dialogState.isOpen}
className={cssNames("StatefulSetScaleDialog", className)}
onOpen={this.onOpen}
onClose={this.onClose}
@@ -19,18 +19,25 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { observable } from "mobx";
import { autobind } from "../../utils";
import { observable, makeObservable } from "mobx";
import { autoBind } from "../../utils";
import { KubeObjectStore } from "../../kube-object.store";
import { IPodMetrics, podsApi, PodStatus, StatefulSet, statefulSetApi } from "../../api/endpoints";
import { podsStore } from "../+workloads-pods/pods.store";
import { apiManager } from "../../api/api-manager";
@autobind()
export class StatefulSetStore extends KubeObjectStore<StatefulSet> {
api = statefulSetApi;
@observable metrics: IPodMetrics = null;
constructor() {
super();
makeObservable(this);
autoBind(this);
}
async loadMetrics(statefulSet: StatefulSet) {
const pods = this.getChildPods(statefulSet);
@@ -26,7 +26,6 @@ import { observer } from "mobx-react";
import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
import { WorkloadsOverview } from "../+workloads-overview/overview";
import { cronJobsRoute, cronJobsURL, daemonSetsRoute, daemonSetsURL, deploymentsRoute, deploymentsURL, jobsRoute, jobsURL, overviewRoute, overviewURL, podsRoute, podsURL, replicaSetsRoute, replicaSetsURL, statefulSetsRoute, statefulSetsURL } from "./workloads.route";
import { namespaceUrlParam } from "../+namespaces/namespace.store";
import { Pods } from "../+workloads-pods";
import { Deployments } from "../+workloads-deployments";
import { DaemonSets } from "../+workloads-daemonsets";
@@ -39,12 +38,11 @@ import { ReplicaSets } from "../+workloads-replicasets";
@observer
export class Workloads extends React.Component {
static get tabRoutes(): TabLayoutRoute[] {
const query = namespaceUrlParam.toObjectParam();
const routes: TabLayoutRoute[] = [
{
title: "Overview",
component: WorkloadsOverview,
url: overviewURL({ query }),
url: overviewURL(),
routePath: overviewRoute.path.toString()
}
];
@@ -53,7 +51,7 @@ export class Workloads extends React.Component {
routes.push({
title: "Pods",
component: Pods,
url: podsURL({ query }),
url: podsURL(),
routePath: podsRoute.path.toString()
});
}
@@ -62,7 +60,7 @@ export class Workloads extends React.Component {
routes.push({
title: "Deployments",
component: Deployments,
url: deploymentsURL({ query }),
url: deploymentsURL(),
routePath: deploymentsRoute.path.toString(),
});
}
@@ -71,7 +69,7 @@ export class Workloads extends React.Component {
routes.push({
title: "DaemonSets",
component: DaemonSets,
url: daemonSetsURL({ query }),
url: daemonSetsURL(),
routePath: daemonSetsRoute.path.toString(),
});
}
@@ -80,7 +78,7 @@ export class Workloads extends React.Component {
routes.push({
title: "StatefulSets",
component: StatefulSets,
url: statefulSetsURL({ query }),
url: statefulSetsURL(),
routePath: statefulSetsRoute.path.toString(),
});
}
@@ -89,7 +87,7 @@ export class Workloads extends React.Component {
routes.push({
title: "ReplicaSets",
component: ReplicaSets,
url: replicaSetsURL({ query }),
url: replicaSetsURL(),
routePath: replicaSetsRoute.path.toString(),
});
}
@@ -98,7 +96,7 @@ export class Workloads extends React.Component {
routes.push({
title: "Jobs",
component: Jobs,
url: jobsURL({ query }),
url: jobsURL(),
routePath: jobsRoute.path.toString(),
});
}
@@ -107,7 +105,7 @@ export class Workloads extends React.Component {
routes.push({
title: "CronJobs",
component: CronJobs,
url: cronJobsURL({ query }),
url: cronJobsURL(),
routePath: cronJobsRoute.path.toString(),
});
}
@@ -26,7 +26,7 @@ import "./ace-editor.scss";
import React from "react";
import { observer } from "mobx-react";
import AceBuild, { Ace } from "ace-builds";
import { autobind, cssNames, noop } from "../../utils";
import { boundMethod, cssNames, noop } from "../../utils";
interface Props extends Partial<Ace.EditorOptions> {
className?: string;
@@ -66,6 +66,7 @@ export class AceEditor extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
require("ace-builds/src-noconflict/mode-yaml");
require("ace-builds/src-noconflict/mode-json");
require("ace-builds/src-noconflict/theme-terminal");
require("ace-builds/src-noconflict/ext-searchbox");
}
@@ -149,7 +150,7 @@ export class AceEditor extends React.Component<Props, State> {
});
}
@autobind()
@boundMethod
onCursorPosChange() {
const { onCursorPosChange } = this.props;
@@ -158,7 +159,7 @@ export class AceEditor extends React.Component<Props, State> {
}
}
@autobind()
@boundMethod
onChange(delta: Ace.Delta) {
const { onChange } = this.props;
+8 -3
View File
@@ -21,9 +21,9 @@
import "./animate.scss";
import React from "react";
import { observable, reaction } from "mobx";
import { observable, reaction, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { autobind, cssNames, noop } from "../../utils";
import { boundMethod, cssNames, noop } from "../../utils";
export type AnimateName = "opacity" | "slide-right" | "opacity-scale" | string;
@@ -51,6 +51,11 @@ export class Animate extends React.Component<AnimateProps> {
leave: false
};
constructor(props: AnimateProps) {
super(props);
makeObservable(this);
}
get contentElem() {
return React.Children.only(this.props.children) as React.ReactElement<React.HTMLAttributes<any>>;
}
@@ -87,7 +92,7 @@ export class Animate extends React.Component<AnimateProps> {
this.statusClassName.leave = false;
}
@autobind()
@boundMethod
onTransitionEnd(evt: React.TransitionEvent) {
const { enter, leave } = this.statusClassName;
const { onTransitionEnd } = this.contentElem.props;
+7 -2
View File
@@ -19,7 +19,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import React from "react";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { Redirect, Route, Router, Switch } from "react-router";
import { history } from "../navigation";
@@ -74,6 +74,11 @@ import { namespaceStore } from "./+namespaces/namespace.store";
@observer
export class App extends React.Component {
constructor(props: {}) {
super(props);
makeObservable(this);
}
static async init() {
const frameId = webFrame.routingId;
const clusterId = getHostedClusterId();
@@ -99,7 +104,7 @@ export class App extends React.Component {
whatInput.ask(); // Start to monitor user input device
// Setup hosted cluster context
KubeObjectStore.defaultContext = clusterContext;
KubeObjectStore.defaultContext.set(clusterContext);
kubeWatchApi.context = clusterContext;
}
@@ -21,7 +21,7 @@
import "./checkbox.scss";
import React from "react";
import { autobind, cssNames } from "../../utils";
import { boundMethod, cssNames } from "../../utils";
export interface CheckboxProps<T = boolean> {
theme?: "dark" | "light";
@@ -36,7 +36,7 @@ export interface CheckboxProps<T = boolean> {
export class Checkbox extends React.PureComponent<CheckboxProps> {
private input: HTMLInputElement;
@autobind()
@boundMethod
onChange(evt: React.ChangeEvent<HTMLInputElement>) {
if (this.props.onChange) {
this.props.onChange(this.input.checked, evt);
@@ -22,7 +22,7 @@
import "./clipboard.scss";
import React from "react";
import { findDOMNode } from "react-dom";
import { autobind } from "../../../common/utils";
import { boundMethod } from "../../../common/utils";
import { Notifications } from "../notifications";
import { copyToClipboard } from "../../utils/copyToClipboard";
import logger from "../../../main/logger";
@@ -54,7 +54,7 @@ export class Clipboard extends React.Component<CopyToClipboardProps> {
return React.Children.only(this.props.children) as React.ReactElement;
}
@autobind()
@boundMethod
onClick(evt: React.MouseEvent) {
if (this.rootReactElem.props.onClick) {
this.rootReactElem.props.onClick(evt); // pass event to children-root-element if any
@@ -25,7 +25,7 @@ import "./cluster-status.scss";
import React from "react";
import { observer } from "mobx-react";
import { ipcRenderer } from "electron";
import { computed, observable } from "mobx";
import { computed, observable, makeObservable } from "mobx";
import { requestMain, subscribeToBroadcast } from "../../../common/ipc";
import { Icon } from "../icon";
import { Button } from "../button";
@@ -45,6 +45,11 @@ export class ClusterStatus extends React.Component<Props> {
@observable authOutput: KubeAuthProxyLog[] = [];
@observable isReconnecting = false;
constructor(props: Props) {
super(props);
makeObservable(this);
}
get cluster(): Cluster {
return ClusterStore.getInstance().getById(this.props.clusterId);
}
@@ -21,83 +21,80 @@
import "./cluster-view.scss";
import React from "react";
import { reaction } from "mobx";
import { computed, makeObservable, reaction } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import type { RouteComponentProps } from "react-router";
import type { IClusterViewRouteParams } from "./cluster-view.route";
import { ClusterStatus } from "./cluster-status";
import { hasLoadedView, initView, lensViews, refreshViews } from "./lens-views";
import { hasLoadedView, initView, refreshViews } from "./lens-views";
import type { Cluster } from "../../../main/cluster";
import { ClusterStore } from "../../../common/cluster-store";
import { requestMain } from "../../../common/ipc";
import { clusterActivateHandler } from "../../../common/cluster-ipc";
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
import { catalogURL } from "../+catalog";
import { navigate } from "../../navigation";
interface Props extends RouteComponentProps<IClusterViewRouteParams> {
}
import { getMatchedClusterId, navigate } from "../../navigation";
import { catalogURL } from "../+catalog/catalog.route";
@observer
export class ClusterView extends React.Component<Props> {
get clusterId() {
return this.props.match.params.clusterId;
export class ClusterView extends React.Component {
constructor(props: {}) {
super(props);
makeObservable(this);
}
get cluster(): Cluster {
get clusterId() {
return getMatchedClusterId();
}
@computed get cluster(): Cluster | undefined {
return ClusterStore.getInstance().getById(this.clusterId);
}
async componentDidMount() {
disposeOnUnmount(this, [
reaction(() => this.clusterId, (clusterId) => {
this.showCluster(clusterId);
}, { fireImmediately: true}
),
reaction(() => this.cluster?.ready, (ready) => {
const clusterView = lensViews.get(this.clusterId);
@computed get isReady(): boolean {
const { cluster, clusterId } = this;
if (clusterView && clusterView.isLoaded && !ready) {
navigate(catalogURL());
return cluster?.ready && cluster?.available && hasLoadedView(clusterId);
}
componentDidMount() {
this.bindEvents();
}
bindEvents() {
disposeOnUnmount(this, [
reaction(() => this.clusterId, async (clusterId) => {
refreshViews(clusterId); // refresh visibility of active cluster
initView(clusterId); // init cluster-view (iframe), requires parent container #lens-views to be in DOM
requestMain(clusterActivateHandler, clusterId, false); // activate and fetch cluster's state from main
catalogEntityRegistry.activeEntity = catalogEntityRegistry.getById(clusterId);
}, {
fireImmediately: true,
}),
reaction(() => this.isReady, (ready) => {
if (ready) {
refreshViews(this.clusterId); // show cluster's view (iframe)
} else if (hasLoadedView(this.clusterId)) {
navigate(catalogURL()); // redirect to catalog when active cluster get disconnected/not available
}
})
}, {
fireImmediately: true,
}),
]);
}
componentWillUnmount() {
this.hideCluster();
}
renderStatus(): React.ReactNode {
const { clusterId, cluster, isReady } = this;
showCluster(clusterId: string) {
initView(clusterId);
requestMain(clusterActivateHandler, this.clusterId, false);
const entity = catalogEntityRegistry.getById(this.clusterId);
if (entity) {
catalogEntityRegistry.activeEntity = entity;
if (cluster && !isReady) {
return <ClusterStatus clusterId={clusterId} className="box center"/>;
}
}
hideCluster() {
refreshViews();
if (catalogEntityRegistry.activeEntity?.metadata?.uid === this.clusterId) {
catalogEntityRegistry.activeEntity = null;
}
return null;
}
render() {
const { cluster } = this;
const showStatus = cluster && (!cluster.available || !hasLoadedView(cluster.id) || !cluster.ready);
refreshViews(cluster.id);
return (
<div className="ClusterView flex align-center">
{showStatus && (
<ClusterStatus key={cluster.id} clusterId={cluster.id} className="box center"/>
)}
{this.renderStatus()}
</div>
);
}
@@ -36,15 +36,9 @@ export function hasLoadedView(clusterId: ClusterId): boolean {
}
export async function initView(clusterId: ClusterId) {
refreshViews(clusterId);
if (!clusterId || lensViews.has(clusterId)) {
return;
}
const cluster = ClusterStore.getInstance().getById(clusterId);
if (!cluster) {
if (!cluster || lensViews.has(clusterId)) {
return;
}
@@ -60,6 +54,7 @@ export async function initView(clusterId: ClusterId) {
}, { once: true });
lensViews.set(clusterId, { clusterId, view: iframe });
parentElem.appendChild(iframe);
logger.info(`[LENS-VIEW]: waiting cluster to be ready, clusterId=${clusterId}`);
await cluster.whenReady;
await autoCleanOnRemove(clusterId, iframe);
@@ -84,7 +79,8 @@ export async function autoCleanOnRemove(clusterId: ClusterId, iframe: HTMLIFrame
}
export function refreshViews(visibleClusterId?: string) {
const cluster = !visibleClusterId ? null : ClusterStore.getInstance().getById(visibleClusterId);
logger.info(`[LENS-VIEW]: refreshing iframe views, visible cluster id=${visibleClusterId}`);
const cluster = ClusterStore.getInstance().getById(visibleClusterId);
lensViews.forEach(({ clusterId, view, isLoaded }) => {
const isCurrent = clusterId === cluster?.id;
@@ -24,7 +24,7 @@ import { observer } from "mobx-react";
import type { Cluster } from "../../../../main/cluster";
import { SubTitle } from "../../layout/sub-title";
import { EditableList } from "../../editable-list";
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
interface Props {
cluster: Cluster;
@@ -34,6 +34,11 @@ interface Props {
export class ClusterAccessibleNamespaces extends React.Component<Props> {
@observable namespaces = new Set(this.props.cluster.accessibleNamespaces);
constructor(props: Props) {
super(props);
makeObservable(this);
}
render() {
return (
<>
@@ -20,7 +20,7 @@
*/
import React from "react";
import { observable, autorun } from "mobx";
import { observable, autorun, makeObservable } from "mobx";
import { observer, disposeOnUnmount } from "mobx-react";
import type { Cluster } from "../../../../main/cluster";
import { Input } from "../../input";
@@ -34,6 +34,11 @@ interface Props {
export class ClusterHomeDirSetting extends React.Component<Props> {
@observable directory = "";
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentDidMount() {
disposeOnUnmount(this,
autorun(() => {
@@ -23,7 +23,7 @@ import React from "react";
import type { Cluster } from "../../../../main/cluster";
import { observer } from "mobx-react";
import { SubTitle } from "../../layout/sub-title";
import { autobind } from "../../../../common/utils";
import { boundMethod } from "../../../../common/utils";
import { shell } from "electron";
interface Props {
@@ -33,7 +33,7 @@ interface Props {
@observer
export class ClusterKubeconfig extends React.Component<Props> {
@autobind()
@boundMethod
openKubeconfig() {
const { cluster } = this.props;
@@ -26,7 +26,7 @@ import { Icon } from "../../icon/icon";
import { Button } from "../../button/button";
import { SubTitle } from "../../layout/sub-title";
import type { Cluster } from "../../../../main/cluster";
import { observable, reaction } from "mobx";
import { observable, reaction, makeObservable } from "mobx";
interface Props {
cluster: Cluster;
@@ -49,6 +49,11 @@ export enum ResourceType {
export class ClusterMetricsSetting extends React.Component<Props> {
@observable hiddenMetrics = observable.set<string>();
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentDidMount() {
this.hiddenMetrics = observable.set<string>(this.props.cluster.preferences.hiddenMetrics ?? []);
@@ -22,7 +22,7 @@
import React from "react";
import type { Cluster } from "../../../../main/cluster";
import { Input } from "../../input";
import { observable, autorun } from "mobx";
import { observable, autorun, makeObservable } from "mobx";
import { observer, disposeOnUnmount } from "mobx-react";
import { SubTitle } from "../../layout/sub-title";
import { isRequired } from "../../input/input_validators";
@@ -35,6 +35,11 @@ interface Props {
export class ClusterNameSetting extends React.Component<Props> {
@observable name = "";
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentDidMount() {
disposeOnUnmount(this,
autorun(() => {
@@ -26,7 +26,7 @@ import type { Cluster } from "../../../../main/cluster";
import { SubTitle } from "../../layout/sub-title";
import { Select, SelectOption } from "../../select";
import { Input } from "../../input";
import { observable, computed, autorun } from "mobx";
import { observable, computed, autorun, makeObservable } from "mobx";
import { productName } from "../../../../common/vars";
const options: SelectOption<string>[] = [
@@ -43,6 +43,11 @@ export class ClusterPrometheusSetting extends React.Component<Props> {
@observable path = "";
@observable provider = "";
constructor(props: Props) {
super(props);
makeObservable(this);
}
@computed get canEditPrometheusPath() {
if (this.provider === "" || this.provider === "lens") return false;
@@ -20,7 +20,7 @@
*/
import React from "react";
import { observable, autorun } from "mobx";
import { observable, autorun, makeObservable } from "mobx";
import { observer, disposeOnUnmount } from "mobx-react";
import type { Cluster } from "../../../../main/cluster";
import { Input, InputValidators } from "../../input";
@@ -34,6 +34,11 @@ interface Props {
export class ClusterProxySetting extends React.Component<Props> {
@observable proxy = "";
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentDidMount() {
disposeOnUnmount(this,
autorun(() => {
@@ -22,7 +22,7 @@
import React from "react";
import { disposeOnUnmount, observer } from "mobx-react";
import type { Cluster } from "../../../../main/cluster";
import { observable, reaction } from "mobx";
import { observable, reaction, makeObservable } from "mobx";
import { Badge } from "../../badge/badge";
import { Icon } from "../../icon/icon";
@@ -34,6 +34,11 @@ interface Props {
export class ShowMetricsSetting extends React.Component<Props> {
@observable hiddenMetrics = observable.set<string>();
constructor(props: Props) {
super(props);
makeObservable(this);
}
componentDidMount() {
this.hiddenMetrics = observable.set<string>(this.props.cluster.preferences.hiddenMetrics ?? []);
Loaded 100 of 256 files, more files were not shown because too many files have changed in this diff. Show more