mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
hotbar store
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
8a0cf91ab4
commit
4ac0c7c899
@ -2,7 +2,7 @@
|
||||
"name": "kontena-lens",
|
||||
"productName": "Lens",
|
||||
"description": "Lens - The Kubernetes IDE",
|
||||
"version": "4.2.0-rc.2",
|
||||
"version": "5.0.0-alpha.0",
|
||||
"main": "static/build/main.js",
|
||||
"copyright": "© 2021, Mirantis, Inc.",
|
||||
"license": "MIT",
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { observable } from "mobx";
|
||||
import { catalogCategoryRegistry } from "../catalog-category-registry";
|
||||
import { CatalogCategory, CatalogEntity, CatalogEntityActionContext, CatalogEntityData, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog-entity";
|
||||
import { CatalogCategory, CatalogEntity, CatalogEntityActionContext, CatalogEntityContextMenuContext, CatalogEntityData, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog-entity";
|
||||
import { clusterDisconnectHandler } from "../cluster-ipc";
|
||||
import { clusterStore } from "../cluster-store";
|
||||
import { requestMain } from "../ipc";
|
||||
|
||||
export type KubernetesClusterSpec = {
|
||||
kubeconfigPath: string;
|
||||
@ -45,8 +47,30 @@ export class KubernetesCluster implements CatalogEntity {
|
||||
//
|
||||
}
|
||||
|
||||
async onContextMenuOpen() {
|
||||
//
|
||||
async onContextMenuOpen(context: CatalogEntityContextMenuContext) {
|
||||
context.menuItems = [
|
||||
{
|
||||
icon: "settings",
|
||||
title: "Settings",
|
||||
onClick: async () => context.navigate(`/cluster/${this.metadata.uid}/settings`)
|
||||
},
|
||||
{
|
||||
icon: "delete",
|
||||
title: "Delete",
|
||||
onClick: async () => clusterStore.removeById(this.metadata.uid)
|
||||
},
|
||||
];
|
||||
|
||||
if (this.status.active) {
|
||||
context.menuItems.unshift({
|
||||
icon: "link_off",
|
||||
title: "Disconnect",
|
||||
onClick: async () => {
|
||||
clusterStore.deactivate(this.metadata.uid);
|
||||
requestMain(clusterDisconnectHandler, this.metadata.uid);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -40,8 +40,14 @@ export interface CatalogEntityActionContext {
|
||||
navigate: (url: string) => void;
|
||||
}
|
||||
|
||||
export type CatalogEntityContextMenu = {
|
||||
icon: string;
|
||||
title: string;
|
||||
onClick: () => Promise<void>;
|
||||
};
|
||||
|
||||
export interface CatalogEntityContextMenuContext extends CatalogEntityActionContext {
|
||||
menu: any
|
||||
menuItems: CatalogEntityContextMenu[];
|
||||
}
|
||||
|
||||
export type CatalogEntityData = {
|
||||
|
||||
@ -4,7 +4,6 @@ import { appEventBus } from "./event-bus";
|
||||
import { ResourceApplier } from "../main/resource-applier";
|
||||
import { ipcMain, IpcMainInvokeEvent } from "electron";
|
||||
import { clusterFrameMap } from "./cluster-frames";
|
||||
import { catalogEntityRegistry } from "./catalog-entity-registry";
|
||||
|
||||
export const clusterActivateHandler = "cluster:activate";
|
||||
export const clusterSetFrameIdHandler = "cluster:set-frame-id";
|
||||
@ -18,10 +17,6 @@ if (ipcMain) {
|
||||
const cluster = clusterStore.getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
catalogEntityRegistry.getItemsForApiKind("entity.k8slens.dev/v1alpha1", "KubernetesCluster").forEach((item) => {
|
||||
if (item.metadata.uid === cluster.id) item.status.active = true;
|
||||
});
|
||||
|
||||
return cluster.activate(force);
|
||||
}
|
||||
});
|
||||
@ -48,9 +43,6 @@ if (ipcMain) {
|
||||
|
||||
if (cluster) {
|
||||
cluster.disconnect();
|
||||
catalogEntityRegistry.getItemsForApiKind("entity.k8slens.dev/v1alpha1", "KubernetesCluster").forEach((item) => {
|
||||
if (item.metadata.uid === cluster.id) item.status.active = false;
|
||||
});
|
||||
clusterFrameMap.delete(cluster.id);
|
||||
}
|
||||
});
|
||||
|
||||
@ -104,7 +104,6 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
@observable removedClusters = observable.map<ClusterId, Cluster>();
|
||||
@observable clusters = observable.map<ClusterId, Cluster>();
|
||||
|
||||
|
||||
private static stateRequestChannel = "cluster:states";
|
||||
|
||||
private constructor() {
|
||||
|
||||
64
src/common/hotbar-store.ts
Normal file
64
src/common/hotbar-store.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { action, comparer, observable, toJS } from "mobx";
|
||||
import { BaseStore } from "./base-store";
|
||||
import migrations from "../migrations/hotbar-store";
|
||||
|
||||
export interface HotbarItem {
|
||||
entity: {
|
||||
uid: string;
|
||||
};
|
||||
params?: {
|
||||
[key: string]: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface Hotbar {
|
||||
name: string;
|
||||
items: HotbarItem[];
|
||||
}
|
||||
|
||||
export interface HotbarStoreModel {
|
||||
hotbars: Hotbar[];
|
||||
}
|
||||
|
||||
export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
||||
@observable hotbars: Hotbar[] = [];
|
||||
|
||||
private constructor() {
|
||||
super({
|
||||
configName: "lens-hotbar-store",
|
||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
||||
syncOptions: {
|
||||
equals: comparer.structural,
|
||||
},
|
||||
migrations,
|
||||
});
|
||||
}
|
||||
|
||||
@action protected async fromStore(data: Partial<HotbarStoreModel> = {}) {
|
||||
this.hotbars = data.hotbars || [];
|
||||
}
|
||||
|
||||
getByName(name: string) {
|
||||
return this.hotbars.find((hotbar) => hotbar.name === name);
|
||||
}
|
||||
|
||||
add(hotbar: Hotbar) {
|
||||
this.hotbars.push(hotbar);
|
||||
}
|
||||
|
||||
remove(hotbar: Hotbar) {
|
||||
this.hotbars = this.hotbars.filter((h) => h !== hotbar);
|
||||
}
|
||||
|
||||
toJSON(): HotbarStoreModel {
|
||||
const model: HotbarStoreModel = {
|
||||
hotbars: this.hotbars
|
||||
};
|
||||
|
||||
return toJS(model, {
|
||||
recurseEverything: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const hotbarStore = HotbarStore.getInstance<HotbarStore>();
|
||||
@ -21,6 +21,7 @@ export class CatalogPusher {
|
||||
}
|
||||
|
||||
broadcast() {
|
||||
console.log("BROADCAST");
|
||||
broadcastMessage("catalog:items", toJS(this.catalog.items, { recurseEverything: true }));
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,9 +69,7 @@ export class ClusterManager extends Singleton {
|
||||
if (entityIndex === -1) {
|
||||
this.catalogSource.push(newEntity);
|
||||
} else {
|
||||
const entity = this.catalogSource[entityIndex] as KubernetesCluster;
|
||||
|
||||
newEntity.status.active = entity.status.active;
|
||||
newEntity.status.active = !cluster.disconnected;
|
||||
this.catalogSource.splice(entityIndex, 1, newEntity);
|
||||
}
|
||||
});
|
||||
@ -86,7 +84,7 @@ export class ClusterManager extends Singleton {
|
||||
name: cluster.name,
|
||||
source: "local",
|
||||
labels: {
|
||||
"distro": cluster.metadata["distro"]?.toString()
|
||||
"distro": (cluster.metadata["distribution"] || "unknown").toString()
|
||||
}
|
||||
},
|
||||
spec: {
|
||||
|
||||
@ -31,6 +31,7 @@ import { startUpdateChecking } from "./app-updater";
|
||||
import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
|
||||
import { CatalogPusher } from "./catalog-pusher";
|
||||
import { catalogEntityRegistry } from "../common/catalog-entity-registry";
|
||||
import { hotbarStore } from "../common/hotbar-store";
|
||||
|
||||
const workingDir = path.join(app.getPath("appData"), appName);
|
||||
let proxyPort: number;
|
||||
@ -108,6 +109,7 @@ app.on("ready", async () => {
|
||||
await Promise.all([
|
||||
userStore.load(),
|
||||
clusterStore.load(),
|
||||
hotbarStore.load(),
|
||||
extensionsStore.load(),
|
||||
filesystemProvisionerStore.load(),
|
||||
]);
|
||||
|
||||
28
src/migrations/hotbar-store/5.0.0-alpha.0.ts
Normal file
28
src/migrations/hotbar-store/5.0.0-alpha.0.ts
Normal file
@ -0,0 +1,28 @@
|
||||
// Cleans up a store that had the state related data stored
|
||||
import { Hotbar } from "../../common/hotbar-store";
|
||||
import { clusterStore } from "../../common/cluster-store";
|
||||
import { migration } from "../migration-wrapper";
|
||||
|
||||
export default migration({
|
||||
version: "5.0.0-alpha.0",
|
||||
run(store) {
|
||||
const hotbars: Hotbar[] = [];
|
||||
|
||||
clusterStore.enabledClustersList.forEach((cluster: any) => {
|
||||
const name = cluster.workspace || "default";
|
||||
let hotbar = hotbars.find((h) => h.name === name);
|
||||
|
||||
if (!hotbar) {
|
||||
hotbar = { name, items: [] };
|
||||
hotbars.push(hotbar);
|
||||
}
|
||||
|
||||
hotbar.items.push({
|
||||
entity: { uid: cluster.id },
|
||||
params: {}
|
||||
});
|
||||
});
|
||||
|
||||
store.set("hotbars", hotbars);
|
||||
}
|
||||
});
|
||||
7
src/migrations/hotbar-store/index.ts
Normal file
7
src/migrations/hotbar-store/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
// Hotbar store migrations
|
||||
|
||||
import version500alpha0 from "./5.0.0-alpha.0";
|
||||
|
||||
export default {
|
||||
...version500alpha0,
|
||||
};
|
||||
@ -4,7 +4,7 @@ import { CatalogEntity, CatalogEntityData } from "../../common/catalog-entity";
|
||||
import { catalogCategoryRegistry, CatalogCategoryRegistry } from "../../common/catalog-category-registry";
|
||||
import "../../common/catalog-entities";
|
||||
|
||||
export { CatalogEntity, CatalogEntityData } from "../../common/catalog-entity";
|
||||
export { CatalogEntity, CatalogEntityData, CatalogEntityContextMenuContext } from "../../common/catalog-entity";
|
||||
|
||||
export class CatalogEntityRegistry {
|
||||
@observable protected _items: CatalogEntity[] = observable.array([], { deep: true });
|
||||
|
||||
@ -14,6 +14,7 @@ import * as LensExtensions from "../extensions/extension-api";
|
||||
import { extensionDiscovery } from "../extensions/extension-discovery";
|
||||
import { extensionLoader } from "../extensions/extension-loader";
|
||||
import { extensionsStore } from "../extensions/extensions-store";
|
||||
import { hotbarStore } from "../common/hotbar-store";
|
||||
import { filesystemProvisionerStore } from "../main/extension-filesystem";
|
||||
import { App } from "./components/app";
|
||||
import { LensApp } from "./lens-app";
|
||||
@ -55,6 +56,7 @@ export async function bootstrap(App: AppComponent) {
|
||||
// preload common stores
|
||||
await Promise.all([
|
||||
userStore.load(),
|
||||
hotbarStore.load(),
|
||||
clusterStore.load(),
|
||||
extensionsStore.load(),
|
||||
filesystemProvisionerStore.load(),
|
||||
|
||||
@ -26,9 +26,25 @@ export class CatalogEntityItem implements ItemObject {
|
||||
return this.entity.status.phase;
|
||||
}
|
||||
|
||||
get labels() {
|
||||
const labels: string[] = [];
|
||||
|
||||
Object.keys(this.entity.metadata.labels).forEach((key) => {
|
||||
const value = this.entity.metadata.labels[key];
|
||||
|
||||
labels.push(`${key}=${value}`);
|
||||
});
|
||||
|
||||
return labels;
|
||||
}
|
||||
|
||||
onRun(ctx: any) {
|
||||
this.entity.onRun(ctx);
|
||||
}
|
||||
|
||||
onContextMenuOpen(ctx: any) {
|
||||
this.entity.onContextMenuOpen(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@autobind()
|
||||
|
||||
@ -7,6 +7,10 @@ import { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store";
|
||||
import { navigate } from "../../navigation";
|
||||
import { kebabCase } from "lodash";
|
||||
import { PageLayout } from "../layout/page-layout";
|
||||
import { MenuItem, MenuActions } from "../menu";
|
||||
import { Icon } from "../icon";
|
||||
import { CatalogEntityContextMenuContext } from "../../api/catalog-entity-registry";
|
||||
import { Badge } from "../badge";
|
||||
|
||||
enum sortBy {
|
||||
name = "name",
|
||||
@ -47,13 +51,35 @@ export class LandingPage extends React.Component {
|
||||
}}
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: sortBy.name },
|
||||
{ title: "Labels", className: "labels" },
|
||||
{ title: "Status", className: "status", sortBy: sortBy.status },
|
||||
]}
|
||||
renderTableContents={(item: CatalogEntityItem) => [
|
||||
item.name,
|
||||
item.labels.map((label) => <Badge key={label} label={label} title={label} />),
|
||||
{ title: item.phase, className: kebabCase(item.phase) }
|
||||
]}
|
||||
onDetails={(item: CatalogEntityItem) => item.onRun({ navigate: (url: string) => navigate(url)})}
|
||||
renderItemMenu={(item: CatalogEntityItem) => {
|
||||
const menuOpenContext: CatalogEntityContextMenuContext = {
|
||||
menuItems: [],
|
||||
navigate: (url: string) => navigate(url)
|
||||
};
|
||||
|
||||
item.onContextMenuOpen(menuOpenContext);
|
||||
|
||||
return (
|
||||
<MenuActions>
|
||||
{ menuOpenContext.menuItems.map((menuItem) => {
|
||||
return (
|
||||
<MenuItem key={menuItem.title} onClick={() => menuItem.onClick()}>
|
||||
<Icon material={menuItem.icon} interactive={true} title={menuItem.title}/> {menuItem.title}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</MenuActions>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</PageLayout>
|
||||
);
|
||||
|
||||
@ -5,8 +5,8 @@ import { observer } from "mobx-react";
|
||||
import { HotbarIcon } from "./hotbar-icon";
|
||||
import { cssNames, IClassName } from "../../utils";
|
||||
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||
import { KubernetesCluster } from "../../../common/catalog-entities/kubernetes-cluster";
|
||||
import { navigate } from "../../navigation";
|
||||
import { hotbarStore } from "../../../common/hotbar-store";
|
||||
|
||||
interface Props {
|
||||
className?: IClassName;
|
||||
@ -16,7 +16,8 @@ interface Props {
|
||||
export class HotbarMenu extends React.Component<Props> {
|
||||
render() {
|
||||
const { className } = this.props;
|
||||
const items = catalogEntityRegistry.getItemsForApiKind<KubernetesCluster>("entity.k8slens.dev/v1alpha1", "KubernetesCluster");
|
||||
const hotbar = hotbarStore.getByName("default"); // FIXME
|
||||
const items = hotbar.items.map((item) => catalogEntityRegistry.items.find((entity) => entity.metadata.uid === item.entity.uid)).filter(Boolean);
|
||||
const runContext = {
|
||||
navigate: (url: string) => navigate(url)
|
||||
};
|
||||
@ -31,7 +32,6 @@ export class HotbarMenu extends React.Component<Props> {
|
||||
entity={entity}
|
||||
isActive={entity.status.active}
|
||||
onClick={() => entity.onRun(runContext)}
|
||||
onContextMenu={() => entity.onContextMenuOpen()}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user