mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
cluster-view fixes
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
31f4a75608
commit
402d07d8e5
@ -62,11 +62,13 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
migrations: migrations,
|
migrations: migrations,
|
||||||
});
|
});
|
||||||
if (ipcRenderer) {
|
if (ipcRenderer) {
|
||||||
ipcRenderer.on("cluster:state", (event, clusterState: ClusterState) => {
|
ipcRenderer.on("cluster:state", (event, state: ClusterState) => {
|
||||||
this.applyWithoutSync(() => {
|
this.applyWithoutSync(() => {
|
||||||
logger.debug(`[CLUSTER-STORE]: received state update for cluster=${clusterState.id}`, clusterState);
|
logger.debug(`[CLUSTER-STORE]: received push-state at ${location.host}`, state);
|
||||||
const cluster = this.getById(clusterState.id);
|
const cluster = this.getById(state.id);
|
||||||
if (cluster) cluster.updateModel(clusterState)
|
if (cluster) {
|
||||||
|
cluster.updateModel(state)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import type { ClusterId, ClusterModel, ClusterPreferences } from "../common/clus
|
|||||||
import type { IMetricsReqParams } from "../renderer/api/endpoints/metrics.api";
|
import type { IMetricsReqParams } from "../renderer/api/endpoints/metrics.api";
|
||||||
import type { WorkspaceId } from "../common/workspace-store";
|
import type { WorkspaceId } from "../common/workspace-store";
|
||||||
import type { FeatureStatusMap } from "./feature"
|
import type { FeatureStatusMap } from "./feature"
|
||||||
import { action, observable, reaction, toJS, when } from "mobx";
|
import { action, computed, observable, reaction, toJS, when } from "mobx";
|
||||||
import { apiKubePrefix } from "../common/vars";
|
import { apiKubePrefix } from "../common/vars";
|
||||||
import { broadcastIpc } from "../common/ipc";
|
import { broadcastIpc } from "../common/ipc";
|
||||||
import { ContextHandler } from "./context-handler"
|
import { ContextHandler } from "./context-handler"
|
||||||
@ -67,6 +67,10 @@ export class Cluster implements ClusterModel {
|
|||||||
@observable allowedNamespaces: string[] = [];
|
@observable allowedNamespaces: string[] = [];
|
||||||
@observable allowedResources: string[] = [];
|
@observable allowedResources: string[] = [];
|
||||||
|
|
||||||
|
@computed get available() {
|
||||||
|
return this.accessible && !this.disconnected;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(model: ClusterModel) {
|
constructor(model: ClusterModel) {
|
||||||
this.updateModel(model);
|
this.updateModel(model);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,13 +31,15 @@ import { isAllowedResource } from "../../common/rbac";
|
|||||||
import { ClusterSettings, clusterSettingsRoute } from "./+cluster-settings";
|
import { ClusterSettings, clusterSettingsRoute } from "./+cluster-settings";
|
||||||
import { ErrorBoundary } from "./error-boundary";
|
import { ErrorBoundary } from "./error-boundary";
|
||||||
import { Terminal } from "./dock/terminal";
|
import { Terminal } from "./dock/terminal";
|
||||||
import { getHostedCluster } from "../../common/cluster-store";
|
import { getHostedCluster, getHostedClusterId } from "../../common/cluster-store";
|
||||||
|
import logger from "../../main/logger";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class App extends React.Component {
|
export class App extends React.Component {
|
||||||
static async init() {
|
static async init() {
|
||||||
|
logger.info(`[APP]: Init dashboard, clusterId=${getHostedClusterId()}`)
|
||||||
await Terminal.preloadFonts()
|
await Terminal.preloadFonts()
|
||||||
await getHostedCluster().whenInitialized;
|
await getHostedCluster().whenInitialized; // wait for cluster-state before initial render
|
||||||
}
|
}
|
||||||
|
|
||||||
get startURL() {
|
get startURL() {
|
||||||
|
|||||||
@ -11,6 +11,19 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#lens-views {
|
||||||
|
grid-area: main;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.ClustersMenu {
|
.ClustersMenu {
|
||||||
grid-area: menu;
|
grid-area: menu;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import "./cluster-manager.scss"
|
import "./cluster-manager.scss"
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Redirect, Route, Switch } from "react-router";
|
import { Redirect, Route, Switch } from "react-router";
|
||||||
import { observer } from "mobx-react";
|
import { disposeOnUnmount, observer } from "mobx-react";
|
||||||
import { ClustersMenu } from "./clusters-menu";
|
import { ClustersMenu } from "./clusters-menu";
|
||||||
import { BottomBar } from "./bottom-bar";
|
import { BottomBar } from "./bottom-bar";
|
||||||
import { LandingPage, landingRoute, landingURL } from "../+landing-page";
|
import { LandingPage, landingRoute, landingURL } from "../+landing-page";
|
||||||
@ -9,11 +9,71 @@ import { Preferences, preferencesRoute } from "../+preferences";
|
|||||||
import { Workspaces, workspacesRoute } from "../+workspaces";
|
import { Workspaces, workspacesRoute } from "../+workspaces";
|
||||||
import { AddCluster, addClusterRoute } from "../+add-cluster";
|
import { AddCluster, addClusterRoute } from "../+add-cluster";
|
||||||
import { ClusterView } from "./cluster-view";
|
import { ClusterView } from "./cluster-view";
|
||||||
import { clusterViewRoute, clusterViewURL } from "./cluster-view.route";
|
import { clusterViewRoute, clusterViewURL, getMatchedCluster } from "./cluster-view.route";
|
||||||
import { clusterStore } from "../../../common/cluster-store";
|
import { ClusterId, clusterStore } from "../../../common/cluster-store";
|
||||||
|
import { WebviewTag } from "electron";
|
||||||
|
import { observable, reaction } from "mobx";
|
||||||
|
import logger from "../../../main/logger";
|
||||||
|
import { clusterIpc } from "../../../common/cluster-ipc";
|
||||||
|
import { cssNames } from "../../utils";
|
||||||
|
|
||||||
|
// fixme: hide active view on disconnect
|
||||||
|
// fixme: webview reloading/blinking when switching common <-> cluster views
|
||||||
|
|
||||||
|
interface LensView {
|
||||||
|
isLoaded?: boolean
|
||||||
|
clusterId: ClusterId;
|
||||||
|
view: WebviewTag
|
||||||
|
}
|
||||||
|
|
||||||
|
const lensViews = observable.map<ClusterId, LensView>();
|
||||||
|
|
||||||
|
// fixme: figure out how to replace webview-tag to iframe
|
||||||
|
function initView(clusterId: ClusterId) {
|
||||||
|
if (lensViews.has(clusterId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.info(`[CLUSTER-VIEW]: init dashboard, clusterId=${clusterId}`)
|
||||||
|
const lensViewsHolder = document.getElementById("lens-views"); // defined in cluster-manager's css-grid
|
||||||
|
const webview = document.createElement("webview");
|
||||||
|
webview.setAttribute("src", `//${clusterId}.${location.host}`)
|
||||||
|
webview.setAttribute("nodeintegration", "true")
|
||||||
|
webview.setAttribute("enableremotemodule", "true")
|
||||||
|
webview.addEventListener("did-finish-load", async () => {
|
||||||
|
logger.info(`[CLUSTER-VIEW]: loaded, clusterId=${clusterId}`)
|
||||||
|
await clusterIpc.init.invokeFromRenderer(clusterId); // push cluster-state to webview and render dashboard
|
||||||
|
lensViews.get(clusterId).isLoaded = true;
|
||||||
|
refreshViews();
|
||||||
|
});
|
||||||
|
webview.addEventListener("did-fail-load", (event) => {
|
||||||
|
logger.error(`[CLUSTER-VIEW]: failed to load, clusterId=${clusterId}`, event)
|
||||||
|
});
|
||||||
|
lensViews.set(clusterId, { clusterId, view: webview });
|
||||||
|
lensViewsHolder.appendChild(webview); // add to dom and init cluster-page loading
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshViews() {
|
||||||
|
const activeCluster = getMatchedCluster()
|
||||||
|
lensViews.forEach(({ clusterId, view, isLoaded }) => {
|
||||||
|
const isVisible = clusterId === activeCluster?.id && activeCluster?.available;
|
||||||
|
view.style.display = isLoaded && isVisible ? "flex" : "none"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class ClusterManager extends React.Component {
|
export class ClusterManager extends React.Component {
|
||||||
|
componentDidMount() {
|
||||||
|
disposeOnUnmount(this, [
|
||||||
|
reaction(getMatchedCluster, cluster => {
|
||||||
|
// auto-refresh visibility for active cluster
|
||||||
|
if (cluster) initView(cluster.id);
|
||||||
|
refreshViews();
|
||||||
|
}, {
|
||||||
|
fireImmediately: true
|
||||||
|
})
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
get startUrl() {
|
get startUrl() {
|
||||||
const { activeClusterId } = clusterStore;
|
const { activeClusterId } = clusterStore;
|
||||||
if (activeClusterId) {
|
if (activeClusterId) {
|
||||||
@ -27,9 +87,11 @@ export class ClusterManager extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const cluster = getMatchedCluster();
|
||||||
return (
|
return (
|
||||||
<div className="ClusterManager">
|
<div className="ClusterManager">
|
||||||
<div id="draggable-top"/>
|
<div id="draggable-top"/>
|
||||||
|
<div id="lens-views" className={cssNames({ active: !!cluster })}/>
|
||||||
<main>
|
<main>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route component={LandingPage} {...landingRoute}/>
|
<Route component={LandingPage} {...landingRoute}/>
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
@include hidden-scrollbar;
|
@include hidden-scrollbar;
|
||||||
max-width: 70vw;
|
max-width: 70vw;
|
||||||
max-height: 40vh;
|
max-height: 40vh;
|
||||||
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Icon {
|
.Icon {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { matchPath, RouteProps } from "react-router";
|
import { matchPath, RouteProps } from "react-router";
|
||||||
import { buildURL, navigation } from "../../navigation";
|
import { buildURL, navigation } from "../../navigation";
|
||||||
|
import { clusterStore } from "../../../common/cluster-store";
|
||||||
|
|
||||||
export interface IClusterViewRouteParams {
|
export interface IClusterViewRouteParams {
|
||||||
clusterId: string;
|
clusterId: string;
|
||||||
@ -20,3 +21,7 @@ export function getMatchedClusterId(): string {
|
|||||||
return matched.params.clusterId;
|
return matched.params.clusterId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMatchedCluster() {
|
||||||
|
return clusterStore.getById(getMatchedClusterId())
|
||||||
|
}
|
||||||
|
|||||||
@ -5,20 +5,3 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#lens-views {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
body > & {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,101 +1,26 @@
|
|||||||
import "./cluster-view.scss"
|
import "./cluster-view.scss"
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { WebviewTag } from "electron";
|
import { observer } from "mobx-react";
|
||||||
import { observable, reaction } from "mobx";
|
import { getMatchedCluster } from "./cluster-view.route";
|
||||||
import { disposeOnUnmount, observer } from "mobx-react";
|
|
||||||
import { ClusterId, clusterStore } from "../../../common/cluster-store";
|
|
||||||
import { getMatchedClusterId } from "./cluster-view.route";
|
|
||||||
import { ClusterStatus } from "./cluster-status";
|
import { ClusterStatus } from "./cluster-status";
|
||||||
import { clusterIpc } from "../../../common/cluster-ipc";
|
|
||||||
import logger from "../../../main/logger";
|
|
||||||
|
|
||||||
// fixme: hide active view on disconnect
|
|
||||||
// fixme: webview reloading/blinking when switching common <-> cluster views
|
|
||||||
|
|
||||||
interface LensView {
|
|
||||||
clusterId: ClusterId;
|
|
||||||
webview: WebviewTag
|
|
||||||
isLoaded?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const lensViews = observable.map<ClusterId, LensView>()
|
|
||||||
const lensViewsHolder = document.createElement("div")
|
|
||||||
lensViewsHolder.id = "lens-views"
|
|
||||||
document.body.appendChild(lensViewsHolder);
|
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class ClusterView extends React.Component {
|
export class ClusterView extends React.Component {
|
||||||
protected placeholder: HTMLElement;
|
renderContent() {
|
||||||
|
const cluster = getMatchedCluster();
|
||||||
get cluster() {
|
if (!cluster) {
|
||||||
return clusterStore.getById(getMatchedClusterId())
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.attachViews();
|
|
||||||
disposeOnUnmount(this, [
|
|
||||||
reaction(() => this.cluster, selectedCluster => {
|
|
||||||
this.initView(selectedCluster?.id)
|
|
||||||
this.refreshViews()
|
|
||||||
}, {
|
|
||||||
fireImmediately: true
|
|
||||||
})
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.detachViews();
|
|
||||||
}
|
|
||||||
|
|
||||||
// fixme: figure out how to replace webview-tag to iframe
|
|
||||||
initView = (clusterId: ClusterId) => {
|
|
||||||
if (!clusterId || lensViews.has(clusterId)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.info(`[WEBVIEW]: init view for clusterId=${clusterId}`)
|
if (!cluster.available) {
|
||||||
const webview = document.createElement("webview");
|
return <ClusterStatus clusterId={cluster.id} className="box center"/>
|
||||||
webview.setAttribute("src", `//${clusterId}.${location.host}`)
|
}
|
||||||
webview.setAttribute("nodeintegration", "true")
|
|
||||||
webview.setAttribute("enableremotemodule", "true")
|
|
||||||
webview.addEventListener("did-finish-load", () => {
|
|
||||||
logger.info(`[WEBVIEW]: loaded, clusterId=${clusterId}`)
|
|
||||||
clusterIpc.init.invokeFromRenderer(clusterId); // push cluster-state to webview
|
|
||||||
lensViews.get(clusterId).isLoaded = true;
|
|
||||||
this.refreshViews();
|
|
||||||
});
|
|
||||||
webview.addEventListener("did-fail-load", (event) => {
|
|
||||||
logger.error(`[WEBVIEW]: failed to load, clusterId=${clusterId}`, event)
|
|
||||||
});
|
|
||||||
lensViews.set(clusterId, { clusterId, webview });
|
|
||||||
lensViewsHolder.appendChild(webview); // add to dom and start loading frame
|
|
||||||
}
|
|
||||||
|
|
||||||
attachViews = () => {
|
|
||||||
this.placeholder.appendChild(lensViewsHolder)
|
|
||||||
}
|
|
||||||
|
|
||||||
detachViews = () => {
|
|
||||||
document.body.appendChild(lensViewsHolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshViews = () => {
|
|
||||||
lensViews.forEach(({ clusterId, webview, isLoaded }) => {
|
|
||||||
const isActive = clusterId === this.cluster?.id;
|
|
||||||
webview.style.display = isLoaded && isActive ? "flex" : "none"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
bindRef = (elem: HTMLElement) => {
|
|
||||||
this.placeholder = elem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { cluster } = this;
|
const cluster = getMatchedCluster();
|
||||||
const view = lensViews.get(cluster?.id);
|
|
||||||
const showStatusPage = cluster && (!cluster.accessible || !view?.isLoaded);
|
|
||||||
return (
|
return (
|
||||||
<div className="ClusterView" ref={this.bindRef}>
|
<div className="ClusterView flex column">
|
||||||
{showStatusPage && <ClusterStatus clusterId={cluster.id}/>}
|
{this.renderContent()}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user