diff --git a/src/extensions/renderer-api/components.ts b/src/extensions/renderer-api/components.ts index aec9f2dae8..dc8c52053d 100644 --- a/src/extensions/renderer-api/components.ts +++ b/src/extensions/renderer-api/components.ts @@ -22,6 +22,11 @@ import notificationsStoreInjectable from "../../renderer/components/notification import podStoreInjectable from "../../renderer/components/+workloads-pods/store.injectable"; import getDetailsUrlInjectable from "../../renderer/components/kube-detail-params/get-details-url.injectable"; import showDetailsInjectable from "../../renderer/components/kube-detail-params/show-details.injectable"; +import showCheckedErrorNotificationInjectable from "../../renderer/components/notifications/show-checked-error.injectable"; +import showErrorNotificationInjectable from "../../renderer/components/notifications/show-error-notification.injectable"; +import showInfoNotificationInjectable from "../../renderer/components/notifications/show-info-notification.injectable"; +import showShortInfoNotificationInjectable from "../../renderer/components/notifications/show-short-info.injectable"; +import showSuccessNotificationInjectable from "../../renderer/components/notifications/show-success-notification.injectable"; // layouts export * from "../../renderer/components/layout/main-layout"; @@ -67,7 +72,25 @@ export * from "../../renderer/components/drawer"; export * from "../../renderer/components/dialog"; export * from "../../renderer/components/line-progress"; export * from "../../renderer/components/menu"; -export * from "../../renderer/components/notifications"; + +export type { + CreateNotificationOptions, + Notification, + NotificationId, + NotificationMessage, + NotificationStatus, + ShowNotification, + NotificationsStore, +} from "../../renderer/components/notifications"; + +export const Notifications = { + ok: asLegacyGlobalFunctionForExtensionApi(showSuccessNotificationInjectable), + error: asLegacyGlobalFunctionForExtensionApi(showErrorNotificationInjectable), + checkedError: asLegacyGlobalFunctionForExtensionApi(showCheckedErrorNotificationInjectable), + info: asLegacyGlobalFunctionForExtensionApi(showInfoNotificationInjectable), + shortInfo: asLegacyGlobalFunctionForExtensionApi(showShortInfoNotificationInjectable), +}; + export * from "../../renderer/components/spinner"; export * from "../../renderer/components/stepper"; export * from "../../renderer/components/wizard"; diff --git a/src/renderer/components/+add-cluster/add-cluster.tsx b/src/renderer/components/+add-cluster/add-cluster.tsx index 5af1e13b48..f8929f2861 100644 --- a/src/renderer/components/+add-cluster/add-cluster.tsx +++ b/src/renderer/components/+add-cluster/add-cluster.tsx @@ -16,7 +16,7 @@ import { loadConfigFromString, splitConfig } from "../../../common/kube-helpers" import { docsUrl } from "../../../common/vars"; import { isDefined, iter } from "../../utils"; import { Button } from "../button"; -import { Notifications } from "../notifications"; +import type { ShowNotification } from "../notifications"; import { SettingLayout } from "../layout/setting-layout"; import { MonacoEditor } from "../monaco-editor"; import { withInjectables } from "@ogre-tools/injectable-react"; @@ -27,6 +27,8 @@ import type { EmitAppEvent } from "../../../common/app-event-bus/emit-event.inje import emitAppEventInjectable from "../../../common/app-event-bus/emit-event.injectable"; import type { GetDirnameOfPath } from "../../../common/path/get-dirname.injectable"; import getDirnameOfPathInjectable from "../../../common/path/get-dirname.injectable"; +import showSuccessNotificationInjectable from "../notifications/show-success-notification.injectable"; +import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable"; interface Option { config: KubeConfig; @@ -38,6 +40,8 @@ interface Dependencies { navigateToCatalog: NavigateToCatalog; getDirnameOfPath: GetDirnameOfPath; emitAppEvent: EmitAppEvent; + showSuccessNotification: ShowNotification; + showErrorNotification: ShowNotification; } function getContexts(config: KubeConfig): Map { @@ -97,11 +101,11 @@ class NonInjectedAddCluster extends React.Component { await fse.ensureDir(this.props.getDirnameOfPath(absPath)); await fse.writeFile(absPath, this.customConfig.trim(), { encoding: "utf-8", mode: 0o600 }); - Notifications.ok(`Successfully added ${this.kubeContexts.size} new cluster(s)`); + this.props.showSuccessNotification(`Successfully added ${this.kubeContexts.size} new cluster(s)`); return this.props.navigateToCatalog(); } catch (error) { - Notifications.error(`Failed to add clusters: ${error}`); + this.props.showErrorNotification(`Failed to add clusters: ${error}`); } }); @@ -163,5 +167,7 @@ export const AddCluster = withInjectables(NonInjectedAddCluster, { navigateToCatalog: di.inject(navigateToCatalogInjectable), getDirnameOfPath: di.inject(getDirnameOfPathInjectable), emitAppEvent: di.inject(emitAppEventInjectable), + showSuccessNotification: di.inject(showSuccessNotificationInjectable), + showErrorNotification: di.inject(showErrorNotificationInjectable), }), }); diff --git a/src/renderer/components/+config-resource-quotas/add-dialog/view.tsx b/src/renderer/components/+config-resource-quotas/add-dialog/view.tsx index b0197e9c1b..21ee1bde3c 100644 --- a/src/renderer/components/+config-resource-quotas/add-dialog/view.tsx +++ b/src/renderer/components/+config-resource-quotas/add-dialog/view.tsx @@ -18,13 +18,14 @@ import type { IResourceQuotaValues, ResourceQuotaApi } from "../../../../common/ import { Select } from "../../select"; import { Icon } from "../../icon"; import { Button } from "../../button"; -import { Notifications } from "../../notifications"; import { NamespaceSelect } from "../../+namespaces/namespace-select"; import { SubTitle } from "../../layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; import closeAddQuotaDialogInjectable from "./close.injectable"; import isAddQuotaDialogOpenInjectable from "./is-open.injectable"; import resourceQuotaApiInjectable from "../../../../common/k8s-api/endpoints/resource-quota.api.injectable"; +import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; export interface AddQuotaDialogProps extends DialogProps { } @@ -33,6 +34,7 @@ interface Dependencies { resourceQuotaApi: ResourceQuotaApi; isAddQuotaDialogOpen: IComputedValue; closeAddQuotaDialog: () => void; + showCheckedErrorNotification: ShowCheckedErrorNotification; } const defaultQuotas = JSON.stringify({ @@ -128,7 +130,7 @@ class NonInjectedAddQuotaDialog extends React.Component closeAddQuotaDialog: di.inject(closeAddQuotaDialogInjectable), isAddQuotaDialogOpen: di.inject(isAddQuotaDialogOpenInjectable), resourceQuotaApi: di.inject(resourceQuotaApiInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), }), }); diff --git a/src/renderer/components/+config-secrets/add-dialog/view.tsx b/src/renderer/components/+config-secrets/add-dialog/view.tsx index 48d277637c..cf68db93be 100644 --- a/src/renderer/components/+config-secrets/add-dialog/view.tsx +++ b/src/renderer/components/+config-secrets/add-dialog/view.tsx @@ -21,7 +21,6 @@ import { NamespaceSelect } from "../../+namespaces/namespace-select"; import { Select } from "../../select"; import { Icon } from "../../icon"; import { base64, iter } from "../../../utils"; -import { Notifications } from "../../notifications"; import upperFirst from "lodash/upperFirst"; import { fromEntries } from "../../../../common/utils/objects"; import type { ShowDetails } from "../../kube-detail-params/show-details.injectable"; @@ -30,6 +29,8 @@ import closeAddSecretDialogInjectable from "./close.injectable"; import secretApiInjectable from "../../../../common/k8s-api/endpoints/secret.api.injectable"; import showDetailsInjectable from "../../kube-detail-params/show-details.injectable"; import isAddSecretDialogOpenInjectable from "./is-open.injectable"; +import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; export interface AddSecretDialogProps extends Partial { } @@ -54,6 +55,7 @@ interface Dependencies { isAddSecretDialogOpen: IComputedValue; closeAddSecretDialog: () => void; showDetails: ShowDetails; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -116,7 +118,7 @@ class NonInjectedAddSecretDialog extends React.Component Promise; @@ -47,6 +51,8 @@ const attemptInstall = ({ createTempFilesAndValidate, getExtensionDestFolder, installStateStore, + showErrorNotification, + showInfoNotification, }: Dependencies): AttemptInstall => async (request, cleanup) => { const dispose = disposer( @@ -66,7 +72,7 @@ const attemptInstall = ({ if (curState !== ExtensionInstallationState.IDLE) { dispose(); - return void Notifications.error( + return void showErrorNotification(
Extension Install Collision:

@@ -86,7 +92,7 @@ const attemptInstall = ({ const { version: oldVersion } = installedExtension.manifest; // confirm to uninstall old version before installing new version - const removeNotification = Notifications.info( + const removeNotification = showInfoNotification(

@@ -142,6 +148,8 @@ const attemptInstallInjectable = getInjectable({ createTempFilesAndValidate: di.inject(createTempFilesAndValidateInjectable), getExtensionDestFolder: di.inject(getExtensionDestFolderInjectable), installStateStore: di.inject(extensionInstallationStateStoreInjectable), + showErrorNotification: di.inject(showErrorNotificationInjectable), + showInfoNotification: di.inject(showInfoNotificationInjectable), }), }); diff --git a/src/renderer/components/+extensions/uninstall-extension.injectable.tsx b/src/renderer/components/+extensions/uninstall-extension.injectable.tsx index 8da5e3090a..fa7cf89fb8 100644 --- a/src/renderer/components/+extensions/uninstall-extension.injectable.tsx +++ b/src/renderer/components/+extensions/uninstall-extension.injectable.tsx @@ -9,10 +9,11 @@ import extensionDiscoveryInjectable from "../../../extensions/extension-discover import loggerInjectable from "../../../common/logger.injectable"; import type { LensExtensionId } from "../../../extensions/lens-extension"; import { extensionDisplayName } from "../../../extensions/lens-extension"; -import { Notifications } from "../notifications"; import React from "react"; import { when } from "mobx"; import { getMessageFromError } from "./get-message-from-error/get-message-from-error"; +import showSuccessNotificationInjectable from "../notifications/show-success-notification.injectable"; +import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable"; const uninstallExtensionInjectable = getInjectable({ id: "uninstall-extension", @@ -22,6 +23,8 @@ const uninstallExtensionInjectable = getInjectable({ const extensionDiscovery = di.inject(extensionDiscoveryInjectable); const extensionInstallationStateStore = di.inject(extensionInstallationStateStoreInjectable); const logger = di.inject(loggerInjectable); + const showSuccessNotification = di.inject(showSuccessNotificationInjectable); + const showErrorNotification = di.inject(showErrorNotificationInjectable); return async (extensionId: LensExtensionId): Promise => { const ext = extensionLoader.getExtension(extensionId); @@ -44,7 +47,7 @@ const uninstallExtensionInjectable = getInjectable({ // wait for the ExtensionLoader to actually uninstall the extension await when(() => !extensionLoader.userExtensions.has(extensionId)); - Notifications.ok( + showSuccessNotification(

{"Extension "} {displayName} @@ -60,7 +63,7 @@ const uninstallExtensionInjectable = getInjectable({ `[EXTENSION-UNINSTALL]: uninstalling ${displayName} has failed: ${error}`, { error }, ); - Notifications.error( + showErrorNotification(

{"Uninstalling extension "} {displayName} diff --git a/src/renderer/components/+helm-releases/dialog/dialog.tsx b/src/renderer/components/+helm-releases/dialog/dialog.tsx index 9a7c3373d4..1fa6370cbc 100644 --- a/src/renderer/components/+helm-releases/dialog/dialog.tsx +++ b/src/renderer/components/+helm-releases/dialog/dialog.tsx @@ -14,7 +14,6 @@ import { Dialog } from "../../dialog"; import { Wizard, WizardStep } from "../../wizard"; import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api"; import { Select } from "../../select"; -import { Notifications } from "../../notifications"; import orderBy from "lodash/orderBy"; import { withInjectables } from "@ogre-tools/injectable-react"; import releaseRollbackDialogStateInjectable from "./state.injectable"; @@ -22,6 +21,8 @@ import type { RollbackRelease } from "../rollback-release/rollback-release.injec import rollbackReleaseInjectable from "../rollback-release/rollback-release.injectable"; import type { HelmReleaseRevision, RequestHelmReleaseHistory } from "../../../../common/k8s-api/endpoints/helm-releases.api/request-history.injectable"; import requestHelmReleaseHistoryInjectable from "../../../../common/k8s-api/endpoints/helm-releases.api/request-history.injectable"; +import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; export interface ReleaseRollbackDialogProps extends DialogProps { } @@ -30,6 +31,7 @@ interface Dependencies { rollbackRelease: RollbackRelease; state: IObservableValue; requestHelmReleaseHistory: RequestHelmReleaseHistory; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -71,7 +73,7 @@ class NonInjectedReleaseRollbackDialog extends React.Component; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -63,7 +65,7 @@ class NonInjectedAddNamespaceDialog extends React.Component void; openPortForward: OpenPortForward; + showErrorNotification: ShowNotification; } class NonInjectedPortForwardMenu extends React.Component { @@ -35,12 +37,12 @@ class NonInjectedPortForwardMenu { - const { portForward } = this.props; + const { portForward, showErrorNotification } = this.props; const pf = await this.portForwardStore.start(portForward); if (pf.status === "Disabled") { const { name, kind, forwardPort } = portForward; - Notifications.error(`Error occurred starting port-forward, the local port ${forwardPort} may not be available or the ${kind} ${name} may not be reachable`); + showErrorNotification(`Error occurred starting port-forward, the local port ${forwardPort} may not be available or the ${kind} ${name} may not be reachable`); } }; @@ -134,15 +136,12 @@ class NonInjectedPortForwardMenu( - NonInjectedPortForwardMenu, - - { - getProps: (di, props) => ({ - portForwardStore: di.inject(portForwardStoreInjectable), - openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open, - openPortForward: di.inject(openPortForwardInjectable), - ...props, - }), - }, -); +export const PortForwardMenu = withInjectables(NonInjectedPortForwardMenu, { + getProps: (di, props) => ({ + ...props, + portForwardStore: di.inject(portForwardStoreInjectable), + openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open, + openPortForward: di.inject(openPortForwardInjectable), + showErrorNotification: di.inject(showErrorNotificationInjectable), + }), +}); diff --git a/src/renderer/components/+network-services/service-port-component.tsx b/src/renderer/components/+network-services/service-port-component.tsx index e30112ce4f..b81ef1350d 100644 --- a/src/renderer/components/+network-services/service-port-component.tsx +++ b/src/renderer/components/+network-services/service-port-component.tsx @@ -10,7 +10,7 @@ import { disposeOnUnmount, observer } from "mobx-react"; import type { Service, ServicePort } from "../../../common/k8s-api/endpoints"; import { action, makeObservable, observable, reaction } from "mobx"; import { cssNames } from "../../utils"; -import { Notifications } from "../notifications"; +import type { ShowNotification } from "../notifications"; import { Button } from "../button"; import type { ForwardedPort, PortForwardStore } from "../../port-forward"; import { predictProtocol } from "../../port-forward"; @@ -24,6 +24,7 @@ import notifyErrorPortForwardingInjectable from "../../port-forward/notify-error import type { OpenPortForward } from "../../port-forward/open-port-forward.injectable"; import openPortForwardInjectable from "../../port-forward/open-port-forward.injectable"; import loggerInjectable from "../../../common/logger.injectable"; +import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable"; export interface ServicePortComponentProps { service: Service; @@ -37,6 +38,7 @@ interface Dependencies { aboutPortForwarding: () => void; notifyErrorPortForwarding: (message: string) => void; openPortForward: OpenPortForward; + showErrorNotification: ShowNotification; } @observer @@ -138,7 +140,7 @@ class NonInjectedServicePortComponent extends React.Component { } @@ -52,6 +53,7 @@ interface Dependencies { closeClusterRoleBindingDialog: CloseClusterRoleBindingDialog; openClusterRoleBindingDialog: OpenClusterRoleBindingDialog; showDetails: ShowDetails; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -167,7 +169,7 @@ class NonInjectedClusterRoleBindingDialog extends React.Component { } @@ -30,6 +31,7 @@ interface Dependencies { clusterRoleStore: ClusterRoleStore; showDetails: ShowDetails; closeAddClusterRoleDialog: () => void; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -40,6 +42,7 @@ class NonInjectedAddClusterRoleDialog extends React.Component { } @@ -51,6 +52,7 @@ interface Dependencies { clusterRoleStore: ClusterRoleStore; serviceAccountStore: ServiceAccountStore; roleApi: RoleApi; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -169,6 +171,7 @@ class NonInjectedRoleBindingDialog extends React.Component { } @@ -32,6 +33,7 @@ interface Dependencies { showDetails: ShowDetails; state: AddRoleDialogState; roleStore: RoleStore; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -42,6 +44,7 @@ class NonInjectedAddRoleDialog extends React.Component(N roleStore: di.inject(roleStoreInjectable), showDetails: di.inject(showDetailsInjectable), state: di.inject(addRoleDialogStateInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), }), }); diff --git a/src/renderer/components/+user-management/+service-accounts/create-dialog/view.tsx b/src/renderer/components/+user-management/+service-accounts/create-dialog/view.tsx index 42e7012e80..6b112b6ea9 100644 --- a/src/renderer/components/+user-management/+service-accounts/create-dialog/view.tsx +++ b/src/renderer/components/+user-management/+service-accounts/create-dialog/view.tsx @@ -14,7 +14,6 @@ import { Dialog } from "../../../dialog"; import { Input } from "../../../input"; import { systemName } from "../../../input/input_validators"; import { SubTitle } from "../../../layout/sub-title"; -import { Notifications } from "../../../notifications"; import { Wizard, WizardStep } from "../../../wizard"; import type { CreateServiceAccountDialogState } from "./state.injectable"; import type { ServiceAccountStore } from "../store"; @@ -24,6 +23,8 @@ import closeCreateServiceAccountDialogInjectable from "./close.injectable"; import serviceAccountStoreInjectable from "../store.injectable"; import showDetailsInjectable from "../../../kube-detail-params/show-details.injectable"; import createServiceAccountDialogStateInjectable from "./state.injectable"; +import type { ShowCheckedErrorNotification } from "../../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../../notifications/show-checked-error.injectable"; export interface CreateServiceAccountDialogProps extends Partial { } @@ -33,12 +34,19 @@ interface Dependencies { serviceAccountStore: ServiceAccountStore; closeCreateServiceAccountDialog: () => void; showDetails: ShowDetails; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer class NonInjectedCreateServiceAccountDialog extends React.Component { createAccount = async () => { - const { closeCreateServiceAccountDialog, serviceAccountStore, state, showDetails } = this.props; + const { + closeCreateServiceAccountDialog, + serviceAccountStore, + state, + showDetails, + showCheckedErrorNotification, + } = this.props; try { const serviceAccount = await serviceAccountStore.create({ @@ -49,7 +57,7 @@ class NonInjectedCreateServiceAccountDialog extends React.Component {} @@ -21,6 +22,7 @@ interface Dependencies { openConfirmDialog: OpenConfirmDialog; openCronJobTriggerDialog: OpenCronJobTriggerDialog; cronJobApi: CronJobApi; + showCheckedErrorNotification: ShowCheckedErrorNotification; } const NonInjectedCronJobMenu = ({ @@ -29,6 +31,7 @@ const NonInjectedCronJobMenu = ({ openConfirmDialog, openCronJobTriggerDialog, cronJobApi, + showCheckedErrorNotification, }: Dependencies & CronJobMenuProps) => ( <> openCronJobTriggerDialog(object)}> @@ -48,7 +51,7 @@ const NonInjectedCronJobMenu = ({ try { await cronJobApi.resume({ namespace: object.getNs(), name: object.getName() }); } catch (err) { - Notifications.checkedError(err, "Unknown error occured while resuming CronJob"); + showCheckedErrorNotification(err, "Unknown error occured while resuming CronJob"); } }, labelOk: `Resume`, @@ -76,7 +79,7 @@ const NonInjectedCronJobMenu = ({ try { await cronJobApi.suspend({ namespace: object.getNs(), name: object.getName() }); } catch (err) { - Notifications.checkedError(err, "Unknown error occured while suspending CronJob"); + showCheckedErrorNotification(err, "Unknown error occured while suspending CronJob"); } }, labelOk: `Suspend`, @@ -106,5 +109,6 @@ export const CronJobMenu = withInjectables(NonIn openConfirmDialog: di.inject(openConfirmDialogInjectable), openCronJobTriggerDialog: di.inject(openCronJobTriggerDialogInjectable), cronJobApi: di.inject(cronJobApiInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), }), }); diff --git a/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.tsx b/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.tsx index 36690ac368..48ff80dc28 100644 --- a/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.tsx +++ b/src/renderer/components/+workloads-cronjobs/trigger-dialog/view.tsx @@ -13,7 +13,7 @@ import type { DialogProps } from "../../dialog"; import { Dialog } from "../../dialog"; import { Wizard, WizardStep } from "../../wizard"; import type { CronJob, JobApi } from "../../../../common/k8s-api/endpoints"; -import { Notifications } from "../../notifications"; +import type { ShowNotification } from "../../notifications"; import { cssNames } from "../../../utils"; import { Input } from "../../input"; import { systemName, maxLength } from "../../input/input_validators"; @@ -21,6 +21,9 @@ import { withInjectables } from "@ogre-tools/injectable-react"; import closeCronJobTriggerDialogInjectable from "./close.injectable"; import jobApiInjectable from "../../../../common/k8s-api/endpoints/job.api.injectable"; import cronJobTriggerDialogStateInjectable from "./state.injectable"; +import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; +import showErrorNotificationInjectable from "../../notifications/show-error-notification.injectable"; export interface CronJobTriggerDialogProps extends Partial { } @@ -29,6 +32,8 @@ interface Dependencies { state: IObservableValue; jobApi: JobApi; closeCronJobTriggerDialog: () => void; + showCheckedErrorNotification: ShowCheckedErrorNotification; + showErrorNotification: ShowNotification; } @observer @@ -49,7 +54,9 @@ class NonInjectedCronJobTriggerDialog extends Component { if (!cronJob.spec.jobTemplate) { - return void Notifications.error(`CronJob ${cronJob.getName()} has no jobTemplate`); + this.props.showErrorNotification(`CronJob ${cronJob.getName()} has no jobTemplate`); + + return; } try { @@ -73,7 +80,7 @@ class NonInjectedCronJobTriggerDialog extends Component {} interface Dependencies { daemonsetApi: DaemonSetApi; openConfirmDialog: OpenConfirmDialog; + showCheckedErrorNotification: ShowCheckedErrorNotification; } const NonInjectedDaemonSetMenu = ({ @@ -25,6 +27,7 @@ const NonInjectedDaemonSetMenu = ({ object, toolbar, openConfirmDialog, + showCheckedErrorNotification, }: Dependencies & DaemonSetMenuProps) => ( <> (N ...props, daemonsetApi: di.inject(daemonSetApiInjectable), openConfirmDialog: di.inject(openConfirmDialogInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), }), }); diff --git a/src/renderer/components/+workloads-deployments/deployment-menu.tsx b/src/renderer/components/+workloads-deployments/deployment-menu.tsx index ce8b031ce9..a32cc0eb11 100644 --- a/src/renderer/components/+workloads-deployments/deployment-menu.tsx +++ b/src/renderer/components/+workloads-deployments/deployment-menu.tsx @@ -7,13 +7,14 @@ import type { KubeObjectMenuProps } from "../kube-object-menu"; import type { Deployment, DeploymentApi } from "../../../common/k8s-api/endpoints"; import { MenuItem } from "../menu"; import { Icon } from "../icon"; -import { Notifications } from "../notifications"; import type { OpenDeploymentScaleDialog } from "./scale/open.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import deploymentApiInjectable from "../../../common/k8s-api/endpoints/deployment.api.injectable"; import openDeploymentScaleDialogInjectable from "./scale/open.injectable"; import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable"; import openConfirmDialogInjectable from "../confirm-dialog/open.injectable"; +import type { ShowCheckedErrorNotification } from "../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../notifications/show-checked-error.injectable"; export interface DeploymentMenuProps extends KubeObjectMenuProps {} @@ -21,6 +22,7 @@ interface Dependencies { openDeploymentScaleDialog: OpenDeploymentScaleDialog; deploymentApi: DeploymentApi; openConfirmDialog: OpenConfirmDialog; + showCheckedErrorNotification: ShowCheckedErrorNotification; } const NonInjectedDeploymentMenu = ({ @@ -29,6 +31,7 @@ const NonInjectedDeploymentMenu = ({ openDeploymentScaleDialog, toolbar, openConfirmDialog, + showCheckedErrorNotification, }: Dependencies & DeploymentMenuProps) => ( <> openDeploymentScaleDialog(object)}> @@ -49,7 +52,7 @@ const NonInjectedDeploymentMenu = ({ name: object.getName(), }); } catch (err) { - Notifications.checkedError(err, "Unknown error occured while restarting deployment"); + showCheckedErrorNotification(err, "Unknown error occured while restarting deployment"); } }, labelOk: "Restart", @@ -78,5 +81,6 @@ export const DeploymentMenu = withInjectables deploymentApi: di.inject(deploymentApiInjectable), openDeploymentScaleDialog: di.inject(openDeploymentScaleDialogInjectable), openConfirmDialog: di.inject(openConfirmDialogInjectable), + showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable), }), }); diff --git a/src/renderer/components/+workloads-deployments/scale/dialog.tsx b/src/renderer/components/+workloads-deployments/scale/dialog.tsx index 00c953cf4c..d6c8b0fcaa 100644 --- a/src/renderer/components/+workloads-deployments/scale/dialog.tsx +++ b/src/renderer/components/+workloads-deployments/scale/dialog.tsx @@ -15,11 +15,12 @@ import { Wizard, WizardStep } from "../../wizard"; import type { Deployment, DeploymentApi } from "../../../../common/k8s-api/endpoints"; import { Icon } from "../../icon"; import { Slider } from "../../slider"; -import { Notifications } from "../../notifications"; import { cssNames } from "../../../utils"; import { withInjectables } from "@ogre-tools/injectable-react"; import deploymentApiInjectable from "../../../../common/k8s-api/endpoints/deployment.api.injectable"; import deploymentScaleDialogStateInjectable from "./dialog-state.injectable"; +import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable"; +import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable"; export interface DeploymentScaleDialogProps extends Partial { } @@ -27,6 +28,7 @@ export interface DeploymentScaleDialogProps extends Partial { interface Dependencies { deploymentApi: DeploymentApi; state: IObservableValue; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -82,7 +84,7 @@ class NonInjectedDeploymentScaleDialog extends Component void; notifyErrorPortForwarding: (message: string) => void; openPortForward: OpenPortForward; + showErrorNotification: ShowNotification; } @observer @@ -134,7 +136,7 @@ class NonInjectedPodContainerPort extends React.Component { } @@ -27,6 +28,7 @@ export interface ReplicaSetScaleDialogProps extends Partial { interface Dependencies { replicaSetApi: ReplicaSetApi; state: IObservableValue; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -82,7 +84,7 @@ class NonInjectedReplicaSetScaleDialog extends Component { } @@ -27,6 +28,7 @@ export interface StatefulSetScaleDialogProps extends Partial { interface Dependencies { statefulSetApi: StatefulSetApi; state: IObservableValue; + showCheckedErrorNotification: ShowCheckedErrorNotification; } @observer @@ -82,7 +84,7 @@ class NonInjectedStatefulSetScaleDialog extends Component { } @@ -39,6 +40,7 @@ export interface ConfirmDialogBooleanParams { interface Dependencies { state: IObservableValue; + showErrorNotification: ShowNotification; } const defaultParams = { @@ -68,7 +70,7 @@ class NonInjectedConfirmDialog extends React.Component this.params.ok())(); } catch (error) { - Notifications.error( + this.props.showErrorNotification( <>

Confirmation action failed:

@@ -96,7 +98,7 @@ class NonInjectedConfirmDialog extends React.Component

Cancelling action failed:

@@ -167,5 +169,6 @@ export const ConfirmDialog = withInjectables(N getProps: (di, props) => ({ ...props, state: di.inject(confirmDialogStateInjectable), + showErrorNotification: di.inject(showErrorNotificationInjectable), }), }); diff --git a/src/renderer/components/dialog/logs-dialog.tsx b/src/renderer/components/dialog/logs-dialog.tsx index e8feb79db1..cd1861db77 100644 --- a/src/renderer/components/dialog/logs-dialog.tsx +++ b/src/renderer/components/dialog/logs-dialog.tsx @@ -9,18 +9,31 @@ import React from "react"; import type { DialogProps } from "../dialog"; import { Dialog } from "../dialog"; import { Wizard, WizardStep } from "../wizard"; -import { Notifications } from "../notifications"; +import type { ShowNotification } from "../notifications"; import { Button } from "../button"; import { Icon } from "../icon"; import { clipboard } from "electron"; import { kebabCase } from "lodash/fp"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import showSuccessNotificationInjectable from "../notifications/show-success-notification.injectable"; export interface LogsDialogProps extends DialogProps { title: string; logs: string; } -export function LogsDialog({ title, logs, ...dialogProps }: LogsDialogProps) { +interface Dependencies { + showSuccessNotification: ShowNotification; +} + +const NonInjectedLogsDialog = (props: LogsDialogProps & Dependencies) => { + const { + title, + logs, + showSuccessNotification, + ...dialogProps + } = props; + return (

{ clipboard.writeText(logs); - Notifications.ok(`Logs copied to clipboard.`); + showSuccessNotification(`Logs copied to clipboard.`); }} > @@ -58,4 +71,11 @@ export function LogsDialog({ title, logs, ...dialogProps }: LogsDialogProps) { ); -} +}; + +export const LogsDialog = withInjectables(NonInjectedLogsDialog, { + getProps: (di, props) => ({ + ...props, + showSuccessNotification: di.inject(showSuccessNotificationInjectable), + }), +}); diff --git a/src/renderer/components/dock/terminal/send-command.injectable.ts b/src/renderer/components/dock/terminal/send-command.injectable.ts index 7580059daa..63902d34e1 100644 --- a/src/renderer/components/dock/terminal/send-command.injectable.ts +++ b/src/renderer/components/dock/terminal/send-command.injectable.ts @@ -4,22 +4,16 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { when } from "mobx"; +import loggerInjectable from "../../../../common/logger.injectable"; import { TerminalChannels } from "../../../../common/terminal/channels"; import { waitUntilDefined } from "../../../../common/utils/wait"; -import type { TerminalApi } from "../../../api/terminal-api"; import { noop } from "../../../utils"; -import { Notifications } from "../../notifications"; +import showSuccessNotificationInjectable from "../../notifications/show-success-notification.injectable"; import selectDockTabInjectable from "../dock/select-dock-tab.injectable"; -import type { DockTab, TabId } from "../dock/store"; +import type { TabId } from "../dock/store"; import createTerminalTabInjectable from "./create-terminal-tab.injectable"; import getTerminalApiInjectable from "./get-terminal-api.injectable"; -interface Dependencies { - selectTab: (tabId: TabId) => void; - createTerminalTab: () => DockTab; - getTerminalApi: (tabId: TabId) => TerminalApi | undefined; -} - export interface SendCommandOptions { /** * Emit an enter after the command @@ -37,59 +31,63 @@ export interface SendCommandOptions { tabId?: TabId; } -const sendCommand = ({ selectTab, createTerminalTab, getTerminalApi }: Dependencies) => async (command: string, options: SendCommandOptions = {}): Promise => { - let tabId: string | undefined = options.tabId; - - if (tabId) { - selectTab(tabId); - } else { - tabId = createTerminalTab().id; - } - - const terminalApi = await waitUntilDefined(() => ( - tabId - ? getTerminalApi(tabId) - : undefined - )); - const shellIsReady = when(() => terminalApi.isReady); - const notifyVeryLong = setTimeout(() => { - shellIsReady.cancel(); - Notifications.info( - "If terminal shell is not ready please check your shell init files, if applicable.", - { - timeout: 4_000, - }, - ); - }, 10_000); - - await shellIsReady.catch(noop); - clearTimeout(notifyVeryLong); - - if (terminalApi) { - if (options.enter) { - command += "\r"; - } - - terminalApi.sendMessage({ - type: TerminalChannels.STDIN, - data: command, - }); - } else { - console.warn( - "The selected tab is does not have a connection. Cannot send command.", - { tabId, command }, - ); - } -}; +export type SendCommand = (command: string, options?: SendCommandOptions) => Promise; const sendCommandInjectable = getInjectable({ id: "send-command", - instantiate: (di) => sendCommand({ - createTerminalTab: di.inject(createTerminalTabInjectable), - selectTab: di.inject(selectDockTabInjectable), - getTerminalApi: di.inject(getTerminalApiInjectable), - }), + instantiate: (di): SendCommand => { + const createTerminalTab = di.inject(createTerminalTabInjectable); + const selectTab = di.inject(selectDockTabInjectable); + const getTerminalApi = di.inject(getTerminalApiInjectable); + const showSuccessNotification = di.inject(showSuccessNotificationInjectable); + const logger = di.inject(loggerInjectable); + + return async (command: string, options: SendCommandOptions = {}): Promise => { + let tabId: string | undefined = options.tabId; + + if (tabId) { + selectTab(tabId); + } else { + tabId = createTerminalTab().id; + } + + const terminalApi = await waitUntilDefined(() => ( + tabId + ? getTerminalApi(tabId) + : undefined + )); + const shellIsReady = when(() => terminalApi.isReady); + const notifyVeryLong = setTimeout(() => { + shellIsReady.cancel(); + showSuccessNotification( + "If terminal shell is not ready please check your shell init files, if applicable.", + { + timeout: 4_000, + }, + ); + }, 10_000); + + await shellIsReady.catch(noop); + clearTimeout(notifyVeryLong); + + if (terminalApi) { + if (options.enter) { + command += "\r"; + } + + terminalApi.sendMessage({ + type: TerminalChannels.STDIN, + data: command, + }); + } else { + logger.warn( + "The selected tab is does not have a connection. Cannot send command.", + { tabId, command }, + ); + } + }; + }, }); export default sendCommandInjectable; diff --git a/src/renderer/components/notifications/notifications.tsx b/src/renderer/components/notifications/notifications.tsx index aa26a94b18..b501968036 100644 --- a/src/renderer/components/notifications/notifications.tsx +++ b/src/renderer/components/notifications/notifications.tsx @@ -16,13 +16,6 @@ import { Animate } from "../animate"; import { Icon } from "../icon"; import { withInjectables } from "@ogre-tools/injectable-react"; import notificationsStoreInjectable from "./notifications-store.injectable"; -import { asLegacyGlobalFunctionForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; -import showSuccessNotificationInjectable from "./show-success-notification.injectable"; -import type { ShowCheckedErrorNotification } from "./show-checked-error.injectable"; -import showCheckedErrorNotificationInjectable from "./show-checked-error.injectable"; -import showErrorNotificationInjectable from "./show-error-notification.injectable"; -import showInfoNotificationInjectable from "./show-info-notification.injectable"; -import showShortInfoNotificationInjectable from "./show-short-info.injectable"; export type ShowNotification = (message: NotificationMessage, opts?: CreateNotificationOptions) => Disposer; @@ -102,25 +95,8 @@ class NonInjectedNotifications extends React.Component { } } -export const Notifications = withInjectables( - NonInjectedNotifications, - - { - getProps: (di) => ({ - store: di.inject(notificationsStoreInjectable), - }), - }, -) as React.FC & { - ok: ShowNotification; - checkedError: ShowCheckedErrorNotification; - error: ShowNotification; - shortInfo: ShowNotification; - info: ShowNotification; -}; - -Notifications.ok = asLegacyGlobalFunctionForExtensionApi(showSuccessNotificationInjectable); -Notifications.error = asLegacyGlobalFunctionForExtensionApi(showErrorNotificationInjectable); -Notifications.checkedError = asLegacyGlobalFunctionForExtensionApi(showCheckedErrorNotificationInjectable); -Notifications.info = asLegacyGlobalFunctionForExtensionApi(showInfoNotificationInjectable); -Notifications.shortInfo = asLegacyGlobalFunctionForExtensionApi(showShortInfoNotificationInjectable); - +export const Notifications = withInjectables(NonInjectedNotifications, { + getProps: (di) => ({ + store: di.inject(notificationsStoreInjectable), + }), +}); diff --git a/src/renderer/initializers/add-sync-entries.injectable.tsx b/src/renderer/initializers/add-sync-entries.injectable.tsx index a11c01c84d..5190bac73a 100644 --- a/src/renderer/initializers/add-sync-entries.injectable.tsx +++ b/src/renderer/initializers/add-sync-entries.injectable.tsx @@ -2,13 +2,13 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { Notifications } from "../components/notifications"; import { getInjectable } from "@ogre-tools/injectable"; import userStoreInjectable from "../../common/user-store/user-store.injectable"; import React from "react"; import navigateToKubernetesPreferencesInjectable from "../../features/preferences/common/navigate-to-kubernetes-preferences.injectable"; import discoverAllKubeconfigSyncKindsInjectable from "../../features/preferences/renderer/preference-items/kubernetes/kubeconfig-sync/discover-all-sync-kinds.injectable"; import { action } from "mobx"; +import showSuccessNotificationInjectable from "../components/notifications/show-success-notification.injectable"; const addSyncEntriesInjectable = getInjectable({ id: "add-sync-entries", @@ -17,6 +17,7 @@ const addSyncEntriesInjectable = getInjectable({ const userStore = di.inject(userStoreInjectable); const navigateToKubernetesPreferences = di.inject(navigateToKubernetesPreferencesInjectable); const discoverAllKubeconfigSyncKinds = di.inject(discoverAllKubeconfigSyncKindsInjectable); + const showSuccessNotification = di.inject(showSuccessNotificationInjectable); return async (filePaths: string[]) => { const kinds = await discoverAllKubeconfigSyncKinds(filePaths); @@ -27,7 +28,7 @@ const addSyncEntriesInjectable = getInjectable({ } }); - Notifications.ok(( + showSuccessNotification((

Selected items has been added to Kubeconfig Sync.


diff --git a/src/renderer/ipc/list-namespaces-forbidden-handler.injectable.tsx b/src/renderer/ipc/list-namespaces-forbidden-handler.injectable.tsx index dda488df06..75fb51fe68 100644 --- a/src/renderer/ipc/list-namespaces-forbidden-handler.injectable.tsx +++ b/src/renderer/ipc/list-namespaces-forbidden-handler.injectable.tsx @@ -5,13 +5,15 @@ import { getInjectable } from "@ogre-tools/injectable"; import navigateToEntitySettingsInjectable from "../../common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable"; import type { ListNamespaceForbiddenArgs } from "../../common/ipc/cluster"; -import { Notifications } from "../components/notifications"; import { Button } from "../components/button"; import type { IpcRendererEvent } from "electron"; import React from "react"; import notificationsStoreInjectable from "../components/notifications/notifications-store.injectable"; import { getMillisecondsFromUnixEpoch } from "../../common/utils/date/get-current-date-time"; import getClusterByIdInjectable from "../../common/cluster-store/get-by-id.injectable"; +import showSuccessNotificationInjectable from "../components/notifications/show-success-notification.injectable"; + +const intervalBetweenNotifications = 1000 * 60; // 60s const listNamespacesForbiddenHandlerInjectable = getInjectable({ id: "list-namespaces-forbidden-handler", @@ -21,7 +23,7 @@ const listNamespacesForbiddenHandlerInjectable = getInjectable({ const notificationsStore = di.inject(notificationsStoreInjectable); const getClusterById = di.inject(getClusterByIdInjectable); const notificationLastDisplayedAt = new Map(); - const intervalBetweenNotifications = 1000 * 60; // 60s + const showSuccessNotification = di.inject(showSuccessNotificationInjectable); return ( event: IpcRendererEvent, @@ -47,7 +49,7 @@ const listNamespacesForbiddenHandlerInjectable = getInjectable({ return; } - Notifications.info( + showSuccessNotification( (
Add Accessible Namespaces diff --git a/src/renderer/ipc/register-ipc-listeners.injectable.ts b/src/renderer/ipc/register-ipc-listeners.injectable.ts index a37f983769..0c2d407d59 100644 --- a/src/renderer/ipc/register-ipc-listeners.injectable.ts +++ b/src/renderer/ipc/register-ipc-listeners.injectable.ts @@ -3,15 +3,28 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { registerIpcListeners } from "./register-listeners"; +import { defaultHotbarCells } from "../../common/hotbars/types"; +import { clusterListNamespaceForbiddenChannel } from "../../common/ipc/cluster"; +import { hotbarTooManyItemsChannel } from "../../common/ipc/hotbar"; +import showErrorNotificationInjectable from "../components/notifications/show-error-notification.injectable"; +import ipcRendererInjectable from "../utils/channel/ipc-renderer.injectable"; import listNamespacesForbiddenHandlerInjectable from "./list-namespaces-forbidden-handler.injectable"; const registerIpcListenersInjectable = getInjectable({ id: "register-ipc-listeners", - instantiate: (di) => registerIpcListeners({ - listNamespacesForbiddenHandler: di.inject(listNamespacesForbiddenHandlerInjectable), - }), + instantiate: (di) => { + const listNamespacesForbiddenHandler = di.inject(listNamespacesForbiddenHandlerInjectable); + const ipcRenderer = di.inject(ipcRendererInjectable); + const showErrorNotification = di.inject(showErrorNotificationInjectable); + + return () => { + ipcRenderer.on(clusterListNamespaceForbiddenChannel, listNamespacesForbiddenHandler); + ipcRenderer.on(hotbarTooManyItemsChannel, () => { + showErrorNotification(`Cannot have more than ${defaultHotbarCells} items pinned to a hotbar`); + }); + }; + }, }); export default registerIpcListenersInjectable; diff --git a/src/renderer/ipc/register-listeners.tsx b/src/renderer/ipc/register-listeners.tsx deleted file mode 100644 index c7b447c334..0000000000 --- a/src/renderer/ipc/register-listeners.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { IpcRendererEvent } from "electron"; -import { ipcRenderer } from "electron"; -import { Notifications } from "../components/notifications"; -import { defaultHotbarCells } from "../../common/hotbars/types"; -import { type ListNamespaceForbiddenArgs, clusterListNamespaceForbiddenChannel } from "../../common/ipc/cluster"; -import { hotbarTooManyItemsChannel } from "../../common/ipc/hotbar"; - -function HotbarTooManyItemsHandler(): void { - Notifications.error(`Cannot have more than ${defaultHotbarCells} items pinned to a hotbar`); -} - -interface Dependencies { - listNamespacesForbiddenHandler: ( - event: IpcRendererEvent, - ...[clusterId]: ListNamespaceForbiddenArgs - ) => void; -} - -export const registerIpcListeners = ({ listNamespacesForbiddenHandler }: Dependencies) => () => { - ipcRenderer.on(clusterListNamespaceForbiddenChannel, listNamespacesForbiddenHandler); - ipcRenderer.on(hotbarTooManyItemsChannel, HotbarTooManyItemsHandler); -}; diff --git a/src/renderer/port-forward/about-port-forwarding.injectable.ts b/src/renderer/port-forward/about-port-forwarding.injectable.ts deleted file mode 100644 index e04fc6a18c..0000000000 --- a/src/renderer/port-forward/about-port-forwarding.injectable.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { aboutPortForwarding } from "./port-forward-notify"; -import navigateToPortForwardsInjectable from "../../common/front-end-routing/routes/cluster/network/port-forwards/navigate-to-port-forwards.injectable"; -import hostedClusterIdInjectable from "../cluster-frame-context/hosted-cluster-id.injectable"; -import assert from "assert"; -import notificationsStoreInjectable from "../components/notifications/notifications-store.injectable"; - -const aboutPortForwardingInjectable = getInjectable({ - id: "about-port-forwarding", - - instantiate: (di) => { - const hostedClusterId = di.inject(hostedClusterIdInjectable); - const notificationsStore = di.inject(notificationsStoreInjectable); - const navigateToPortForwards = di.inject(navigateToPortForwardsInjectable); - - assert(hostedClusterId, "Only allowed to notify about port forward errors within a cluster frame"); - - return aboutPortForwarding({ - navigateToPortForwards, - hostedClusterId, - notificationsStore, - }); - }, -}); - -export default aboutPortForwardingInjectable; diff --git a/src/renderer/port-forward/about-port-forwarding.injectable.tsx b/src/renderer/port-forward/about-port-forwarding.injectable.tsx new file mode 100644 index 0000000000..4cec9bf4c5 --- /dev/null +++ b/src/renderer/port-forward/about-port-forwarding.injectable.tsx @@ -0,0 +1,48 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import React from "react"; +import navigateToPortForwardsInjectable from "../../common/front-end-routing/routes/cluster/network/port-forwards/navigate-to-port-forwards.injectable"; +import { Button } from "../components/button"; +import showSuccessNotificationInjectable from "../components/notifications/show-success-notification.injectable"; + +const aboutPortForwardingInjectable = getInjectable({ + id: "about-port-forwarding", + + instantiate: (di) => { + const showSuccessNotification = di.inject(showSuccessNotificationInjectable); + const navigateToPortForwards = di.inject(navigateToPortForwardsInjectable); + + return () => { + const removeNotification = showSuccessNotification( + ( +
+ Port Forwarding +

+ You can manage your port forwards on the Port Forwarding Page. +

+
+
+
+ ), + { + id: "port-forward-notification", + timeout: 10_000, + }, + ); + }; + }, +}); + +export default aboutPortForwardingInjectable; diff --git a/src/renderer/port-forward/notify-error-port-forwarding.injectable.ts b/src/renderer/port-forward/notify-error-port-forwarding.injectable.ts deleted file mode 100644 index 9ef9daf805..0000000000 --- a/src/renderer/port-forward/notify-error-port-forwarding.injectable.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { notifyErrorPortForwarding } from "./port-forward-notify"; -import navigateToPortForwardsInjectable from "../../common/front-end-routing/routes/cluster/network/port-forwards/navigate-to-port-forwards.injectable"; -import hostedClusterIdInjectable from "../cluster-frame-context/hosted-cluster-id.injectable"; -import assert from "assert"; -import notificationsStoreInjectable from "../components/notifications/notifications-store.injectable"; - -const notifyErrorPortForwardingInjectable = getInjectable({ - id: "notify-error-port-forwarding", - - instantiate: (di) => { - const hostedClusterId = di.inject(hostedClusterIdInjectable); - const notificationsStore = di.inject(notificationsStoreInjectable); - const navigateToPortForwards = di.inject(navigateToPortForwardsInjectable); - - assert(hostedClusterId, "Only allowed to notify about port forward errors within a cluster frame"); - - return notifyErrorPortForwarding({ - navigateToPortForwards, - hostedClusterId, - notificationsStore, - }); - }, -}); - -export default notifyErrorPortForwardingInjectable; diff --git a/src/renderer/port-forward/notify-error-port-forwarding.injectable.tsx b/src/renderer/port-forward/notify-error-port-forwarding.injectable.tsx new file mode 100644 index 0000000000..af2b7577e5 --- /dev/null +++ b/src/renderer/port-forward/notify-error-port-forwarding.injectable.tsx @@ -0,0 +1,47 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import navigateToPortForwardsInjectable from "../../common/front-end-routing/routes/cluster/network/port-forwards/navigate-to-port-forwards.injectable"; +import showErrorNotificationInjectable from "../components/notifications/show-error-notification.injectable"; +import React from "react"; +import { Button } from "../components/button"; + +const notifyErrorPortForwardingInjectable = getInjectable({ + id: "notify-error-port-forwarding", + + instantiate: (di) => { + const showErrorNotification = di.inject(showErrorNotificationInjectable); + const navigateToPortForwards = di.inject(navigateToPortForwardsInjectable); + + return (msg: string) => { + const removeNotification = showErrorNotification( + ( +
+ Port Forwarding +

+ {msg} +

+
+
+
+ ), + { + timeout: 10_000, + }, + ); + }; + }, +}); + +export default notifyErrorPortForwardingInjectable; diff --git a/src/renderer/port-forward/port-forward-notify.tsx b/src/renderer/port-forward/port-forward-notify.tsx deleted file mode 100644 index ed768ca13e..0000000000 --- a/src/renderer/port-forward/port-forward-notify.tsx +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import React from "react"; -import { Button } from "../components/button"; -import { Notifications } from "../components/notifications"; -import type { NavigateToPortForwards } from "../../common/front-end-routing/routes/cluster/network/port-forwards/navigate-to-port-forwards.injectable"; -import type { NotificationsStore } from "../components/notifications/notifications.store"; - -interface AboutPortForwardingDependencies { - navigateToPortForwards: NavigateToPortForwards; - hostedClusterId: string; - notificationsStore: NotificationsStore; -} - -export const aboutPortForwarding = ({ - navigateToPortForwards, - hostedClusterId, - notificationsStore, -}: AboutPortForwardingDependencies) => () => { - const notificationId = `port-forward-notification-${hostedClusterId}`; - - Notifications.info( - ( -
- Port Forwarding -

- You can manage your port forwards on the Port Forwarding Page. -

-
-
-
- ), - { - id: notificationId, - timeout: 10_000, - }, - ); -}; - -interface NotifyErrorPortForwardingDependencies { - navigateToPortForwards: NavigateToPortForwards; - hostedClusterId: string; - notificationsStore: NotificationsStore; -} - - -export const notifyErrorPortForwarding = ({ - navigateToPortForwards, - hostedClusterId, - notificationsStore, -}: NotifyErrorPortForwardingDependencies) => (msg: string) => { - const notificationId = `port-forward-error-notification-${hostedClusterId}`; - - Notifications.error( - ( -
- Port Forwarding -

- {msg} -

-
-
-
- ), - { - id: notificationId, - timeout: 10_000, - }, - ); -}; - diff --git a/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.injectable.ts b/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.injectable.ts index aaffe8dc32..1d378df375 100644 --- a/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.injectable.ts +++ b/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.injectable.ts @@ -16,6 +16,7 @@ import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.i // TODO: Importing from features is not OK. Make protocol-router to comply with Open Closed Principle to allow moving implementation under a feature import navigateToPreferencesInjectable from "../../../features/preferences/common/navigate-to-preferences.injectable"; import getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable"; +import showShortInfoNotificationInjectable from "../../components/notifications/show-short-info.injectable"; const bindProtocolAddRouteHandlersInjectable = getInjectable({ id: "bind-protocol-add-route-handlers", @@ -31,6 +32,7 @@ const bindProtocolAddRouteHandlersInjectable = getInjectable({ navigateToPreferences: di.inject(navigateToPreferencesInjectable), entityRegistry: di.inject(catalogEntityRegistryInjectable), getClusterById: di.inject(getClusterByIdInjectable), + showShortInfoNotification: di.inject(showShortInfoNotificationInjectable), }), }); diff --git a/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.tsx b/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.tsx index 12a95a101d..4bcf207340 100644 --- a/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.tsx +++ b/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.tsx @@ -11,7 +11,7 @@ import { EXTENSION_PUBLISHER_MATCH, LensProtocolRouter, } from "../../../common/protocol-handler"; -import { Notifications } from "../../components/notifications"; +import type { ShowNotification } from "../../components/notifications"; import type { NavigateToCatalog } from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; import type { NavigateToEntitySettings } from "../../../common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable"; import type { NavigateToClusterView } from "../../../common/front-end-routing/routes/cluster-view/navigate-to-cluster-view.injectable"; @@ -19,7 +19,6 @@ import assert from "assert"; import type { AttemptInstallByInfo } from "../../components/+extensions/attempt-install-by-info.injectable"; import type { GetClusterById } from "../../../common/cluster-store/get-by-id.injectable"; -// TODO: make it so that the handlers are type safe and we don't need to do the asserts interface Dependencies { attemptInstallByInfo: AttemptInstallByInfo; lensProtocolRouterRenderer: LensProtocolRouterRenderer; @@ -31,6 +30,7 @@ interface Dependencies { navigateToPreferences: (tabId: string) => void; entityRegistry: CatalogEntityRegistry; getClusterById: GetClusterById; + showShortInfoNotification: ShowNotification; } export const bindProtocolAddRouteHandlers = ({ @@ -44,6 +44,7 @@ export const bindProtocolAddRouteHandlers = ({ navigateToPreferences, entityRegistry, getClusterById, + showShortInfoNotification, }: Dependencies) => () => { lensProtocolRouterRenderer .addInternalHandler("/preferences", ({ search: { highlight: tabId }}) => { @@ -53,7 +54,7 @@ export const bindProtocolAddRouteHandlers = ({ }) .addInternalHandler("/", ({ tail }) => { if (tail) { - Notifications.shortInfo( + showShortInfoNotification(

{"Unknown Action for "} @@ -83,7 +84,7 @@ export const bindProtocolAddRouteHandlers = ({ if (entity) { navigateToEntitySettings(entityId); } else { - Notifications.shortInfo( + showShortInfoNotification(

{"Unknown catalog entity "} {entityId} @@ -100,7 +101,7 @@ export const bindProtocolAddRouteHandlers = ({ if (cluster) { navigateToClusterView(clusterId); } else { - Notifications.shortInfo( + showShortInfoNotification(

{"Unknown catalog entity "} {clusterId} @@ -116,7 +117,7 @@ export const bindProtocolAddRouteHandlers = ({ if (cluster) { navigateToEntitySettings(clusterId); } else { - Notifications.shortInfo( + showShortInfoNotification(

{"Unknown catalog entity "} {clusterId} diff --git a/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.injectable.ts b/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.injectable.ts index a3d85e86db..35938b32fd 100644 --- a/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.injectable.ts +++ b/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.injectable.ts @@ -7,16 +7,19 @@ import extensionLoaderInjectable from "../../../extensions/extension-loader/exte import { LensProtocolRouterRenderer } from "./lens-protocol-router-renderer"; import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable"; import loggerInjectable from "../../../common/logger.injectable"; +import showErrorNotificationInjectable from "../../components/notifications/show-error-notification.injectable"; +import showShortInfoNotificationInjectable from "../../components/notifications/show-short-info.injectable"; const lensProtocolRouterRendererInjectable = getInjectable({ id: "lens-protocol-router-renderer", - instantiate: (di) => - new LensProtocolRouterRenderer({ - extensionLoader: di.inject(extensionLoaderInjectable), - extensionsStore: di.inject(extensionsStoreInjectable), - logger: di.inject(loggerInjectable), - }), + instantiate: (di) => new LensProtocolRouterRenderer({ + extensionLoader: di.inject(extensionLoaderInjectable), + extensionsStore: di.inject(extensionsStoreInjectable), + logger: di.inject(loggerInjectable), + showErrorNotification: di.inject(showErrorNotificationInjectable), + showShortInfoNotification: di.inject(showShortInfoNotificationInjectable), + }), }); export default lensProtocolRouterRendererInjectable; diff --git a/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.tsx b/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.tsx index 0875037134..3c9959aa87 100644 --- a/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.tsx +++ b/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.tsx @@ -9,12 +9,15 @@ import * as proto from "../../../common/protocol-handler"; import Url from "url-parse"; import type { LensProtocolRouterDependencies } from "../../../common/protocol-handler"; import { foldAttemptResults, ProtocolHandlerInvalid, RouteAttempt } from "../../../common/protocol-handler"; -import { Notifications } from "../../components/notifications"; +import type { ShowNotification } from "../../components/notifications"; -interface Dependencies extends LensProtocolRouterDependencies {} +interface Dependencies extends LensProtocolRouterDependencies { + showShortInfoNotification: ShowNotification; + showErrorNotification: ShowNotification; +} export class LensProtocolRouterRenderer extends proto.LensProtocolRouter { - constructor(protected dependencies: Dependencies) { + constructor(protected readonly dependencies: Dependencies) { super(dependencies); } @@ -26,7 +29,7 @@ export class LensProtocolRouterRenderer extends proto.LensProtocolRouter { const rendererAttempt = this._routeToInternal(new Url(rawUrl, true)); if (foldAttemptResults(mainAttemptResult, rendererAttempt) === RouteAttempt.MISSING) { - Notifications.shortInfo(( + this.dependencies.showShortInfoNotification((

{"Unknown action "} {rawUrl} @@ -40,7 +43,7 @@ export class LensProtocolRouterRenderer extends proto.LensProtocolRouter { switch (foldAttemptResults(mainAttemptResult, rendererAttempt)) { case RouteAttempt.MISSING: - Notifications.shortInfo(( + this.dependencies.showShortInfoNotification((

{"Unknown action "} {rawUrl} @@ -49,7 +52,7 @@ export class LensProtocolRouterRenderer extends proto.LensProtocolRouter { )); break; case RouteAttempt.MISSING_EXTENSION: - Notifications.shortInfo(( + this.dependencies.showShortInfoNotification((

{"Missing extension for action "} {rawUrl} @@ -60,7 +63,7 @@ export class LensProtocolRouterRenderer extends proto.LensProtocolRouter { } }); ipcRenderer.on(ProtocolHandlerInvalid, (event, error: string, rawUrl: string) => { - Notifications.error(( + this.dependencies.showErrorNotification(( <>

{"Failed to route "}