diff --git a/src/common/cluster-store.ts b/src/common/cluster-store.ts index bef96f9b5d..e4987d85a1 100644 --- a/src/common/cluster-store.ts +++ b/src/common/cluster-store.ts @@ -156,11 +156,6 @@ export class ClusterStore extends BaseStore { this.activeClusterId = newClusters.has(activeCluster) ? activeCluster : null; this.clusters.replace(newClusters); this.removedClusters.replace(removedClusters); - - // "auto-select" first cluster if available - if (!this.activeClusterId && newClusters.size) { - this.activeClusterId = Array.from(newClusters.values())[0].id; - } } toJSON(): ClusterStoreModel { diff --git a/src/common/workspace-store.ts b/src/common/workspace-store.ts index 047d0dfedd..3935dda02e 100644 --- a/src/common/workspace-store.ts +++ b/src/common/workspace-store.ts @@ -1,4 +1,4 @@ -import { action, computed, observable, reaction, toJS } from "mobx"; +import { action, computed, observable, toJS } from "mobx"; import { BaseStore } from "./base-store"; import { clusterStore } from "./cluster-store" @@ -22,15 +22,6 @@ export class WorkspaceStore extends BaseStore { super({ configName: "lens-workspace-store", }); - - // switch to first available cluster in current workspace - reaction(() => this.currentWorkspaceId, workspaceId => { - const clusters = clusterStore.getByWorkspaceId(workspaceId); - const activeClusterInWorkspace = clusters.some(cluster => cluster.id === clusterStore.activeClusterId); - if (!activeClusterInWorkspace) { - clusterStore.activeClusterId = clusters.length ? clusters[0].id : null; - } - }) } @observable currentWorkspaceId = WorkspaceStore.defaultId; diff --git a/src/main/cluster.ts b/src/main/cluster.ts index 82037a1fb4..c6528c702f 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -67,11 +67,6 @@ export class Cluster implements ClusterModel { @observable allowedNamespaces: string[] = []; @observable allowedResources: string[] = []; - @computed get host() { - const proxyHost = new URL(this.kubeProxyUrl).host; - return `${this.id}.${proxyHost}` - } - constructor(model: ClusterModel) { this.updateModel(model); } @@ -223,7 +218,7 @@ export class Cluster implements ClusterModel { json: true, timeout: 5000, headers: { - Host: this.host, // provide cluster-id for ClusterManager.getClusterForRequest() + Host: `${this.id}.${new URL(this.kubeProxyUrl).host}`, // required in ClusterManager.getClusterForRequest() ...(options.headers || {}), }, }) diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts index c4ae9a0d96..099007dd99 100644 --- a/src/main/window-manager.ts +++ b/src/main/window-manager.ts @@ -27,6 +27,7 @@ export class WindowManager { webPreferences: { nodeIntegration: true, enableRemoteModule: true, + webviewTag: true, }, }); this.windowState.manage(this.mainView); diff --git a/src/renderer/components/cluster-manager/cluster-manager.tsx b/src/renderer/components/cluster-manager/cluster-manager.tsx index 4b72704aa3..7baf5d1fd2 100644 --- a/src/renderer/components/cluster-manager/cluster-manager.tsx +++ b/src/renderer/components/cluster-manager/cluster-manager.tsx @@ -1,32 +1,34 @@ import "./cluster-manager.scss" import React from "react"; +import { Redirect, Route, Switch } from "react-router"; import { observer } from "mobx-react"; import { ClustersMenu } from "./clusters-menu"; import { BottomBar } from "./bottom-bar"; -import { cssNames, IClassName } from "../../utils"; -import { ClusterId } from "../../../common/cluster-store"; -import { Route, Switch } from "react-router"; -import { LandingPage, landingRoute } from "../+landing-page"; +import { LandingPage, landingRoute, landingURL } from "../+landing-page"; import { Preferences, preferencesRoute } from "../+preferences"; import { Workspaces, workspacesRoute } from "../+workspaces"; import { AddCluster, addClusterRoute } from "../+add-cluster"; -import { ClusterStatus } from "./cluster-status"; -import { clusterStatusRoute } from "./cluster-status.route"; - -interface Props { - className?: IClassName; - contentClass?: IClassName; -} +import { ClusterView } from "./cluster-view"; +import { clusterViewRoute, clusterViewURL } from "./cluster-view.route"; +import { clusterStore } from "../../../common/cluster-store"; @observer -export class ClusterManager extends React.Component { - activateView(clusterId: ClusterId) { +export class ClusterManager extends React.Component { + get startUrl() { + const { activeClusterId } = clusterStore; + if (activeClusterId) { + return clusterViewURL({ + params: { + clusterId: activeClusterId + } + }) + } + return landingURL() } render() { - const { className } = this.props; return ( -
+
@@ -34,8 +36,8 @@ export class ClusterManager extends React.Component { - -

Lens

}/> + +
diff --git a/src/renderer/components/cluster-manager/cluster-status.route.ts b/src/renderer/components/cluster-manager/cluster-status.route.ts deleted file mode 100644 index c3443f0e99..0000000000 --- a/src/renderer/components/cluster-manager/cluster-status.route.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { RouteProps } from "react-router"; -import { buildURL } from "../../navigation"; - -export const clusterStatusRoute: RouteProps = { - path: "/cluster-status" -} - -export const clusterStatusURL = buildURL(clusterStatusRoute.path) diff --git a/src/renderer/components/cluster-manager/cluster-status.tsx b/src/renderer/components/cluster-manager/cluster-status.tsx index db7b318d2e..270a670896 100644 --- a/src/renderer/components/cluster-manager/cluster-status.tsx +++ b/src/renderer/components/cluster-manager/cluster-status.tsx @@ -2,37 +2,37 @@ import type { KubeAuthProxyLog } from "../../../main/kube-auth-proxy"; import "./cluster-status.scss" import React from "react"; -import { disposeOnUnmount, observer } from "mobx-react"; +import { observer } from "mobx-react"; import { ipcRenderer } from "electron"; -import { autorun, computed, observable } from "mobx"; +import { computed, observable } from "mobx"; import { clusterIpc } from "../../../common/cluster-ipc"; import { Icon } from "../icon"; import { Button } from "../button"; import { cssNames } from "../../utils"; -import { navigate } from "../../navigation"; import { Cluster } from "../../../main/cluster"; +import { ClusterId, clusterStore } from "../../../common/cluster-store"; + +interface Props { + clusterId: ClusterId; +} @observer -export class ClusterStatus extends React.Component { +export class ClusterStatus extends React.Component { @observable authOutput: KubeAuthProxyLog[] = []; @observable isReconnecting = false; - // fixme + @computed get clusterId() { + return this.props.clusterId; + } + @computed get cluster(): Cluster { - return null; + return clusterStore.getById(this.clusterId); } @computed get hasErrors(): boolean { return this.authOutput.some(({ error }) => error) || !!this.cluster.failureReason; } - @disposeOnUnmount - autoRedirectToMain = autorun(() => { - if (this.cluster.accessible && !this.hasErrors) { - navigate("/"); - } - }) - async componentDidMount() { if (this.cluster.disconnected) { return; @@ -48,11 +48,11 @@ export class ClusterStatus extends React.Component { } componentWillUnmount() { - ipcRenderer.removeAllListeners(`kube-auth:${this.cluster.id}`); + ipcRenderer.removeAllListeners(`kube-auth:${this.clusterId}`); } async refreshClusterState() { - return clusterIpc.activate.invokeFromRenderer(); + return clusterIpc.activate.invokeFromRenderer(this.clusterId); } reconnect = async () => { diff --git a/src/renderer/components/cluster-manager/cluster-view.route.ts b/src/renderer/components/cluster-manager/cluster-view.route.ts new file mode 100644 index 0000000000..ed1e628610 --- /dev/null +++ b/src/renderer/components/cluster-manager/cluster-view.route.ts @@ -0,0 +1,22 @@ +import { matchPath, RouteProps } from "react-router"; +import { buildURL, navigation } from "../../navigation"; + +export interface IClusterViewRouteParams { + clusterId: string; +} + +export const clusterViewRoute: RouteProps = { + path: "/cluster/:clusterId" +} + +export const clusterViewURL = buildURL(clusterViewRoute.path) + +export function getMatchedClusterId(): string { + const matched = matchPath(navigation.location.pathname, { + ...clusterViewRoute, + exact: true, + }) + if (matched) { + return matched.params.clusterId; + } +} diff --git a/src/renderer/components/cluster-manager/cluster-view.scss b/src/renderer/components/cluster-manager/cluster-view.scss new file mode 100644 index 0000000000..66867ffdd8 --- /dev/null +++ b/src/renderer/components/cluster-manager/cluster-view.scss @@ -0,0 +1,10 @@ +.ClusterView { + width: 100%; + height: 100%; + border: 0; + //display: none; + + &.loaded { + display: block; + } +} diff --git a/src/renderer/components/cluster-manager/cluster-view.tsx b/src/renderer/components/cluster-manager/cluster-view.tsx new file mode 100644 index 0000000000..8b6ee45e12 --- /dev/null +++ b/src/renderer/components/cluster-manager/cluster-view.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import { autorun, computed, observable } from "mobx"; +import { disposeOnUnmount, observer } from "mobx-react"; +import { ClusterId, clusterStore } from "../../../common/cluster-store"; +import { getMatchedClusterId } from "./cluster-view.route"; +import { Cluster } from "../../../main/cluster"; + +@observer +export class ClusterView extends React.Component { + static views = observable.map() + static isLoaded = observable.map() + + @computed get cluster() { + return clusterStore.getById(getMatchedClusterId()) + } + + @computed get clusterView() { + return ClusterView.views.get(this.cluster?.id) + } + + componentDidMount() { + disposeOnUnmount(this, [ + autorun(() => this.activateView(this.cluster)) + ]) + } + + activateView = (cluster: Cluster) => { + if (!cluster || ClusterView.views.has(cluster.id)) { + return; + } + const view = document.createElement("webview"); + view.className = "ClusterView" + view.src = `${location.protocol}://${cluster.id}.${location.host}` + view.onload = () => console.log('CLUSTER VIEW READY!', cluster); + document.body.appendChild(view); + ClusterView.views.set(cluster.id, view); + } + + render() { + const { cluster } = this; + if (cluster && cluster.accessible) { + + } + return ""; + } +} diff --git a/src/renderer/components/cluster-manager/clusters-menu.tsx b/src/renderer/components/cluster-manager/clusters-menu.tsx index 2d58c82367..1015531ed8 100644 --- a/src/renderer/components/cluster-manager/clusters-menu.tsx +++ b/src/renderer/components/cluster-manager/clusters-menu.tsx @@ -20,7 +20,7 @@ import { landingURL } from "../+landing-page"; import { Tooltip } from "../tooltip"; import { ConfirmDialog } from "../confirm-dialog"; import { clusterIpc } from "../../../common/cluster-ipc"; -import { clusterStatusURL } from "./cluster-status.route"; +import { clusterViewURL, getMatchedClusterId } from "./cluster-view.route"; // fixme: allow to rearrange clusters with drag&drop @@ -32,12 +32,9 @@ interface Props { export class ClustersMenu extends React.Component { @observable showHint = true; - showCluster = (clusterId: ClusterId) => { - if (clusterStore.activeClusterId === clusterId) { - navigate("/"); // redirect to index - } else { - clusterStore.activeClusterId = clusterId; - } + activateCluster = (clusterId: ClusterId) => { + clusterStore.activeClusterId = clusterId; + navigate(clusterViewURL({ params: { clusterId } })) } addCluster = () => { @@ -57,9 +54,6 @@ export class ClustersMenu extends React.Component { label: _i18n._(t`Disconnect`), click: async () => { await clusterIpc.disconnect.invokeFromRenderer(cluster.id); - if (cluster.id === clusterStore.activeClusterId) { - navigate(clusterStatusURL()); - } } })) } @@ -110,8 +104,8 @@ export class ClustersMenu extends React.Component { key={cluster.id} showErrors={true} cluster={cluster} - isActive={cluster.id === clusterStore.activeClusterId} - onClick={() => this.showCluster(cluster.id)} + isActive={cluster.id === getMatchedClusterId()} + onClick={() => this.activateCluster(cluster.id)} onContextMenu={() => this.showContextMenu(cluster)} /> )