mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Rework the image updating injectables to use the updater state
- Move cache up a level and add a new option to createTryIcon - Make useDarkColors and updateAvailable fully observable, computed, and injectable - Add locking to remove race conditions within async code Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
1fc2008b2f
commit
c5edceca67
104
src/common/utils/hash-map.ts
Normal file
104
src/common/utils/hash-map.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function makeIterableIterator<T>(iterator: Iterator<T>): IterableIterator<T> {
|
||||||
|
(iterator as IterableIterator<T>)[Symbol.iterator] = () => iterator as IterableIterator<T>;
|
||||||
|
|
||||||
|
return iterator as IterableIterator<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HashMap<K, V> implements Map<K, V> {
|
||||||
|
#hashmap: Map<string, { key: K; value: V }>;
|
||||||
|
|
||||||
|
constructor(protected hasher: (key: K) => string, initialValues?: Iterable<readonly [K, V]>) {
|
||||||
|
this.#hashmap = new Map();
|
||||||
|
|
||||||
|
if (initialValues) {
|
||||||
|
for (const [key, value] of initialValues) {
|
||||||
|
this.#hashmap.set(this.hasher(key), { key, value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): void {
|
||||||
|
this.#hashmap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(key: K): boolean {
|
||||||
|
return this.#hashmap.delete(this.hasher(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void {
|
||||||
|
this.#hashmap.forEach(entry => callbackfn(entry.value, entry.key, thisArg ?? this));
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key: K): V | undefined {
|
||||||
|
return this.#hashmap.get(this.hasher(key))?.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
has(key: K): boolean {
|
||||||
|
return this.#hashmap.has(this.hasher(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key: K, value: V): this {
|
||||||
|
this.#hashmap.set(this.hasher(key), { key, value });
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get size(): number {
|
||||||
|
return this.#hashmap.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries(): IterableIterator<[K, V]> {
|
||||||
|
let nextIndex = 0;
|
||||||
|
const keys = Array.from(this.keys());
|
||||||
|
const values = Array.from(this.values());
|
||||||
|
|
||||||
|
return makeIterableIterator<[K, V]>({
|
||||||
|
next() {
|
||||||
|
const index = nextIndex++;
|
||||||
|
|
||||||
|
return index < values.length
|
||||||
|
? { value: [keys[index], values[index]], done: false }
|
||||||
|
: { done: true, value: undefined };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
keys(): IterableIterator<K> {
|
||||||
|
let nextIndex = 0;
|
||||||
|
const observableValues = Array.from(this.#hashmap.values());
|
||||||
|
|
||||||
|
return makeIterableIterator<K>({
|
||||||
|
next: () => {
|
||||||
|
return nextIndex < observableValues.length
|
||||||
|
? { value: observableValues[nextIndex++].key, done: false }
|
||||||
|
: { done: true, value: undefined };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
values(): IterableIterator<V> {
|
||||||
|
let nextIndex = 0;
|
||||||
|
const observableValues = Array.from(this.#hashmap.values());
|
||||||
|
|
||||||
|
return makeIterableIterator<V>({
|
||||||
|
next: () => {
|
||||||
|
return nextIndex < observableValues.length
|
||||||
|
? { value: observableValues[nextIndex++].value, done: false }
|
||||||
|
: { done: true, value: undefined };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.iterator](): IterableIterator<[K, V]> {
|
||||||
|
return this.entries();
|
||||||
|
}
|
||||||
|
|
||||||
|
get [Symbol.toStringTag]() {
|
||||||
|
return "Map";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,7 +6,7 @@
|
|||||||
import type { IInterceptable, IInterceptor, IListenable, ISetWillChange, ObservableMap } from "mobx";
|
import type { IInterceptable, IInterceptor, IListenable, ISetWillChange, ObservableMap } from "mobx";
|
||||||
import { action, observable, ObservableSet } from "mobx";
|
import { action, observable, ObservableSet } from "mobx";
|
||||||
|
|
||||||
export function makeIterableIterator<T>(iterator: Iterator<T>): IterableIterator<T> {
|
function makeIterableIterator<T>(iterator: Iterator<T>): IterableIterator<T> {
|
||||||
(iterator as IterableIterator<T>)[Symbol.iterator] = () => iterator as IterableIterator<T>;
|
(iterator as IterableIterator<T>)[Symbol.iterator] = () => iterator as IterableIterator<T>;
|
||||||
|
|
||||||
return iterator as IterableIterator<T>;
|
return iterator as IterableIterator<T>;
|
||||||
|
|||||||
@ -26,6 +26,7 @@ export * from "./downloadFile";
|
|||||||
export * from "./escapeRegExp";
|
export * from "./escapeRegExp";
|
||||||
export * from "./formatDuration";
|
export * from "./formatDuration";
|
||||||
export * from "./getRandId";
|
export * from "./getRandId";
|
||||||
|
export * from "./hash-map";
|
||||||
export * from "./hash-set";
|
export * from "./hash-set";
|
||||||
export * from "./n-fircate";
|
export * from "./n-fircate";
|
||||||
export * from "./objects";
|
export * from "./objects";
|
||||||
|
|||||||
@ -14,6 +14,8 @@ import { once } from "lodash";
|
|||||||
import { ipcMain } from "electron";
|
import { ipcMain } from "electron";
|
||||||
import { nextUpdateChannel } from "./utils/update-channel";
|
import { nextUpdateChannel } from "./utils/update-channel";
|
||||||
import { UserStore } from "../common/user-store";
|
import { UserStore } from "../common/user-store";
|
||||||
|
import { Environments, getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||||
|
import appUpdaterStateInjectable from "./app-updater/state.injectable";
|
||||||
|
|
||||||
let installVersion: null | string = null;
|
let installVersion: null | string = null;
|
||||||
|
|
||||||
@ -52,6 +54,8 @@ export const startUpdateChecking = once(function (interval = 1000 * 60 * 60 * 24
|
|||||||
}
|
}
|
||||||
|
|
||||||
const userStore = UserStore.getInstance();
|
const userStore = UserStore.getInstance();
|
||||||
|
const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.main);
|
||||||
|
const state = di.inject(appUpdaterStateInjectable);
|
||||||
|
|
||||||
autoUpdater.autoDownload = false;
|
autoUpdater.autoDownload = false;
|
||||||
autoUpdater.autoInstallOnAppQuit = false;
|
autoUpdater.autoInstallOnAppQuit = false;
|
||||||
@ -94,6 +98,9 @@ export const startUpdateChecking = once(function (interval = 1000 * 60 * 60 * 24
|
|||||||
});
|
});
|
||||||
logger.info(`${AutoUpdateLogPrefix}: broadcasting update available`, { backchannel, version: info.version });
|
logger.info(`${AutoUpdateLogPrefix}: broadcasting update available`, { backchannel, version: info.version });
|
||||||
broadcastMessage(UpdateAvailableChannel, backchannel, info);
|
broadcastMessage(UpdateAvailableChannel, backchannel, info);
|
||||||
|
state.set({
|
||||||
|
status: "update-install-ready",
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`${AutoUpdateLogPrefix}: broadcasting failed`, { error });
|
logger.error(`${AutoUpdateLogPrefix}: broadcasting failed`, { error });
|
||||||
installVersion = undefined;
|
installVersion = undefined;
|
||||||
|
|||||||
21
src/main/app-updater/state.injectable.ts
Normal file
21
src/main/app-updater/state.injectable.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 { observable } from "mobx";
|
||||||
|
|
||||||
|
export type AppUpdaterState = {
|
||||||
|
status: "idle";
|
||||||
|
} | {
|
||||||
|
status: "update-install-ready";
|
||||||
|
};
|
||||||
|
|
||||||
|
const appUpdaterStateInjectable = getInjectable({
|
||||||
|
id: "app-updater-state",
|
||||||
|
instantiate: () => observable.box<AppUpdaterState>({
|
||||||
|
status: "idle",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default appUpdaterStateInjectable;
|
||||||
18
src/main/app-updater/update-available.injectable.ts
Normal file
18
src/main/app-updater/update-available.injectable.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* 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 { computed } from "mobx";
|
||||||
|
import appUpdaterStateInjectable from "./state.injectable";
|
||||||
|
|
||||||
|
const updateAvailableInjectable = getInjectable({
|
||||||
|
id: "update-available",
|
||||||
|
instantiate: (di) => {
|
||||||
|
const appUpdaterState = di.inject(appUpdaterStateInjectable);
|
||||||
|
|
||||||
|
return computed(() => appUpdaterState.get().status === "update-install-ready");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default updateAvailableInjectable;
|
||||||
21
src/main/electron/use-dark-colors.injectable.ts
Normal file
21
src/main/electron/use-dark-colors.injectable.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 { observable } from "mobx";
|
||||||
|
import nativeThemeInjectable from "./native-theme.injectable";
|
||||||
|
|
||||||
|
const useDarkColorsInjectable = getInjectable({
|
||||||
|
id: "use-dark-colors",
|
||||||
|
instantiate: (di) => {
|
||||||
|
const nativeTheme = di.inject(nativeThemeInjectable);
|
||||||
|
const state = observable.box(nativeTheme.shouldUseDarkColors);
|
||||||
|
|
||||||
|
nativeTheme.on("updated", () => state.set(nativeTheme.shouldUseDarkColors));
|
||||||
|
|
||||||
|
return state;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default useDarkColorsInjectable;
|
||||||
67
src/main/tray/computed-tray-icon.injectable.ts
Normal file
67
src/main/tray/computed-tray-icon.injectable.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* 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 AwaitLock from "await-lock";
|
||||||
|
import type { NativeImage, Tray } from "electron";
|
||||||
|
import { comparer, reaction } from "mobx";
|
||||||
|
import loggerInjectable from "../../common/logger.injectable";
|
||||||
|
import type { Disposer } from "../../common/utils";
|
||||||
|
import { getOrInsertWithAsync, HashMap } from "../../common/utils";
|
||||||
|
import updateAvailableInjectable from "../app-updater/update-available.injectable";
|
||||||
|
import useDarkColorsInjectable from "../electron/use-dark-colors.injectable";
|
||||||
|
import { createTrayIcon } from "./create-tray-icon";
|
||||||
|
|
||||||
|
export interface ComputedTrayIcon {
|
||||||
|
getCurrent(): Promise<NativeImage>;
|
||||||
|
subscribe(tray: Tray): Disposer;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NativeImageCacheKey {
|
||||||
|
updateAvailable: boolean;
|
||||||
|
useDarkColors: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const computedTrayIconInjectable = getInjectable({
|
||||||
|
id: "computed-tray-icon",
|
||||||
|
instantiate: (di): ComputedTrayIcon => {
|
||||||
|
const useDarkColors = di.inject(useDarkColorsInjectable);
|
||||||
|
const updateAvailable = di.inject(updateAvailableInjectable);
|
||||||
|
const logger = di.inject(loggerInjectable);
|
||||||
|
const lock = new AwaitLock();
|
||||||
|
const cache = new HashMap<NativeImageCacheKey, NativeImage>(
|
||||||
|
(key) => `${key.updateAvailable ? "updateAvailable" : ""}:${key.useDarkColors ? "shouldUseDarkColors" : ""}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const computedCurrent = (key: NativeImageCacheKey) => getOrInsertWithAsync(cache, key, () => createTrayIcon({
|
||||||
|
size: 16,
|
||||||
|
...key,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
getCurrent: () => computedCurrent({
|
||||||
|
updateAvailable: updateAvailable.get(),
|
||||||
|
useDarkColors: useDarkColors.get(),
|
||||||
|
}),
|
||||||
|
subscribe: (tray) => reaction(
|
||||||
|
() => ({
|
||||||
|
updateAvailable: updateAvailable.get(),
|
||||||
|
useDarkColors: useDarkColors.get(),
|
||||||
|
}),
|
||||||
|
(key) => {
|
||||||
|
lock.acquireAsync()
|
||||||
|
.then(() => computedCurrent(key))
|
||||||
|
.then(img => tray.setImage(img))
|
||||||
|
.catch((error) => logger.warn("[TRAY]: failed to update image after changing state", { key, error }))
|
||||||
|
.finally(() => lock.release());
|
||||||
|
},
|
||||||
|
{
|
||||||
|
equals: (a, b) => comparer.structural(a, b),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default computedTrayIconInjectable;
|
||||||
@ -1,22 +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 nativeThemeInjectable from "../electron/native-theme.injectable";
|
|
||||||
import { createTrayIcon } from "./create-tray-icon";
|
|
||||||
|
|
||||||
const createCurrentTrayIconInjectable = getInjectable({
|
|
||||||
id: "create-current-tray-icon",
|
|
||||||
instantiate: (di) => {
|
|
||||||
const nativeTheme = di.inject(nativeThemeInjectable);
|
|
||||||
|
|
||||||
return () => createTrayIcon({
|
|
||||||
shouldUseDarkColors: nativeTheme.shouldUseDarkColors,
|
|
||||||
size: 16,
|
|
||||||
updateIsAvailable: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default createCurrentTrayIconInjectable;
|
|
||||||
@ -4,24 +4,21 @@
|
|||||||
*/
|
*/
|
||||||
import { nativeImage } from "electron";
|
import { nativeImage } from "electron";
|
||||||
import type { NativeImage } from "electron";
|
import type { NativeImage } from "electron";
|
||||||
import { base64, getOrInsertWithAsync } from "../../common/utils";
|
import { base64 } from "../../common/utils";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { JSDOM } from "jsdom";
|
import { JSDOM } from "jsdom";
|
||||||
import LogoLens from "../../renderer/components/icon/logo-lens.svg";
|
import LogoLens from "../../renderer/components/icon/logo-lens.svg";
|
||||||
import Notice from "../../renderer/components/icon/notice.svg";
|
import Notice from "../../renderer/components/icon/notice.svg";
|
||||||
|
|
||||||
export interface CreateTrayIconArgs {
|
export interface CreateTrayIconArgs {
|
||||||
shouldUseDarkColors: boolean;
|
useDarkColors: boolean;
|
||||||
size: number;
|
size: number;
|
||||||
updateIsAvailable: boolean;
|
updateAvailable: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const trayIcons = new Map<boolean, NativeImage>();
|
export async function createTrayIcon({ useDarkColors, size, updateAvailable }: CreateTrayIconArgs): Promise<NativeImage> {
|
||||||
|
const trayIconColor = useDarkColors ? "white" : "black"; // Invert to show contrast
|
||||||
export async function createTrayIcon({ shouldUseDarkColors, size, updateIsAvailable }: CreateTrayIconArgs): Promise<NativeImage> {
|
const trayBackgroundColor = useDarkColors ? "black" : "white";
|
||||||
return getOrInsertWithAsync(trayIcons, shouldUseDarkColors, async () => {
|
|
||||||
const trayIconColor = shouldUseDarkColors ? "white" : "black"; // Invert to show contrast
|
|
||||||
const trayBackgroundColor = shouldUseDarkColors ? "black" : "white";
|
|
||||||
const styleTag = `
|
const styleTag = `
|
||||||
<style>
|
<style>
|
||||||
ellipse {
|
ellipse {
|
||||||
@ -41,7 +38,7 @@ export async function createTrayIcon({ shouldUseDarkColors, size, updateIsAvaila
|
|||||||
|
|
||||||
logoSvgRoot.innerHTML += styleTag;
|
logoSvgRoot.innerHTML += styleTag;
|
||||||
|
|
||||||
if (updateIsAvailable) {
|
if (updateAvailable) {
|
||||||
// This adds some contrast between the notice icon and the logo
|
// This adds some contrast between the notice icon and the logo
|
||||||
logoSvgRoot.innerHTML += `<ellipse ry="192" rx="192" cy="352" cx="352" />`;
|
logoSvgRoot.innerHTML += `<ellipse ry="192" rx="192" cy="352" cx="352" />`;
|
||||||
|
|
||||||
@ -71,5 +68,4 @@ export async function createTrayIcon({ shouldUseDarkColors, size, updateIsAvaila
|
|||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
|
||||||
return nativeImage.createFromBuffer(iconBuffer);
|
return nativeImage.createFromBuffer(iconBuffer);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,8 +19,7 @@ import { getInjectable } from "@ogre-tools/injectable";
|
|||||||
import windowManagerInjectable from "../window-manager.injectable";
|
import windowManagerInjectable from "../window-manager.injectable";
|
||||||
import navigateToPreferencesInjectable from "../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable";
|
import navigateToPreferencesInjectable from "../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable";
|
||||||
import trayMenuItemsInjectable from "./tray-menu-items.injectable";
|
import trayMenuItemsInjectable from "./tray-menu-items.injectable";
|
||||||
import createCurrentTrayIconInjectable from "./create-current-tray-icon.injectable";
|
import computedTrayIconInjectable from "./computed-tray-icon.injectable";
|
||||||
import trayIconUpdaterInjectable from "./tray-icon-updater.injectable";
|
|
||||||
|
|
||||||
const initTrayInjectable = getInjectable({
|
const initTrayInjectable = getInjectable({
|
||||||
id: "init-tray",
|
id: "init-tray",
|
||||||
@ -28,11 +27,10 @@ const initTrayInjectable = getInjectable({
|
|||||||
const windowManager = di.inject(windowManagerInjectable);
|
const windowManager = di.inject(windowManagerInjectable);
|
||||||
const trayMenuItems = di.inject(trayMenuItemsInjectable);
|
const trayMenuItems = di.inject(trayMenuItemsInjectable);
|
||||||
const navigateToPreferences = di.inject(navigateToPreferencesInjectable);
|
const navigateToPreferences = di.inject(navigateToPreferencesInjectable);
|
||||||
const createCurrentTrayIcon = di.inject(createCurrentTrayIconInjectable);
|
const computedTrayIcon = di.inject(computedTrayIconInjectable);
|
||||||
const trayIconUpdater = di.inject(trayIconUpdaterInjectable);
|
|
||||||
|
|
||||||
return async (): Promise<Disposer> => {
|
return async (): Promise<Disposer> => {
|
||||||
const tray = new Tray(await createCurrentTrayIcon());
|
const tray = new Tray(await computedTrayIcon.getCurrent());
|
||||||
|
|
||||||
tray.setToolTip(packageInfo.description);
|
tray.setToolTip(packageInfo.description);
|
||||||
tray.setIgnoreDoubleClickEvents(true);
|
tray.setIgnoreDoubleClickEvents(true);
|
||||||
@ -46,7 +44,7 @@ const initTrayInjectable = getInjectable({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return disposer(
|
return disposer(
|
||||||
trayIconUpdater(tray),
|
computedTrayIcon.subscribe(tray),
|
||||||
autorun(() => {
|
autorun(() => {
|
||||||
try {
|
try {
|
||||||
const menu = createTrayMenu(windowManager, toJS(trayMenuItems.get()), navigateToPreferences);
|
const menu = createTrayMenu(windowManager, toJS(trayMenuItems.get()), navigateToPreferences);
|
||||||
|
|||||||
@ -1,44 +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 type { Tray } from "electron";
|
|
||||||
import type { Disposer } from "../../common/utils";
|
|
||||||
import nativeThemeInjectable from "../electron/native-theme.injectable";
|
|
||||||
import createCurrentTrayIconInjectable from "./create-current-tray-icon.injectable";
|
|
||||||
|
|
||||||
export type TrayIconUpdater = (tray: Tray) => Disposer;
|
|
||||||
|
|
||||||
const trayIconUpdaterInjectable = getInjectable({
|
|
||||||
id: "tray-icon-updater",
|
|
||||||
instantiate: (di): TrayIconUpdater => {
|
|
||||||
const nativeTheme = di.inject(nativeThemeInjectable);
|
|
||||||
const createCurrentTrayIcon = di.inject(createCurrentTrayIconInjectable);
|
|
||||||
|
|
||||||
return (tray) => {
|
|
||||||
let prevShouldUseDarkColors = nativeTheme.shouldUseDarkColors;
|
|
||||||
const onUpdated = () => {
|
|
||||||
if (prevShouldUseDarkColors !== nativeTheme.shouldUseDarkColors) {
|
|
||||||
const localShouldUseDarkColors = prevShouldUseDarkColors = nativeTheme.shouldUseDarkColors;
|
|
||||||
|
|
||||||
createCurrentTrayIcon()
|
|
||||||
.then(img => {
|
|
||||||
// This guards against rapid changes back and forth
|
|
||||||
if (localShouldUseDarkColors === prevShouldUseDarkColors) {
|
|
||||||
tray.setImage(img);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
nativeTheme.on("updated", onUpdated);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
nativeTheme.off("updated", onUpdated);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default trayIconUpdaterInjectable;
|
|
||||||
Loading…
Reference in New Issue
Block a user