mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add auto-update notifications and confirmation (#1941)
* add auto-update notifications and confirmation * Show single update notification (#1985) * Moving notification icons to top (#1987) * Switch to EventEmitter (producer&consumer) model * Add `onCorrect` and `onceCorrect` to ipc module for typechecking ipc messages * move type enforced ipc methods to seperate file, add unit tests Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
741973dd29
commit
a61425124f
23 files changed
+434
-47
No files matched your search
@@ -8,7 +8,7 @@ import { workloadURL, workloadStores } from "../+workloads";
|
||||
import { namespaceStore } from "../+namespaces/namespace.store";
|
||||
import { NamespaceSelectFilter } from "../+namespaces/namespace-select";
|
||||
import { isAllowedResource, KubeResource } from "../../../common/rbac";
|
||||
import { ResourceNames } from "../../../renderer/utils/rbac";
|
||||
import { ResourceNames } from "../../utils/rbac";
|
||||
import { autobind } from "../../utils";
|
||||
|
||||
const resources: KubeResource[] = [
|
||||
|
||||
@@ -21,10 +21,16 @@
|
||||
&.primary {
|
||||
background: $buttonPrimaryBackground;
|
||||
}
|
||||
|
||||
&.accent {
|
||||
background: $buttonAccentBackground;
|
||||
}
|
||||
|
||||
&.light {
|
||||
background-color: $buttonLightBackground;
|
||||
color: #505050;
|
||||
}
|
||||
|
||||
&.plain {
|
||||
color: inherit;
|
||||
background: transparent;
|
||||
|
||||
@@ -8,6 +8,7 @@ export interface ButtonProps extends ButtonHTMLAttributes<any>, TooltipDecorator
|
||||
waiting?: boolean;
|
||||
primary?: boolean;
|
||||
accent?: boolean;
|
||||
light?: boolean;
|
||||
plain?: boolean;
|
||||
outlined?: boolean;
|
||||
hidden?: boolean;
|
||||
@@ -24,13 +25,16 @@ export class Button extends React.PureComponent<ButtonProps, {}> {
|
||||
private button: HTMLButtonElement;
|
||||
|
||||
render() {
|
||||
const { className, waiting, label, primary, accent, plain, hidden, active, big, round, outlined, tooltip, children, ...props } = this.props;
|
||||
const btnProps = props as Partial<ButtonProps>;
|
||||
const {
|
||||
className, waiting, label, primary, accent, plain, hidden, active, big,
|
||||
round, outlined, tooltip, light, children, ...props
|
||||
} = this.props;
|
||||
const btnProps: Partial<ButtonProps> = props;
|
||||
|
||||
if (hidden) return null;
|
||||
|
||||
btnProps.className = cssNames("Button", className, {
|
||||
waiting, primary, accent, plain, active, big, round, outlined
|
||||
waiting, primary, accent, plain, active, big, round, outlined, light,
|
||||
});
|
||||
|
||||
const btnContent: ReactNode = (
|
||||
|
||||
@@ -42,5 +42,9 @@
|
||||
box-shadow: 0 0 20px $boxShadow;
|
||||
}
|
||||
}
|
||||
|
||||
.close {
|
||||
margin-top: -2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -18,6 +18,7 @@ export interface Notification {
|
||||
message: NotificationMessage;
|
||||
status?: NotificationStatus;
|
||||
timeout?: number; // auto-hiding timeout in milliseconds, 0 = no hide
|
||||
onClose?(): void; // additonal logic on when the notification times out or is closed by the "x"
|
||||
}
|
||||
|
||||
@autobind()
|
||||
@@ -72,23 +72,26 @@ export class Notifications extends React.Component {
|
||||
return (
|
||||
<div className="Notifications flex column align-flex-end" ref={e => this.elem = e}>
|
||||
{notifications.map(notification => {
|
||||
const { id, status } = notification;
|
||||
const { id, status, onClose } = notification;
|
||||
const msgText = this.getMessage(notification);
|
||||
|
||||
return (
|
||||
<Animate key={id}>
|
||||
<div
|
||||
className={cssNames("notification flex align-center", status)}
|
||||
className={cssNames("notification flex", status)}
|
||||
onMouseLeave={() => addAutoHideTimer(id)}
|
||||
onMouseEnter={() => removeAutoHideTimer(id)}>
|
||||
<div className="box center">
|
||||
<div className="box">
|
||||
<Icon material="info_outline"/>
|
||||
</div>
|
||||
<div className="message box grow">{msgText}</div>
|
||||
<div className="box center">
|
||||
<div className="box">
|
||||
<Icon
|
||||
material="close" className="close"
|
||||
onClick={prevDefault(() => remove(id))}
|
||||
onClick={prevDefault(() => {
|
||||
remove(id);
|
||||
onClose?.();
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import React from "react";
|
||||
import { ipcRenderer, IpcRendererEvent } from "electron";
|
||||
import { areArgsUpdateAvailableFromMain, UpdateAvailableChannel, onCorrect, UpdateAvailableFromMain, BackchannelArg } from "../../common/ipc";
|
||||
import { Notifications, notificationsStore } from "../components/notifications";
|
||||
import { Button } from "../components/button";
|
||||
import { isMac } from "../../common/vars";
|
||||
import * as uuid from "uuid";
|
||||
|
||||
function sendToBackchannel(backchannel: string, notificationId: string, data: BackchannelArg): void {
|
||||
notificationsStore.remove(notificationId);
|
||||
ipcRenderer.send(backchannel, data);
|
||||
}
|
||||
|
||||
function RenderYesButtons(props: { backchannel: string, notificationId: string }) {
|
||||
if (isMac) {
|
||||
/**
|
||||
* auto-updater's "installOnQuit" is not applicable for macOS as per their docs.
|
||||
*
|
||||
* See: https://github.com/electron-userland/electron-builder/blob/master/packages/electron-updater/src/AppUpdater.ts#L27-L32
|
||||
*/
|
||||
return <Button light label="Yes" onClick={() => sendToBackchannel(props.backchannel, props.notificationId, { doUpdate: true, now: true })} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button light label="Yes, now" onClick={() => sendToBackchannel(props.backchannel, props.notificationId, { doUpdate: true, now: true })} />
|
||||
<Button active outlined label="Yes, later" onClick={() => sendToBackchannel(props.backchannel, props.notificationId, { doUpdate: true, now: false })} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function UpdateAvailableHandler(event: IpcRendererEvent, ...[backchannel, updateInfo]: UpdateAvailableFromMain): void {
|
||||
const notificationId = uuid.v4();
|
||||
|
||||
Notifications.info(
|
||||
(
|
||||
<>
|
||||
<b>Update Available</b>
|
||||
<p>Version {updateInfo.version} of Lens IDE is now available. Would you like to update?</p>
|
||||
<div className="flex gaps row align-left box grow">
|
||||
<RenderYesButtons backchannel={backchannel} notificationId={notificationId} />
|
||||
<Button active outlined label="No" onClick={() => sendToBackchannel(backchannel, notificationId, { doUpdate: false })} />
|
||||
</div>
|
||||
</>
|
||||
), {
|
||||
id: notificationId,
|
||||
onClose() {
|
||||
sendToBackchannel(backchannel, notificationId, { doUpdate: false });
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function registerIpcHandlers() {
|
||||
onCorrect({
|
||||
source: ipcRenderer,
|
||||
channel: UpdateAvailableChannel,
|
||||
listener: UpdateAvailableHandler,
|
||||
verifier: areArgsUpdateAvailableFromMain,
|
||||
});
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import { ConfirmDialog } from "./components/confirm-dialog";
|
||||
import { extensionLoader } from "../extensions/extension-loader";
|
||||
import { broadcastMessage } from "../common/ipc";
|
||||
import { CommandContainer } from "./components/command-palette/command-container";
|
||||
import { registerIpcHandlers } from "./ipc";
|
||||
|
||||
@observer
|
||||
export class LensApp extends React.Component {
|
||||
@@ -23,6 +24,8 @@ export class LensApp extends React.Component {
|
||||
window.addEventListener("online", () => {
|
||||
broadcastMessage("network:online");
|
||||
});
|
||||
|
||||
registerIpcHandlers();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"sidebarSubmenuActiveColor": "#ffffff",
|
||||
"buttonPrimaryBackground": "#3d90ce",
|
||||
"buttonDefaultBackground": "#414448",
|
||||
"buttonLightBackground": "#f1f1f1",
|
||||
"buttonAccentBackground": "#e85555",
|
||||
"buttonDisabledBackground": "#808080",
|
||||
"tableBgcStripe": "#2a2d33",
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"sidebarBackground": "#e8e8e8",
|
||||
"buttonPrimaryBackground": "#3d90ce",
|
||||
"buttonDefaultBackground": "#414448",
|
||||
"buttonLightBackground": "#f1f1f1",
|
||||
"buttonAccentBackground": "#e85555",
|
||||
"buttonDisabledBackground": "#808080",
|
||||
"tableBgcStripe": "#f8f8f8",
|
||||
|
||||
@@ -34,6 +34,7 @@ $sidebarBackground: var(--sidebarBackground);
|
||||
// Elements
|
||||
$buttonPrimaryBackground: var(--buttonPrimaryBackground);
|
||||
$buttonDefaultBackground: var(--buttonDefaultBackground);
|
||||
$buttonLightBackground: var(--buttonLightBackground);
|
||||
$buttonAccentBackground: var(--buttonAccentBackground);
|
||||
$buttonDisabledBackground: var(--buttonDisabledBackground);
|
||||
|
||||
@@ -131,4 +132,4 @@ $selectOptionHoveredColor: var(--selectOptionHoveredColor);
|
||||
$lineProgressBackground: var(--lineProgressBackground);
|
||||
$radioActiveBackground: var(--radioActiveBackground);
|
||||
$menuActiveBackground: var(--menuActiveBackground);
|
||||
$menuSelectedOptionBgc: var(--menuSelectedOptionBgc);
|
||||
$menuSelectedOptionBgc: var(--menuSelectedOptionBgc);
|
||||
Reference in new issue
Block a user