diff --git a/src/main/catalog-pusher.ts b/src/main/catalog-pusher.ts index 8ec922c3ed..c2d7267b92 100644 --- a/src/main/catalog-pusher.ts +++ b/src/main/catalog-pusher.ts @@ -19,41 +19,15 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { autorun } from "mobx"; -import { broadcastMessage, subscribeToBroadcast, unsubscribeFromBroadcast } from "../common/ipc"; -import type { CatalogEntityRegistry } from "./catalog"; +import { reaction, toJS } from "mobx"; +import { broadcastMessage } from "../common/ipc"; +import type { CatalogEntityRegistry} from "./catalog"; import "../common/catalog-entities/kubernetes-cluster"; -import { Disposer, disposer } from "../common/utils"; -import logger from "./logger"; -export class CatalogPusher { - static logPrefix = `[CatalogPusher]`; - - static init(catalog: CatalogEntityRegistry) { - return new CatalogPusher(catalog).init(); - } - - private constructor(private catalog: CatalogEntityRegistry) { - } - - private init(): Disposer { - logger.info(`${CatalogPusher.logPrefix}: init`); - - const dispose = disposer(); - - const broadcastItems = () => { - logger.info(`${CatalogPusher.logPrefix}: broadcasting entities`); - broadcastMessage("catalog:items", this.catalog.items); - }; - - // broadcast entities when catalog items gets updated - dispose.push(autorun(broadcastItems)); - - // broadcast entities from IPC-event request - const listener = subscribeToBroadcast("catalog:broadcast", broadcastItems); - - dispose.push(() => unsubscribeFromBroadcast("catalog:broadcast", listener)); - - return dispose; - } +export function pushCatalogToRenderer(catalog: CatalogEntityRegistry) { + return reaction(() => toJS(catalog.items, { recurseEverything: true }), (items) => { + broadcastMessage("catalog:items", items); + }, { + fireImmediately: true, + }); } diff --git a/src/main/index.ts b/src/main/index.ts index 042e980642..271010d922 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -45,11 +45,11 @@ import type { LensExtensionId } from "../extensions/lens-extension"; import { FilesystemProvisionerStore } from "./extension-filesystem"; import { installDeveloperTools } from "./developer-tools"; import { LensProtocolRouterMain } from "./protocol-handler"; -import { getAppVersion, getAppVersionFromProxyServer } from "../common/utils"; +import { disposer, getAppVersion, getAppVersionFromProxyServer } from "../common/utils"; import { bindBroadcastHandlers } from "../common/ipc"; import { startUpdateChecking } from "./app-updater"; import { IpcRendererNavigationEvents } from "../renderer/navigation/events"; -import { CatalogPusher } from "./catalog-pusher"; +import { pushCatalogToRenderer } from "./catalog-pusher"; import { catalogEntityRegistry } from "./catalog"; import { HotbarStore } from "../common/hotbar-store"; import { HelmRepoManager } from "./helm/helm-repo-manager"; @@ -58,6 +58,7 @@ import { handleWsUpgrade } from "./proxy/ws-upgrade"; import configurePackages from "../common/configure-packages"; const workingDir = path.join(app.getPath("appData"), appName); +const cleanup = disposer(); app.setName(appName); @@ -144,6 +145,7 @@ app.on("ready", async () => { const lensProxy = LensProxy.createInstance(handleWsUpgrade); ClusterManager.createInstance(); + KubeconfigSyncManager.createInstance(); try { logger.info("🔌 Starting LensProxy"); @@ -188,8 +190,8 @@ app.on("ready", async () => { } ipcMain.on(IpcRendererNavigationEvents.LOADED, () => { - KubeconfigSyncManager.createInstance().startSync(); - CatalogPusher.init(catalogEntityRegistry); + cleanup.push(pushCatalogToRenderer(catalogEntityRegistry)); + KubeconfigSyncManager.getInstance().startSync(); startUpdateChecking(); LensProtocolRouterMain.getInstance().rendererLoaded = true; }); @@ -249,6 +251,7 @@ app.on("will-quit", (event) => { appEventBus.emit({name: "app", action: "close"}); ClusterManager.getInstance(false)?.stop(); // close cluster connections KubeconfigSyncManager.getInstance(false)?.stopSync(); + cleanup(); if (blockQuit) { event.preventDefault(); // prevent app's default shutdown (e.g. required for telemetry, etc.) diff --git a/src/renderer/api/catalog-entity-registry.ts b/src/renderer/api/catalog-entity-registry.ts index abcf555c2b..63681106bd 100644 --- a/src/renderer/api/catalog-entity-registry.ts +++ b/src/renderer/api/catalog-entity-registry.ts @@ -20,7 +20,7 @@ */ import { computed, observable, makeObservable } from "mobx"; -import { broadcastMessage, subscribeToBroadcast } from "../../common/ipc"; +import { subscribeToBroadcast } from "../../common/ipc"; import { CatalogCategory, CatalogEntity, CatalogEntityData, catalogCategoryRegistry, CatalogCategoryRegistry, CatalogEntityKindData } from "../../common/catalog"; import "../../common/catalog-entities"; import { iter } from "../utils"; @@ -37,7 +37,6 @@ export class CatalogEntityRegistry { subscribeToBroadcast("catalog:items", (ev, items: (CatalogEntityData & CatalogEntityKindData)[]) => { this.rawItems.replace(items); }); - broadcastMessage("catalog:broadcast"); } set activeEntity(entity: CatalogEntity) { diff --git a/src/renderer/components/+catalog/catalog.route.ts b/src/renderer/components/+catalog/catalog.route.ts index 39ad958650..b7bbe8c993 100644 --- a/src/renderer/components/+catalog/catalog.route.ts +++ b/src/renderer/components/+catalog/catalog.route.ts @@ -22,8 +22,12 @@ import type { RouteProps } from "react-router"; import { buildURL } from "../../../common/utils/buildUrl"; +export interface ICatalogViewRouteParam { + group?: string; + kind?: string; +} export const catalogRoute: RouteProps = { - path: "/catalog" + path: "/catalog/:group?/:kind?" }; -export const catalogURL = buildURL(catalogRoute.path); +export const catalogURL = buildURL(catalogRoute.path); diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index 042e22aa70..6785807f83 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -23,7 +23,7 @@ import "./catalog.scss"; import React from "react"; import { disposeOnUnmount, observer } from "mobx-react"; import { ItemListLayout } from "../item-object-list"; -import { computed, makeObservable, observable, reaction } from "mobx"; +import { computed, makeObservable, observable, reaction, when } from "mobx"; import { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store"; import { navigate } from "../../navigation"; import { kebabCase } from "lodash"; @@ -37,6 +37,9 @@ import { ConfirmDialog } from "../confirm-dialog"; import { Tab, Tabs } from "../tabs"; import { catalogCategoryRegistry } from "../../../common/catalog"; import { CatalogAddButton } from "./catalog-add-button"; +import type { RouteComponentProps } from "react-router"; +import type { ICatalogViewRouteParam } from "./catalog.route"; +import { Notifications } from "../notifications"; enum sortBy { name = "name", @@ -44,8 +47,16 @@ enum sortBy { status = "status" } +interface Props extends RouteComponentProps { +} + @observer -export class Catalog extends React.Component { +export class Catalog extends React.Component { + constructor(props: Props) { + super(props); + makeObservable(this); + } + private catalogEntityStore = new CatalogEntityStore(); private contextMenu: CatalogEntityContextMenuContext = { @@ -67,9 +78,14 @@ export class Catalog extends React.Component { return this.catalogEntityStore.getEntities(this.activeCategory); } - constructor(props: {}) { - super(props); - makeObservable(this); + get routeActiveTab(): string | undefined { + const { group, kind } = this.props.match.params ?? {}; + + if (group && kind) { + return `${group}/${kind}`; + } + + return undefined; } componentDidMount() { @@ -77,7 +93,18 @@ export class Catalog extends React.Component { // autofill catalog entities into store reaction(() => this.items, items => this.catalogEntityStore.loadItems(items), { fireImmediately: true, - }) + }), + + when(() => catalogCategoryRegistry.items.length > 0, () => { + const item = catalogCategoryRegistry.items.find(i => i.getId() === this.routeActiveTab); + + if (item || this.routeActiveTab === undefined) { + this.activeTab = this.routeActiveTab; + this.catalogEntityStore.activeCategory = item; + } else { + Notifications.error(

Unknown category: {this.routeActiveTab}

); + } + }), ]); } diff --git a/src/renderer/components/hotbar/hotbar-icon.tsx b/src/renderer/components/hotbar/hotbar-icon.tsx index bdd00859c2..7c567ff681 100644 --- a/src/renderer/components/hotbar/hotbar-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-icon.tsx @@ -93,6 +93,10 @@ export const HotbarIcon = observer(({menuItems = [], ...props}: Props) => { }; const getIconString = () => { + if (!title) { + return "??"; + } + const [rawFirst, rawSecond, rawThird] = getNameParts(title); const splitter = new GraphemeSplitter(); const first = splitter.iterateGraphemes(rawFirst); @@ -108,7 +112,7 @@ export const HotbarIcon = observer(({menuItems = [], ...props}: Props) => { return (
- +
{ renderGrid() { return this.items.map((item, index) => { const entity = this.getEntity(item); + const disabledMenuItems: CatalogEntityContextMenu[] = [ + { + title: "Unpin from Hotbar", + onClick: () => this.removeItem(item.entity.uid) + } + ]; return ( @@ -157,6 +163,7 @@ export class HotbarMenu extends React.Component { uid={item.entity.uid} title={item.entity.name} source={item.entity.source} + menuItems={disabledMenuItems} disabled /> )} diff --git a/src/renderer/protocol-handler/app-handlers.ts b/src/renderer/protocol-handler/app-handlers.ts index 0e5e2f3ee6..bbd91ab19b 100644 --- a/src/renderer/protocol-handler/app-handlers.ts +++ b/src/renderer/protocol-handler/app-handlers.ts @@ -43,6 +43,13 @@ export function bindProtocolAddRouteHandlers() { .addInternalHandler("/landing", () => { navigate(catalogURL()); }) + .addInternalHandler("/landing/view/:group/:kind", ({ pathname: { group, kind } }) => { + navigate(catalogURL({ + params: { + group, kind + } + })); + }) .addInternalHandler("/cluster", () => { navigate(addClusterURL()); })