diff --git a/docs/extensions/capabilities/color-reference.md b/docs/extensions/capabilities/color-reference.md index 660e0fe067..6a38ba861c 100644 --- a/docs/extensions/capabilities/color-reference.md +++ b/docs/extensions/capabilities/color-reference.md @@ -43,6 +43,7 @@ You can use theme-based CSS Variables to style an extension according to the act ## Button Colors - `--buttonPrimaryBackground`: button background color for primary actions. - `--buttonDefaultBackground`: default button background color. +- `--buttonLightBackground`: light button background color. - `--buttonAccentBackground`: accent button background color. - `--buttonDisabledBackground`: disabled button background color. diff --git a/src/common/ipc/ipc.ts b/src/common/ipc/ipc.ts index be96531afc..3fb1bef672 100644 --- a/src/common/ipc/ipc.ts +++ b/src/common/ipc/ipc.ts @@ -11,7 +11,6 @@ import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames"; const subFramesChannel = "ipc:get-sub-frames"; export type HandlerEvent = Parameters[1]>[0]; -export type EventListener = (event: HandlerEvent, ...args: T) => any; export function handleRequest(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any) { ipcMain.handle(channel, listener); @@ -32,18 +31,23 @@ export function onceCorrect< EM extends EventEmitter, T extends any[], L extends (event: HandlerEvent, ...args: T) => any ->( +>({ + source, + channel, + listener, + verifier, +}: { source: EM, channel: string | symbol, listener: L, - verifier: (args: unknown[]) => args is T -): void { + verifier: (args: unknown[]) => args is T, +}): void { function handler(event: HandlerEvent, ...args: unknown[]): void { if (verifier(args)) { source.removeListener(channel, handler); // remove immediately - Promise.resolve(listener(event, ...args)) // might return a promise - .catch(error => logger.error("[IPC]: channel once handler threw error", { channel, error })); + (async () => (listener(event, ...args)))() // might return a promise, or throw, or reject + .catch((error: any) => logger.error("[IPC]: channel once handler threw error", { channel, error })); } else { logger.error("[IPC]: channel was sent to with invalid data", { channel, args }); } @@ -62,15 +66,20 @@ export function onCorrect< EM extends EventEmitter, T extends any[], L extends (event: HandlerEvent, ...args: T) => any ->( +>({ + source, + channel, + listener, + verifier, +}: { source: EM, channel: string | symbol, listener: L, - verifier: (args: unknown[]) => args is T -): void { + verifier: (args: unknown[]) => args is T, +}): void { source.on(channel, (event, ...args: unknown[]) => { if (verifier(args)) { - Promise.resolve(listener(event, ...args)) // might return a promise + (async () => (listener(event, ...args)))() // might return a promise, or throw, or reject .catch(error => logger.error("[IPC]: channel on handler threw error", { channel, error })); } else { logger.error("[IPC]: channel was sent to with invalid data", { channel, args }); diff --git a/src/main/app-updater.ts b/src/main/app-updater.ts index 94a4b39c1f..618f714b49 100644 --- a/src/main/app-updater.ts +++ b/src/main/app-updater.ts @@ -3,7 +3,6 @@ import logger from "./logger"; import { isDevelopment, isTestEnv } from "../common/vars"; import { delay } from "../common/utils"; import { areArgsUpdateAvailableToBackchannel, AutoUpdateLogPrefix, broadcastMessage, onceCorrect, UpdateAvailableChannel, UpdateAvailableToBackchannel } from "../common/ipc"; -import * as uuid from "uuid"; import { ipcMain } from "electron"; function handleAutoUpdateBackChannel(event: Electron.IpcMainEvent, ...[arg]: UpdateAvailableToBackchannel) { @@ -40,11 +39,17 @@ export function startUpdateChecking(interval = 1000 * 60 * 60 * 24): void { autoUpdater .on("update-available", (args: UpdateInfo) => { try { - // use a UUID so that this back-channel is harder to discover - const backchannel = uuid.v4(); + const backchannel = `auto-update:${args.version}`; + + ipcMain.removeAllListeners(backchannel); // only one handler should be present // make sure that the handler is in place before broadcasting (prevent race-condition) - onceCorrect(ipcMain, backchannel, handleAutoUpdateBackChannel, areArgsUpdateAvailableToBackchannel); + onceCorrect({ + source: ipcMain, + channel: backchannel, + listener: handleAutoUpdateBackChannel, + verifier: areArgsUpdateAvailableToBackchannel, + }); logger.info(`${AutoUpdateLogPrefix}: broadcasting update available`, { backchannel, version: args.version }); broadcastMessage(UpdateAvailableChannel, backchannel, args); } catch (error) { diff --git a/src/renderer/components/button/button.scss b/src/renderer/components/button/button.scss index fe26c04ee4..9224b7e5d3 100644 --- a/src/renderer/components/button/button.scss +++ b/src/renderer/components/button/button.scss @@ -27,7 +27,7 @@ } &.light { - background-color: white; + background-color: $buttonLightBackground; color: #505050; } @@ -51,7 +51,6 @@ &.outlined { color: inherit; background: transparent; - border: 1px solid; &.active, &:focus { diff --git a/src/renderer/ipc/index.tsx b/src/renderer/ipc/index.tsx index 4b942b65c3..13dce37a2d 100644 --- a/src/renderer/ipc/index.tsx +++ b/src/renderer/ipc/index.tsx @@ -6,52 +6,56 @@ import { Button, ButtonPannel } 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