diff --git a/_locales/en/messages.json b/_locales/en/messages.json index bf80a8fba..f4251c653 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -87,6 +87,18 @@ "messageformat": "Apple silicon", "description": "Shown in the about box for Apple silicon product name" }, + "icu:About__AppEnvironment": { + "messageformat": "{appEnv}", + "description": "AboutWindow > App Environment Info > On non-apple devices" + }, + "icu:About__AppEnvironment--AppleSilicon": { + "messageformat": "{appEnv} (Apple silicon)", + "description": "AboutWindow > App Environment Info > On Apple Silicon Devices" + }, + "icu:About__AppEnvironment--AppleIntel": { + "messageformat": "{appEnv} (Intel)", + "description": "AboutWindow > App Environment Info > On Apple Intel Devices" + }, "icu:copyErrorAndQuit": { "messageformat": "Copy error and quit", "description": "Shown in the top-level error popup, allowing user to copy the error text and close the app" diff --git a/app/SystemTrayService.ts b/app/SystemTrayService.ts index db38ed985..4a60360b5 100644 --- a/app/SystemTrayService.ts +++ b/app/SystemTrayService.ts @@ -1,11 +1,14 @@ // Copyright 2017 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import { join } from 'path'; import type { BrowserWindow, NativeImage } from 'electron'; -import { Menu, Tray, app, nativeImage } from 'electron'; +import { Menu, Tray, app, nativeImage, nativeTheme, screen } from 'electron'; +import os from 'node:os'; +import { join } from 'node:path'; +import { readFileSync } from 'node:fs'; import * as log from '../ts/logging/log'; import type { LocalizerType } from '../ts/types/I18N'; +import { SystemThemeType } from '../ts/types/Util'; export type SystemTrayServiceOptionsType = Readonly<{ i18n: LocalizerType; @@ -43,6 +46,8 @@ export class SystemTrayService { this.i18n = i18n; this.boundRender = this.render.bind(this); this.createTrayInstance = createTrayInstance || (icon => new Tray(icon)); + + nativeTheme.on('updated', this.boundRender); } /** @@ -88,6 +93,7 @@ export class SystemTrayService { log.info(`System tray service: ${isEnabled ? 'enabling' : 'disabling'}`); this.isEnabled = isEnabled; + this.render(); } @@ -137,7 +143,7 @@ export class SystemTrayService { log.info('System tray service: rendering the tray'); - this.tray = this.tray || this.createTray(); + this.tray ??= this.createTray(); const { browserWindow, tray } = this; try { @@ -245,32 +251,117 @@ export class SystemTrayService { } } +const Variant = { + Size16: { size: 16, scaleFactor: 1 }, + Size32: { size: 32, scaleFactor: 2 }, + Size48: { size: 48, scaleFactor: 3 }, + Size256: { size: 256, scaleFactor: 16 }, +} as const; + +const Variants = Object.values(Variant); + +function getDisplaysMaxScaleFactor(): number { + const displays = screen.getAllDisplays(); + const scaleFactors = displays + .map(display => display.scaleFactor) + .filter(scaleFactor => Number.isFinite(scaleFactor) && scaleFactor > 1.0); + return Math.max(1.0, ...scaleFactors); +} + +function getVariantForScaleFactor(scaleFactor: number) { + const match = Variants.find(variant => { + return variant.scaleFactor >= scaleFactor; + }); + + return match ?? Variant.Size32; +} + +function getTrayIconImagePath( + size: number, + theme: SystemThemeType, + unreadCount: number +): string { + let dirName: string; + let fileName: string; + + if (unreadCount === 0) { + dirName = 'base'; + fileName = `signal-tray-icon-${size}x${size}-${theme}-base.png`; + } else if (unreadCount < 10) { + dirName = 'alert'; + fileName = `signal-tray-icon-${size}x${size}-${theme}-alert-${unreadCount}.png`; + } else { + dirName = 'alert'; + fileName = `signal-tray-icon-${size}x${size}-${theme}-alert-9+.png`; + } + + const iconPath = join( + __dirname, + '..', + 'images', + 'tray-icons', + dirName, + fileName + ); + + return iconPath; +} + +const TrayIconCache = new Map(); + function getIcon(unreadCount: number) { - let iconSize: string; - switch (process.platform) { - case 'darwin': - iconSize = '16'; - break; - case 'linux': - case 'win32': - iconSize = '32'; - break; - default: - iconSize = '256'; - break; + const theme = nativeTheme.shouldUseDarkColors + ? SystemThemeType.dark + : SystemThemeType.light; + + const cacheKey = `${theme}-${unreadCount}`; + + const cached = TrayIconCache.get(cacheKey); + if (cached != null) { + return cached; } - if (unreadCount > 0) { - const filename = `${String(unreadCount >= 10 ? 10 : unreadCount)}.png`; - return join(__dirname, '..', 'images', 'alert', iconSize, filename); + const platform = os.platform(); + + let image: NativeImage; + + if (platform === 'linux') { + // Linux: Static tray icons + // Use a single tray icon for Linux, as it does not support scale factors. + // We choose the best icon based on the highest display scale factor. + const scaleFactor = getDisplaysMaxScaleFactor(); + const variant = getVariantForScaleFactor(scaleFactor); + const iconPath = getTrayIconImagePath(variant.size, theme, unreadCount); + const buffer = readFileSync(iconPath); + image = nativeImage.createFromBuffer(buffer, { + scaleFactor: 1.0, // Must be 1.0 for Linux + width: variant.size, + height: variant.size, + }); + } else { + // Windows/macOS: Responsive tray icons + image = nativeImage.createEmpty(); + + for (const variant of Variants) { + const iconPath = getTrayIconImagePath(variant.size, theme, unreadCount); + const buffer = readFileSync(iconPath); + image.addRepresentation({ + buffer, + width: variant.size, + height: variant.size, + scaleFactor: variant.scaleFactor, + }); + } } - return join(__dirname, '..', 'images', `icon_${iconSize}.png`); + TrayIconCache.set(cacheKey, image); + + return image; } let defaultIcon: undefined | NativeImage; function getDefaultIcon(): NativeImage { - defaultIcon ??= nativeImage.createFromPath(getIcon(0)); + defaultIcon ??= getIcon(0); return defaultIcon; } diff --git a/background.html b/background.html index ba1ebbbc7..ab69604db 100644 --- a/background.html +++ b/background.html @@ -95,7 +95,9 @@
- +
diff --git a/build/icon.ico b/build/icon.ico index 924c2361c..03624a52a 100644 Binary files a/build/icon.ico and b/build/icon.ico differ diff --git a/build/icons/mac/icon.icns b/build/icons/mac/icon.icns index 9abce7d74..a33c9b708 100644 Binary files a/build/icons/mac/icon.icns and b/build/icons/mac/icon.icns differ diff --git a/build/icons/png/1024x1024.png b/build/icons/png/1024x1024.png index 118c7543a..29b335644 100644 Binary files a/build/icons/png/1024x1024.png and b/build/icons/png/1024x1024.png differ diff --git a/build/icons/png/128x128.png b/build/icons/png/128x128.png index 9cbf134c2..dc7aab72b 100644 Binary files a/build/icons/png/128x128.png and b/build/icons/png/128x128.png differ diff --git a/build/icons/png/16x16.png b/build/icons/png/16x16.png index d1abdaeeb..0de202f1c 100644 Binary files a/build/icons/png/16x16.png and b/build/icons/png/16x16.png differ diff --git a/build/icons/png/24x24.png b/build/icons/png/24x24.png index f4fcc8f6f..c38bc753d 100644 Binary files a/build/icons/png/24x24.png and b/build/icons/png/24x24.png differ diff --git a/build/icons/png/256x256.png b/build/icons/png/256x256.png index 9bfb4a0c1..8f2b1c16d 100644 Binary files a/build/icons/png/256x256.png and b/build/icons/png/256x256.png differ diff --git a/build/icons/png/32x32.png b/build/icons/png/32x32.png index 36fdce1d6..49649b2b1 100644 Binary files a/build/icons/png/32x32.png and b/build/icons/png/32x32.png differ diff --git a/build/icons/png/48x48.png b/build/icons/png/48x48.png index 94d08de22..1c6df2b40 100644 Binary files a/build/icons/png/48x48.png and b/build/icons/png/48x48.png differ diff --git a/build/icons/png/512x512.png b/build/icons/png/512x512.png index 8423cc06e..d92688ef0 100644 Binary files a/build/icons/png/512x512.png and b/build/icons/png/512x512.png differ diff --git a/build/icons/png/64x64.png b/build/icons/png/64x64.png index 5530e3a8c..83b91710f 100644 Binary files a/build/icons/png/64x64.png and b/build/icons/png/64x64.png differ diff --git a/build/icons/win/icon.ico b/build/icons/win/icon.ico index 924c2361c..1fd25e924 100644 Binary files a/build/icons/win/icon.ico and b/build/icons/win/icon.ico differ diff --git a/build/installerHeaderIcon.ico b/build/installerHeaderIcon.ico index 190d5e38d..1dd722902 100644 Binary files a/build/installerHeaderIcon.ico and b/build/installerHeaderIcon.ico differ diff --git a/build/installerIcon.ico b/build/installerIcon.ico index a322c5642..1dd722902 100644 Binary files a/build/installerIcon.ico and b/build/installerIcon.ico differ diff --git a/images/alert/16/1.png b/images/alert/16/1.png deleted file mode 100644 index c1913bfbf..000000000 Binary files a/images/alert/16/1.png and /dev/null differ diff --git a/images/alert/16/10.png b/images/alert/16/10.png deleted file mode 100644 index a07da7717..000000000 Binary files a/images/alert/16/10.png and /dev/null differ diff --git a/images/alert/16/2.png b/images/alert/16/2.png deleted file mode 100644 index 271a54331..000000000 Binary files a/images/alert/16/2.png and /dev/null differ diff --git a/images/alert/16/3.png b/images/alert/16/3.png deleted file mode 100644 index 5b62c2a7b..000000000 Binary files a/images/alert/16/3.png and /dev/null differ diff --git a/images/alert/16/4.png b/images/alert/16/4.png deleted file mode 100644 index ab1b7d16f..000000000 Binary files a/images/alert/16/4.png and /dev/null differ diff --git a/images/alert/16/5.png b/images/alert/16/5.png deleted file mode 100644 index c335dd166..000000000 Binary files a/images/alert/16/5.png and /dev/null differ diff --git a/images/alert/16/6.png b/images/alert/16/6.png deleted file mode 100644 index 7e9a8d716..000000000 Binary files a/images/alert/16/6.png and /dev/null differ diff --git a/images/alert/16/7.png b/images/alert/16/7.png deleted file mode 100644 index 9e8a3844d..000000000 Binary files a/images/alert/16/7.png and /dev/null differ diff --git a/images/alert/16/8.png b/images/alert/16/8.png deleted file mode 100644 index 4eeeed881..000000000 Binary files a/images/alert/16/8.png and /dev/null differ diff --git a/images/alert/16/9.png b/images/alert/16/9.png deleted file mode 100644 index 390f8c72b..000000000 Binary files a/images/alert/16/9.png and /dev/null differ diff --git a/images/alert/256/1.png b/images/alert/256/1.png deleted file mode 100644 index d85a9fd3a..000000000 Binary files a/images/alert/256/1.png and /dev/null differ diff --git a/images/alert/256/10.png b/images/alert/256/10.png deleted file mode 100644 index 7c19983e8..000000000 Binary files a/images/alert/256/10.png and /dev/null differ diff --git a/images/alert/256/2.png b/images/alert/256/2.png deleted file mode 100644 index 0ed1c2bab..000000000 Binary files a/images/alert/256/2.png and /dev/null differ diff --git a/images/alert/256/3.png b/images/alert/256/3.png deleted file mode 100644 index 87f529e65..000000000 Binary files a/images/alert/256/3.png and /dev/null differ diff --git a/images/alert/256/4.png b/images/alert/256/4.png deleted file mode 100644 index b187cde3d..000000000 Binary files a/images/alert/256/4.png and /dev/null differ diff --git a/images/alert/256/5.png b/images/alert/256/5.png deleted file mode 100644 index 89e7f23f0..000000000 Binary files a/images/alert/256/5.png and /dev/null differ diff --git a/images/alert/256/6.png b/images/alert/256/6.png deleted file mode 100644 index 9d8549ee2..000000000 Binary files a/images/alert/256/6.png and /dev/null differ diff --git a/images/alert/256/7.png b/images/alert/256/7.png deleted file mode 100644 index de122ac67..000000000 Binary files a/images/alert/256/7.png and /dev/null differ diff --git a/images/alert/256/8.png b/images/alert/256/8.png deleted file mode 100644 index bb8442da8..000000000 Binary files a/images/alert/256/8.png and /dev/null differ diff --git a/images/alert/256/9.png b/images/alert/256/9.png deleted file mode 100644 index f2fe9ec42..000000000 Binary files a/images/alert/256/9.png and /dev/null differ diff --git a/images/alert/32/1.png b/images/alert/32/1.png deleted file mode 100644 index a0de47e3c..000000000 Binary files a/images/alert/32/1.png and /dev/null differ diff --git a/images/alert/32/10.png b/images/alert/32/10.png deleted file mode 100644 index d165861ee..000000000 Binary files a/images/alert/32/10.png and /dev/null differ diff --git a/images/alert/32/2.png b/images/alert/32/2.png deleted file mode 100644 index 3fa07839d..000000000 Binary files a/images/alert/32/2.png and /dev/null differ diff --git a/images/alert/32/3.png b/images/alert/32/3.png deleted file mode 100644 index 5491b8e57..000000000 Binary files a/images/alert/32/3.png and /dev/null differ diff --git a/images/alert/32/4.png b/images/alert/32/4.png deleted file mode 100644 index aabe24f68..000000000 Binary files a/images/alert/32/4.png and /dev/null differ diff --git a/images/alert/32/5.png b/images/alert/32/5.png deleted file mode 100644 index 1e2320169..000000000 Binary files a/images/alert/32/5.png and /dev/null differ diff --git a/images/alert/32/6.png b/images/alert/32/6.png deleted file mode 100644 index 3dd3d8374..000000000 Binary files a/images/alert/32/6.png and /dev/null differ diff --git a/images/alert/32/7.png b/images/alert/32/7.png deleted file mode 100644 index 1cf5a5aec..000000000 Binary files a/images/alert/32/7.png and /dev/null differ diff --git a/images/alert/32/8.png b/images/alert/32/8.png deleted file mode 100644 index bad47c9c7..000000000 Binary files a/images/alert/32/8.png and /dev/null differ diff --git a/images/alert/32/9.png b/images/alert/32/9.png deleted file mode 100644 index d182aad82..000000000 Binary files a/images/alert/32/9.png and /dev/null differ diff --git a/images/icon_16.png b/images/icon_16.png deleted file mode 100644 index 35fcb8178..000000000 Binary files a/images/icon_16.png and /dev/null differ diff --git a/images/icon_256.png b/images/icon_256.png deleted file mode 100644 index d00abeb06..000000000 Binary files a/images/icon_256.png and /dev/null differ diff --git a/images/icon_32.png b/images/icon_32.png deleted file mode 100644 index 634bd2c92..000000000 Binary files a/images/icon_32.png and /dev/null differ diff --git a/images/logo-parts/base.svg b/images/logo-parts/base.svg index f1c126063..52168496f 100644 --- a/images/logo-parts/base.svg +++ b/images/logo-parts/base.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p1.svg b/images/logo-parts/p1.svg index efd7d1e9a..da1219f13 100644 --- a/images/logo-parts/p1.svg +++ b/images/logo-parts/p1.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p10.svg b/images/logo-parts/p10.svg index 2ed31901c..5ee9c82cc 100644 --- a/images/logo-parts/p10.svg +++ b/images/logo-parts/p10.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p11.svg b/images/logo-parts/p11.svg index a5046179f..46df46193 100644 --- a/images/logo-parts/p11.svg +++ b/images/logo-parts/p11.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p12.svg b/images/logo-parts/p12.svg index b6966d025..485bf1d2e 100644 --- a/images/logo-parts/p12.svg +++ b/images/logo-parts/p12.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p13.svg b/images/logo-parts/p13.svg index 0380fbd81..f6b7c2240 100644 --- a/images/logo-parts/p13.svg +++ b/images/logo-parts/p13.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p14.svg b/images/logo-parts/p14.svg index 44e5eda3b..173ce4db4 100644 --- a/images/logo-parts/p14.svg +++ b/images/logo-parts/p14.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p15.svg b/images/logo-parts/p15.svg index f2d3a9021..744319d47 100644 --- a/images/logo-parts/p15.svg +++ b/images/logo-parts/p15.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p16.svg b/images/logo-parts/p16.svg index 4185c38d2..c23828e6c 100644 --- a/images/logo-parts/p16.svg +++ b/images/logo-parts/p16.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p2.svg b/images/logo-parts/p2.svg index 84255e452..b915b8fcd 100644 --- a/images/logo-parts/p2.svg +++ b/images/logo-parts/p2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p3.svg b/images/logo-parts/p3.svg index eedbb66c1..295656f92 100644 --- a/images/logo-parts/p3.svg +++ b/images/logo-parts/p3.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p4.svg b/images/logo-parts/p4.svg index 93932c930..6d2f14e6d 100644 --- a/images/logo-parts/p4.svg +++ b/images/logo-parts/p4.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p5.svg b/images/logo-parts/p5.svg index f55f9ba1d..98403cd53 100644 --- a/images/logo-parts/p5.svg +++ b/images/logo-parts/p5.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p6.svg b/images/logo-parts/p6.svg index 1df6c5b30..aa5db34fb 100644 --- a/images/logo-parts/p6.svg +++ b/images/logo-parts/p6.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p7.svg b/images/logo-parts/p7.svg index a9b3d09e7..6cc75c66e 100644 --- a/images/logo-parts/p7.svg +++ b/images/logo-parts/p7.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p8.svg b/images/logo-parts/p8.svg index 8940b8ddd..253b9cc2b 100644 --- a/images/logo-parts/p8.svg +++ b/images/logo-parts/p8.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/logo-parts/p9.svg b/images/logo-parts/p9.svg index ffc18eff9..0fbbb0f5b 100644 --- a/images/logo-parts/p9.svg +++ b/images/logo-parts/p9.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/images/signal-logo.svg b/images/signal-logo.svg index ff0e2a07e..036d0696f 100644 --- a/images/signal-logo.svg +++ b/images/signal-logo.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-1.png b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-1.png new file mode 100644 index 000000000..097ab8bda Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-1.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-2.png b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-2.png new file mode 100644 index 000000000..62ea4a6c8 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-2.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-3.png b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-3.png new file mode 100644 index 000000000..c1738383b Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-3.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-4.png b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-4.png new file mode 100644 index 000000000..6019eefe3 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-4.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-5.png b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-5.png new file mode 100644 index 000000000..94ae43ec2 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-5.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-6.png b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-6.png new file mode 100644 index 000000000..020263f41 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-6.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-7.png b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-7.png new file mode 100644 index 000000000..3ae81daf1 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-7.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-8.png b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-8.png new file mode 100644 index 000000000..170275a70 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-8.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-9+.png b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-9+.png new file mode 100644 index 000000000..31c3a2be2 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-9+.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-9.png b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-9.png new file mode 100644 index 000000000..6fd82b88c Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-dark-alert-9.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-1.png b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-1.png new file mode 100644 index 000000000..da8275758 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-1.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-2.png b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-2.png new file mode 100644 index 000000000..96c9c8478 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-2.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-3.png b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-3.png new file mode 100644 index 000000000..0124d7a5d Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-3.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-4.png b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-4.png new file mode 100644 index 000000000..f1de8b308 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-4.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-5.png b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-5.png new file mode 100644 index 000000000..bfcdec182 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-5.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-6.png b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-6.png new file mode 100644 index 000000000..58a092fdc Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-6.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-7.png b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-7.png new file mode 100644 index 000000000..3b6457636 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-7.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-8.png b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-8.png new file mode 100644 index 000000000..52b26b08b Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-8.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-9+.png b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-9+.png new file mode 100644 index 000000000..6de775b2e Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-9+.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-9.png b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-9.png new file mode 100644 index 000000000..ce857845b Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-16x16-light-alert-9.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-1.png b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-1.png new file mode 100644 index 000000000..e20cc7f49 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-1.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-2.png b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-2.png new file mode 100644 index 000000000..9dc572498 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-2.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-3.png b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-3.png new file mode 100644 index 000000000..8e2e154e7 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-3.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-4.png b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-4.png new file mode 100644 index 000000000..737e95e83 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-4.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-5.png b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-5.png new file mode 100644 index 000000000..5c1aa6ebe Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-5.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-6.png b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-6.png new file mode 100644 index 000000000..b9dc763e3 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-6.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-7.png b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-7.png new file mode 100644 index 000000000..3a0c14546 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-7.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-8.png b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-8.png new file mode 100644 index 000000000..f6ec1c70d Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-8.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-9+.png b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-9+.png new file mode 100644 index 000000000..c1c724be8 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-9+.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-9.png b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-9.png new file mode 100644 index 000000000..7739bbf46 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-dark-alert-9.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-1.png b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-1.png new file mode 100644 index 000000000..eb9383e56 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-1.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-2.png b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-2.png new file mode 100644 index 000000000..66cacef66 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-2.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-3.png b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-3.png new file mode 100644 index 000000000..54d3d417b Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-3.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-4.png b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-4.png new file mode 100644 index 000000000..4801f9bde Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-4.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-5.png b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-5.png new file mode 100644 index 000000000..0cfe4de19 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-5.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-6.png b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-6.png new file mode 100644 index 000000000..ce83d02db Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-6.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-7.png b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-7.png new file mode 100644 index 000000000..0ff553dbd Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-7.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-8.png b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-8.png new file mode 100644 index 000000000..3149bd691 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-8.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-9+.png b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-9+.png new file mode 100644 index 000000000..9fc7c4d39 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-9+.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-9.png b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-9.png new file mode 100644 index 000000000..8bc39230e Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-256x256-light-alert-9.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-1.png b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-1.png new file mode 100644 index 000000000..7e5fed8c0 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-1.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-2.png b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-2.png new file mode 100644 index 000000000..94e13c9fb Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-2.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-3.png b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-3.png new file mode 100644 index 000000000..218fe72ce Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-3.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-4.png b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-4.png new file mode 100644 index 000000000..994918717 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-4.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-5.png b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-5.png new file mode 100644 index 000000000..0e801dae3 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-5.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-6.png b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-6.png new file mode 100644 index 000000000..7e9964c21 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-6.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-7.png b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-7.png new file mode 100644 index 000000000..0890ba653 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-7.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-8.png b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-8.png new file mode 100644 index 000000000..e0c729595 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-8.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-9+.png b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-9+.png new file mode 100644 index 000000000..a85d27cee Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-9+.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-9.png b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-9.png new file mode 100644 index 000000000..206faec1b Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-dark-alert-9.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-1.png b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-1.png new file mode 100644 index 000000000..54950c6e8 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-1.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-2.png b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-2.png new file mode 100644 index 000000000..e99ca3e76 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-2.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-3.png b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-3.png new file mode 100644 index 000000000..782ea967f Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-3.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-4.png b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-4.png new file mode 100644 index 000000000..e21495b08 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-4.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-5.png b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-5.png new file mode 100644 index 000000000..1dfa2a122 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-5.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-6.png b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-6.png new file mode 100644 index 000000000..2312bfb62 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-6.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-7.png b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-7.png new file mode 100644 index 000000000..8b12a24b0 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-7.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-8.png b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-8.png new file mode 100644 index 000000000..269b62b21 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-8.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-9+.png b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-9+.png new file mode 100644 index 000000000..e9ff89ef4 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-9+.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-9.png b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-9.png new file mode 100644 index 000000000..34b90e872 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-32x32-light-alert-9.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-1.png b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-1.png new file mode 100644 index 000000000..8f5372422 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-1.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-2.png b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-2.png new file mode 100644 index 000000000..a313afd04 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-2.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-3.png b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-3.png new file mode 100644 index 000000000..017daa419 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-3.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-4.png b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-4.png new file mode 100644 index 000000000..97bb35b04 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-4.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-5.png b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-5.png new file mode 100644 index 000000000..04ddb3edb Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-5.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-6.png b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-6.png new file mode 100644 index 000000000..1ac794873 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-6.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-7.png b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-7.png new file mode 100644 index 000000000..2c560c8b4 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-7.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-8.png b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-8.png new file mode 100644 index 000000000..2cbe8ff6b Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-8.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-9+.png b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-9+.png new file mode 100644 index 000000000..d257218cc Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-9+.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-9.png b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-9.png new file mode 100644 index 000000000..353a8f627 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-dark-alert-9.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-1.png b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-1.png new file mode 100644 index 000000000..a478fbf0b Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-1.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-2.png b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-2.png new file mode 100644 index 000000000..37278b99b Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-2.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-3.png b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-3.png new file mode 100644 index 000000000..6c89cd251 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-3.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-4.png b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-4.png new file mode 100644 index 000000000..d3d31b146 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-4.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-5.png b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-5.png new file mode 100644 index 000000000..3c17b095a Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-5.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-6.png b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-6.png new file mode 100644 index 000000000..87988cc08 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-6.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-7.png b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-7.png new file mode 100644 index 000000000..072fcc247 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-7.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-8.png b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-8.png new file mode 100644 index 000000000..934959ac8 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-8.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-9+.png b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-9+.png new file mode 100644 index 000000000..5aa59e392 Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-9+.png differ diff --git a/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-9.png b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-9.png new file mode 100644 index 000000000..cc5a59f8e Binary files /dev/null and b/images/tray-icons/alert/signal-tray-icon-48x48-light-alert-9.png differ diff --git a/images/tray-icons/base/signal-tray-icon-16x16-dark-base.png b/images/tray-icons/base/signal-tray-icon-16x16-dark-base.png new file mode 100644 index 000000000..7a4f014d3 Binary files /dev/null and b/images/tray-icons/base/signal-tray-icon-16x16-dark-base.png differ diff --git a/images/tray-icons/base/signal-tray-icon-16x16-light-base.png b/images/tray-icons/base/signal-tray-icon-16x16-light-base.png new file mode 100644 index 000000000..178d2f819 Binary files /dev/null and b/images/tray-icons/base/signal-tray-icon-16x16-light-base.png differ diff --git a/images/tray-icons/base/signal-tray-icon-256x256-dark-base.png b/images/tray-icons/base/signal-tray-icon-256x256-dark-base.png new file mode 100644 index 000000000..ecd766a8e Binary files /dev/null and b/images/tray-icons/base/signal-tray-icon-256x256-dark-base.png differ diff --git a/images/tray-icons/base/signal-tray-icon-256x256-light-base.png b/images/tray-icons/base/signal-tray-icon-256x256-light-base.png new file mode 100644 index 000000000..e9b31110f Binary files /dev/null and b/images/tray-icons/base/signal-tray-icon-256x256-light-base.png differ diff --git a/images/tray-icons/base/signal-tray-icon-32x32-dark-base.png b/images/tray-icons/base/signal-tray-icon-32x32-dark-base.png new file mode 100644 index 000000000..1ed18a4aa Binary files /dev/null and b/images/tray-icons/base/signal-tray-icon-32x32-dark-base.png differ diff --git a/images/tray-icons/base/signal-tray-icon-32x32-light-base.png b/images/tray-icons/base/signal-tray-icon-32x32-light-base.png new file mode 100644 index 000000000..e84cbd1ba Binary files /dev/null and b/images/tray-icons/base/signal-tray-icon-32x32-light-base.png differ diff --git a/images/tray-icons/base/signal-tray-icon-48x48-dark-base.png b/images/tray-icons/base/signal-tray-icon-48x48-dark-base.png new file mode 100644 index 000000000..c4e1dbea7 Binary files /dev/null and b/images/tray-icons/base/signal-tray-icon-48x48-dark-base.png differ diff --git a/images/tray-icons/base/signal-tray-icon-48x48-light-base.png b/images/tray-icons/base/signal-tray-icon-48x48-light-base.png new file mode 100644 index 000000000..94babc3af Binary files /dev/null and b/images/tray-icons/base/signal-tray-icon-48x48-light-base.png differ diff --git a/loading.html b/loading.html index 7aa2f18c2..7e0e0295e 100644 --- a/loading.html +++ b/loading.html @@ -25,7 +25,9 @@
- +
diff --git a/package-lock.json b/package-lock.json index 495799667..4b63484ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -126,6 +126,7 @@ "@indutny/parallel-prettier": "3.0.0", "@indutny/rezip-electron": "1.3.2", "@indutny/symbolicate-mac": "2.3.0", + "@napi-rs/canvas": "0.1.58", "@signalapp/mock-server": "9.0.2", "@storybook/addon-a11y": "8.1.11", "@storybook/addon-actions": "8.1.11", @@ -5121,6 +5122,180 @@ "node": ">=10" } }, + "node_modules/@napi-rs/canvas": { + "version": "0.1.58", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.58.tgz", + "integrity": "sha512-XDeVEFbXfiiXeB9816rlbSZyOysJPaQTKYG7u/wL3GQhg4YznZkjq8vEsNvK6C+bJx29OV8INu9bmfXPaE4Drw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.58", + "@napi-rs/canvas-darwin-arm64": "0.1.58", + "@napi-rs/canvas-darwin-x64": "0.1.58", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.58", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.58", + "@napi-rs/canvas-linux-arm64-musl": "0.1.58", + "@napi-rs/canvas-linux-x64-gnu": "0.1.58", + "@napi-rs/canvas-linux-x64-musl": "0.1.58", + "@napi-rs/canvas-win32-x64-msvc": "0.1.58" + } + }, + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.58", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.58.tgz", + "integrity": "sha512-0bwURPP2jNTRoZ++sLbUubYS8MQdoJrEp8T1Z5QNu6Msf2DZXYSyfNrzeA/I+3TfNmJ5r3kQueSxHA89e7CLUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.58", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.58.tgz", + "integrity": "sha512-3AetouQtGTDldvIV9t7iZ6eZlwnn1r32GLr6zxmJmjDFQs5Ey1LYC7hYRYzRHx2fgU/slvwfMhgQjlH+T+vrlw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.58", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.58.tgz", + "integrity": "sha512-JwF2yTqzlvBAon4/GvzQf5RpCpcy4vusi4GZA35B0UI66+6DJv0J83E+tCuMJgCywk9GqUNEH9ozXqZsSMc7hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.58", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.58.tgz", + "integrity": "sha512-I0perR7XYeUFCASZJ3eMoCJz690PpZcq+9/kItOfqzH2+EO2I+MTn194p2YPHENzC+kybTUUucl9jyQDzRuZUg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.58", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.58.tgz", + "integrity": "sha512-9U/T2N2aVOQ2OcIxWxOP3GXXuBZkNFtvZFVW+Uiov6qRiTeu3xjqrvAP+CSWhpVIW+GhR3rhlcYZY73rlVcJjQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.58", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.58.tgz", + "integrity": "sha512-CyPvv0CoZLn64t3zhgNy5Jd8QE5lKy+dcbkP/CT2XRNAJ8eulO1qr5gcKZBOhXkPFSHHjYcLHWuHK/6/KvggcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.58", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.58.tgz", + "integrity": "sha512-lt1KsWrdB0SJ1fhxGW1y1aLv+Xy+7/qQ/80oh+nbstqLqziMRUYkLOmsIKiMlqvv3zETa6VUPofi8dZmCbNUOQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.58", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.58.tgz", + "integrity": "sha512-YCQwtulayB1Y+lJi9p3vLGs5q9udRsx9cRDY1fnyPAQnsGO5Snub/MZltYgEKUDlUlJxAcnVJlMUKPWT/zIR+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.58", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.58.tgz", + "integrity": "sha512-qyRhuEqyoSQTBRHLKvShfjueqVWWtsG2U3yaxAE9Yhkg0sx6lunH7fPPrFcIKxbQ4k8pAbmWebhi9xPVf6mYPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@ndelangen/get-tarball": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/@ndelangen/get-tarball/-/get-tarball-3.0.9.tgz", diff --git a/package.json b/package.json index 1c863b6db..e91e242e7 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "build:dns-fallback": "node ts/scripts/generate-dns-fallback.js", "build:icu-types": "node ts/scripts/generate-icu-types.js", "build:compact-locales": "node ts/scripts/generate-compact-locales.js", + "build:tray-icons": "ts-node ts/scripts/generate-tray-icons.ts", "build:dev": "run-s --print-label generate build:esbuild:prod", "build:esbuild": "node scripts/esbuild.js", "build:esbuild:prod": "node scripts/esbuild.js --prod", @@ -212,6 +213,7 @@ "@indutny/parallel-prettier": "3.0.0", "@indutny/rezip-electron": "1.3.2", "@indutny/symbolicate-mac": "2.3.0", + "@napi-rs/canvas": "0.1.58", "@signalapp/mock-server": "9.0.2", "@storybook/addon-a11y": "8.1.11", "@storybook/addon-actions": "8.1.11", diff --git a/sticker-creator/src/colors.scss b/sticker-creator/src/colors.scss index 96733f959..cdad53f19 100644 --- a/sticker-creator/src/colors.scss +++ b/sticker-creator/src/colors.scss @@ -54,7 +54,7 @@ $color-black-alpha-90: rgba($color-black, 0.9); $color-transparent: rgba(0, 0, 0, 0); $color-ultramarine-dark: #1851b4; -$color-ultramarine-icon: #3a76f0; +$color-ultramarine-logo: #3b45fd; $color-ultramarine-light: #6191f3; $color-ultramarine-dawn: #406ec9; $color-ultramarine: #2c6bed; diff --git a/stylesheets/_global.scss b/stylesheets/_global.scss index 4f6f5943e..a9ac68078 100644 --- a/stylesheets/_global.scss +++ b/stylesheets/_global.scss @@ -280,12 +280,14 @@ $loading-height: 16px; * theme class. */ @include explicit-light-theme { - background-color: $color-ultramarine-icon; + background-color: $color-white; + color: $color-black-alpha-80; } @include dark-theme { background-color: $color-gray-95; + color: $color-white-alpha-80; } - color: $color-white; + display: flex; flex-direction: column; align-items: center; @@ -301,12 +303,18 @@ $loading-height: 16px; .dot { width: 14px; height: 14px; - border: 3px solid $color-white; + border: 3px solid; border-radius: 50%; float: inline-start; margin-block: 0; margin-inline: 6px; transform: scale(0); + @include light-theme { + border-color: $color-black; + } + @include dark-theme { + border-color: $color-white; + } animation: loading 1500ms ease infinite 0ms; &:nth-child(2) { @@ -321,8 +329,6 @@ $loading-height: 16px; &__progress { &--container { animation: fade-in 150ms ease 1 0ms; - - background: $color-white-alpha-20; border-radius: 2px; height: 4px; max-width: 400px; @@ -330,10 +336,15 @@ $loading-height: 16px; width: 100%; margin-block: 12px 26px; margin-inline: 0; + @include light-theme { + background: $color-black-alpha-20; + } + @include dark-theme { + background: $color-white-alpha-20; + } } &--bar { - background: $color-white; border-radius: 2px; display: block; height: 100%; @@ -347,6 +358,13 @@ $loading-height: 16px; transform: translateX(100%); } transition: transform 500ms linear; + + @include light-theme { + background: $color-black; + } + @include dark-theme { + background: $color-white; + } } } .message { diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index 72f203e08..5c56fe9a4 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -34,37 +34,24 @@ } .module-splash-screen__logo { - @include color-svg('../images/signal-logo.svg', $color-white); margin-block: 24px; margin-inline: 0; - &.module-img--256 { - height: 256px; - width: 256px; + @include light-theme { + @include color-svg('../images/signal-logo.svg', $color-ultramarine-logo); + } + @include dark-theme { + @include color-svg('../images/signal-logo.svg', $color-white); } - &.module-img--200 { - height: 200px; - width: 200px; - } - - &.module-img--150 { - height: 150px; - width: 150px; - } - - &.module-img--128 { + &.module-splash-screen__logo--128 { height: 128px; width: 128px; } - &.module-img--80 { - height: 80px; - width: 80px; - } - - &.module-logo-blue { - background-color: $color-ultramarine-icon; + &.module-splash-screen__logo--96 { + height: 96px; + width: 96px; } } diff --git a/stylesheets/_variables.scss b/stylesheets/_variables.scss index b177c04a7..34435682f 100644 --- a/stylesheets/_variables.scss +++ b/stylesheets/_variables.scss @@ -75,7 +75,7 @@ $color-black-alpha-90: rgba($color-black, 0.9); $color-transparent: rgba(0, 0, 0, 0); $color-ultramarine-dark: #1851b4; -$color-ultramarine-icon: #3a76f0; +$color-ultramarine-logo: #3b45fd; $color-ultramarine-light: #6191f3; $color-ultramarine-dawn: #406ec9; $color-ultramarine-pastel: #abc4f8; diff --git a/stylesheets/components/About.scss b/stylesheets/components/About.scss index fe73d8734..acfc4d6df 100644 --- a/stylesheets/components/About.scss +++ b/stylesheets/components/About.scss @@ -3,8 +3,7 @@ .About { align-items: center; - background-color: $color-ultramarine-icon; - color: $color-white; + display: flex; font-size: 14px; height: 100vh; @@ -12,11 +11,31 @@ overflow: hidden; text-align: center; + @include light-theme { + background-color: $color-white; + color: $color-black; + } + + @include dark-theme { + background-color: $color-gray-95; + color: $color-white-alpha-80; + } + img { margin-top: 1em; } a { - color: $color-white; + @include light-theme { + color: $color-ultramarine; + } + @include dark-theme { + color: $color-ultramarine-pastel; + } } } + +.About__Title { + @include font-body-1-bold; + margin: 0; +} diff --git a/stylesheets/components/Button.scss b/stylesheets/components/Button.scss index 27e38f799..e26824e1e 100644 --- a/stylesheets/components/Button.scss +++ b/stylesheets/components/Button.scss @@ -36,7 +36,7 @@ } @include dark-keyboard-mode { - @include focus-box-shadow($color-black, $color-ultramarine-icon); + @include focus-box-shadow($color-black, $color-ultramarine); } @include disabled { diff --git a/stylesheets/components/InstallScreenSignalLogo.scss b/stylesheets/components/InstallScreenSignalLogo.scss index 684fc5cad..bbe9deceb 100644 --- a/stylesheets/components/InstallScreenSignalLogo.scss +++ b/stylesheets/components/InstallScreenSignalLogo.scss @@ -11,7 +11,7 @@ inset-inline-start: 32px; &::before { - @include color-svg('../images/signal-logo.svg', $color-ultramarine); + @include color-svg('../images/signal-logo.svg', $color-ultramarine-logo); content: ''; display: block; height: 32px; diff --git a/stylesheets/components/TimelineWarning.scss b/stylesheets/components/TimelineWarning.scss index c71ae06d4..113387eb9 100644 --- a/stylesheets/components/TimelineWarning.scss +++ b/stylesheets/components/TimelineWarning.scss @@ -55,7 +55,7 @@ text-decoration: none; @include light-theme { - color: $color-ultramarine-icon; + color: $color-ultramarine; } @include dark-theme { color: $color-ios-blue-tint; diff --git a/ts/components/About.stories.tsx b/ts/components/About.stories.tsx new file mode 100644 index 000000000..36330070e --- /dev/null +++ b/ts/components/About.stories.tsx @@ -0,0 +1,36 @@ +// Copyright 2024 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import React from 'react'; +import { action } from '@storybook/addon-actions'; +import { setupI18n } from '../util/setupI18n'; +import enMessages from '../../_locales/en/messages.json'; +import type { ComponentMeta } from '../storybook/types'; +import type { AboutProps } from './About'; +import { About } from './About'; + +const i18n = setupI18n('en', enMessages); + +export default { + title: 'Components/About', + component: About, + parameters: { + layout: 'fullscreen', + }, + args: { + i18n, + closeAbout: action('showWhatsNewModal'), + appEnv: 'production', + platform: 'darwin', + arch: 'arm64', + version: '1.2.3', + }, +} satisfies ComponentMeta; + +export function Basic(args: AboutProps): JSX.Element { + return ( +
+ +
+ ); +} diff --git a/ts/components/About.tsx b/ts/components/About.tsx index bfcad0f58..24dabfcbb 100644 --- a/ts/components/About.tsx +++ b/ts/components/About.tsx @@ -6,28 +6,46 @@ import React from 'react'; import type { LocalizerType } from '../types/Util'; import { useEscapeHandling } from '../hooks/useEscapeHandling'; -export type PropsType = { +export type AboutProps = Readonly<{ closeAbout: () => unknown; - environment: string; + appEnv: string; + arch: string; + platform: string; i18n: LocalizerType; version: string; -}; +}>; export function About({ closeAbout, - environment, + appEnv, + arch, + platform, i18n, version, -}: PropsType): JSX.Element { +}: AboutProps): JSX.Element { useEscapeHandling(closeAbout); + let env: string; + + if (platform === 'darwin') { + if (arch === 'arm64') { + env = i18n('icu:About__AppEnvironment--AppleSilicon', { appEnv }); + } else { + env = i18n('icu:About__AppEnvironment--AppleIntel', { appEnv }); + } + } else { + env = i18n('icu:About__AppEnvironment', { appEnv }); + } + return (
-
+
+

{i18n('icu:signalDesktop')}

{version}
-
{environment}
+
{env}
+
diff --git a/ts/components/ChatsTab.stories.tsx b/ts/components/ChatsTab.stories.tsx new file mode 100644 index 000000000..38306c2df --- /dev/null +++ b/ts/components/ChatsTab.stories.tsx @@ -0,0 +1,46 @@ +// Copyright 2024 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import React from 'react'; +import { action } from '@storybook/addon-actions'; +import { setupI18n } from '../util/setupI18n'; +import enMessages from '../../_locales/en/messages.json'; +import type { ComponentMeta } from '../storybook/types'; +import type { ChatsTabProps } from './ChatsTab'; +import { ChatsTab } from './ChatsTab'; + +const i18n = setupI18n('en', enMessages); + +export default { + title: 'Components/ChatsTab', + component: ChatsTab, + parameters: { + layout: 'fullscreen', + }, + args: { + i18n, + otherTabsUnreadStats: { + unreadCount: 0, + unreadMentionsCount: 0, + markedUnread: false, + }, + isStaging: false, + hasPendingUpdate: false, + hasFailedStorySends: false, + navTabsCollapsed: false, + onToggleNavTabsCollapse: action('onToggleNavTabsCollapse'), + renderConversationView: () => <>{null}, + renderLeftPane: () => <>{null}, + renderMiniPlayer: () => <>{null}, + selectedConversationId: undefined, + showWhatsNewModal: action('showWhatsNewModal'), + }, +} satisfies ComponentMeta; + +export function Basic(args: ChatsTabProps): JSX.Element { + return ( +
+ +
+ ); +} diff --git a/ts/components/ChatsTab.tsx b/ts/components/ChatsTab.tsx index 18256d96c..e2af2b512 100644 --- a/ts/components/ChatsTab.tsx +++ b/ts/components/ChatsTab.tsx @@ -7,7 +7,7 @@ import type { NavTabPanelProps } from './NavTabs'; import { WhatsNewLink } from './WhatsNewLink'; import type { UnreadStats } from '../util/countUnreadStats'; -type ChatsTabProps = Readonly<{ +export type ChatsTabProps = Readonly<{ otherTabsUnreadStats: UnreadStats; i18n: LocalizerType; isStaging: boolean; @@ -59,7 +59,7 @@ export function ChatsTab({ ) : (
{renderMiniPlayer({ shouldFlow: false })} -
+

{isStaging ? 'THIS IS A STAGING DESKTOP' diff --git a/ts/components/Inbox.tsx b/ts/components/Inbox.tsx index c84fc443c..946b16dba 100644 --- a/ts/components/Inbox.tsx +++ b/ts/components/Inbox.tsx @@ -150,7 +150,9 @@ export function Inbox({ } logo =
{parts}
; } else { - logo =
; + logo = ( +
+ ); } return ( diff --git a/ts/context/createNativeThemeListener.ts b/ts/context/createNativeThemeListener.ts index 7c4a6d4cc..f3af48309 100644 --- a/ts/context/createNativeThemeListener.ts +++ b/ts/context/createNativeThemeListener.ts @@ -3,6 +3,7 @@ /* eslint-disable no-restricted-syntax */ import type { NativeThemeState } from '../types/NativeThemeNotifier.d'; +import { SystemThemeType } from '../types/Util'; export type Callback = (change: NativeThemeState) => void; @@ -19,9 +20,6 @@ export interface MinimalIPC { listener: (event: unknown, ...args: ReadonlyArray) => void ): this; } - -type SystemThemeType = 'dark' | 'light'; - export type SystemThemeHolder = { systemTheme: SystemThemeType }; export type NativeThemeType = { @@ -41,7 +39,9 @@ export function createNativeThemeListener( let systemTheme: SystemThemeType; function update(): SystemThemeType { - const nextSystemTheme = theme.shouldUseDarkColors ? 'dark' : 'light'; + const nextSystemTheme = theme.shouldUseDarkColors + ? SystemThemeType.dark + : SystemThemeType.light; // eslint-disable-next-line no-param-reassign holder.systemTheme = nextSystemTheme; return nextSystemTheme; diff --git a/ts/scripts/generate-tray-icons.ts b/ts/scripts/generate-tray-icons.ts new file mode 100644 index 000000000..33817bc93 --- /dev/null +++ b/ts/scripts/generate-tray-icons.ts @@ -0,0 +1,297 @@ +// Copyright 2024 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { createCanvas, GlobalFonts, loadImage } from '@napi-rs/canvas'; +import { join } from 'node:path'; +import { mkdir, rm, writeFile } from 'node:fs/promises'; +import { strictAssert } from '../util/assert'; +import { SystemThemeType } from '../types/Util'; + +const cwd = __dirname; +const fontsDir = join(cwd, '..', '..', 'fonts'); +const imagesDir = join(cwd, '..', '..', 'images'); +const trayIconsDir = join(imagesDir, 'tray-icons'); +const trayIconsBaseDir = join(trayIconsDir, 'base'); +const trayIconsAlertsDir = join(trayIconsDir, 'alert'); + +enum TrayIconSize { + Size16 = '16', + Size32 = '32', + Size48 = '48', + Size256 = '256', +} + +type TrayIconValue = number | string | null; + +type TrayIconImageRequest = Readonly<{ + size: TrayIconSize; + theme: SystemThemeType; + value: TrayIconValue; +}>; + +type TrayIconVariant = { + size: number; + maxCount: number; + badgePadding: number; + fontSize: number; + fontWeight: string; + fontOffsetY: number; + badgeShadowBlur: number; + badgeShadowOffsetY: number; + images: Record; +}; + +GlobalFonts.loadFontsFromDir(fontsDir); + +const Inter = GlobalFonts.families.find(family => { + return family.family === 'Inter'; +}); + +strictAssert(Inter != null, `Failed to load fonts from ${fontsDir}`); + +const Constants = { + fontFamily: 'Inter', + badgeColor: 'rgb(244, 67, 54)', + badgeShadowColor: 'rgba(0, 0, 0, 0.25)', +}; + +const Variants: Record = { + [TrayIconSize.Size16]: { + size: 16, + maxCount: 9, + badgePadding: 2, + fontSize: 8, + fontWeight: '500', + fontOffsetY: 0, + badgeShadowBlur: 0, + badgeShadowOffsetY: 0, + images: { + light: join(trayIconsBaseDir, 'signal-tray-icon-16x16-light-base.png'), + dark: join(trayIconsBaseDir, 'signal-tray-icon-16x16-dark-base.png'), + }, + }, + [TrayIconSize.Size32]: { + size: 32, + maxCount: 9, + badgePadding: 4, + fontSize: 12, + fontWeight: '500', + fontOffsetY: 0, + badgeShadowBlur: 1, + badgeShadowOffsetY: 1, + images: { + light: join(trayIconsBaseDir, 'signal-tray-icon-32x32-light-base.png'), + dark: join(trayIconsBaseDir, 'signal-tray-icon-32x32-dark-base.png'), + }, + }, + [TrayIconSize.Size48]: { + size: 48, + maxCount: 9, + badgePadding: 6, + fontSize: 16, + fontWeight: '500', + fontOffsetY: -1, + badgeShadowBlur: 1, + badgeShadowOffsetY: 1, + images: { + light: join(trayIconsBaseDir, 'signal-tray-icon-48x48-light-base.png'), + dark: join(trayIconsBaseDir, 'signal-tray-icon-48x48-dark-base.png'), + }, + }, + [TrayIconSize.Size256]: { + size: 256, + maxCount: 9, + fontSize: 72, + fontWeight: '600', + fontOffsetY: 0, + badgePadding: 32, + badgeShadowBlur: 8, + badgeShadowOffsetY: 8, + images: { + light: join(trayIconsBaseDir, 'signal-tray-icon-256x256-light-base.png'), + dark: join(trayIconsBaseDir, 'signal-tray-icon-256x256-dark-base.png'), + }, + }, +}; + +function trayIconValueToText( + value: TrayIconValue, + variant: TrayIconVariant +): string { + if (value == null) { + return ''; + } + + if (typeof value === 'string') { + return value.trim(); + } + + if (typeof value === 'number') { + if (!Number.isSafeInteger(value) || value < 0) { + throw new RangeError(`Unread count must be positive integer ${value}`); + } + + if (value === 0) { + return ''; + } + + if (value > variant.maxCount) { + return `${variant.maxCount}+`; + } + + return `${value}`; + } + throw new TypeError(`Invalid value ${value}`); +} + +async function generateTrayIconImage( + request: TrayIconImageRequest +): Promise { + const variant = Variants[request.size]; + if (variant == null) { + throw new TypeError(`Invalid variant size (${request.size})`); + } + + const imagePath = variant.images[request.theme]; + if (imagePath == null) { + throw new TypeError(`Invalid theme (theme: ${request.theme})`); + } + + const text = trayIconValueToText(request.value, variant); + + const image = await loadImage(imagePath); + const canvas = createCanvas(variant.size, variant.size); + const context = canvas.getContext('2d'); + + if (context == null) { + throw new Error('Failed to create 2d canvas context'); + } + + context.imageSmoothingEnabled = false; + context.imageSmoothingQuality = 'high'; + context.drawImage(image, 0, 0, variant.size, variant.size); + + if (text !== '') { + // Decrements by 1 until the badge fits within the canvas. + let currentFontSize = variant.fontSize; + + while (currentFontSize > 4) { + const font = `${variant.fontWeight} ${currentFontSize}px ${Constants.fontFamily}`; + + context.font = font; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + // @ts-expect-error Missing types + context.textRendering = 'optimizeLegibility'; + context.fontKerning = 'normal'; + + // All font settings should be set before now and should not change. + const capMetrics = context.measureText('X'); + const textMetrics = context.measureText(text); + const textWidth = Math.ceil( + textMetrics.actualBoundingBoxRight + textMetrics.actualBoundingBoxLeft + ); + const textHeight = Math.ceil( + capMetrics.actualBoundingBoxAscent + capMetrics.actualBoundingBoxDescent + ); + + const boxHeight = textHeight + variant.badgePadding * 2; + const boxWidth = Math.max( + boxHeight, // Ensures the badge is a circle + textWidth + variant.badgePadding * 2 + ); + + // Needed to avoid cutting off the shadow blur + const boxMargin = variant.badgeShadowBlur; + const boxWidthWithMargins = boxWidth + boxMargin * 2; + + if (boxWidthWithMargins > variant.size) { + currentFontSize -= 1; + continue; + } + + const boxX = variant.size - boxWidth - boxMargin; // right aligned + const boxY = boxMargin; + const boxMidX = boxX + boxWidth / 2; + const boxMidY = boxY + boxHeight / 2; + const boxRadius = Math.ceil(boxHeight / 2); + + context.save(); + context.beginPath(); + context.roundRect(boxX, boxY, boxWidth, boxHeight, boxRadius); + context.fillStyle = Constants.badgeColor; + if (variant.badgeShadowBlur !== 0 || variant.badgeShadowOffsetY !== 0) { + context.shadowBlur = variant.badgeShadowBlur; + context.shadowOffsetX = 0; + context.shadowOffsetY = variant.badgeShadowOffsetY; + context.shadowColor = Constants.badgeShadowColor; + } + context.fill(); + context.restore(); + + context.fillStyle = 'white'; + context.fillText(text, boxMidX, boxMidY + variant.fontOffsetY); + + break; + } + + if (currentFontSize <= 4) { + throw new Error( + `Badge text is too large for canvas size ${variant.size} (${text})` + ); + } + } + + return canvas.toBuffer('image/png'); +} + +function range(start: number, end: number): Array { + const length = end - start + 1; + return Array.from({ length }, (_, index) => start + index); +} + +async function main() { + try { + await rm(trayIconsAlertsDir, { recursive: true }); + } catch (error) { + if (error.code !== 'ENOENT') { + throw error; + } + } + + const requests: Array = []; + for (const size of Object.values(TrayIconSize)) { + const variant = Variants[size]; + const { maxCount } = variant; + const values = range(1, maxCount + 1); + for (const theme of Object.values(SystemThemeType)) { + for (const value of values) { + requests.push({ size, theme, value }); + } + } + } + + await Promise.all( + requests.map(async ({ size, theme, value }) => { + const variant = Variants[size]; + const text = trayIconValueToText(value, variant); + + const fileDir = join(trayIconsAlertsDir); + const fileName = `signal-tray-icon-${size}x${size}-${theme}-alert-${text}.png`; + const filePath = join(fileDir, fileName); + + const fileContents = await generateTrayIconImage({ size, theme, value }); + + console.log(`Writing "${fileName}"`); + await mkdir(fileDir, { recursive: true }); + await writeFile(filePath, fileContents); + }) + ); + + console.log('Done'); +} + +main().catch(error => { + console.error(error); + process.exit(1); +}); diff --git a/ts/test-electron/context/createNativeThemeListener_test.ts b/ts/test-electron/context/createNativeThemeListener_test.ts index cb664bcb9..126d735f4 100644 --- a/ts/test-electron/context/createNativeThemeListener_test.ts +++ b/ts/test-electron/context/createNativeThemeListener_test.ts @@ -10,6 +10,7 @@ import type { } from '../../context/createNativeThemeListener'; import { createNativeThemeListener } from '../../context/createNativeThemeListener'; import type { NativeThemeState } from '../../types/NativeThemeNotifier.d'; +import { SystemThemeType } from '../../types/Util'; class FakeIPC extends EventEmitter implements MinimalIPC { constructor(private readonly state: NativeThemeState) { @@ -29,7 +30,7 @@ class FakeIPC extends EventEmitter implements MinimalIPC { } describe('NativeThemeListener', () => { - const holder: SystemThemeHolder = { systemTheme: 'dark' }; + const holder: SystemThemeHolder = { systemTheme: SystemThemeType.dark }; it('syncs the initial native theme', () => { const dark = createNativeThemeListener( diff --git a/ts/test-node/app/SystemTrayService_test.ts b/ts/test-node/app/SystemTrayService_test.ts index c2ced014e..86e1ebe93 100644 --- a/ts/test-node/app/SystemTrayService_test.ts +++ b/ts/test-node/app/SystemTrayService_test.ts @@ -5,7 +5,6 @@ import { assert } from 'chai'; import * as sinon from 'sinon'; import type { MenuItem } from 'electron'; import { BrowserWindow, Tray, nativeImage } from 'electron'; -import * as path from 'path'; import { MINUTE } from '../../util/durations'; import type { SystemTrayServiceOptionsType } from '../../../app/SystemTrayService'; @@ -218,26 +217,17 @@ describe('SystemTrayService', function (this: Mocha.Suite) { // can't spy on `Tray.prototype.setImage` because it's not defined that way. So we // spy on the specific instance, just to get the image. const setImageSpy = sandbox.spy(tray, 'setImage'); - const getImagePath = (): string => { - const result = setImageSpy.lastCall?.firstArg; - if (!result) { - throw new Error('Expected tray.setImage to be called at least once'); - } - return result; - }; - - for (let i = 9; i >= 1; i -= 1) { - service.setUnreadCount(i); - assert.strictEqual(path.parse(getImagePath()).base, `${i}.png`); - } - - for (let i = 10; i < 13; i += 1) { - service.setUnreadCount(i); - assert.strictEqual(path.parse(getImagePath()).base, '10.png'); - } + service.setUnreadCount(1); + assert.strictEqual(setImageSpy.callCount, 1); + service.setUnreadCount(1); + assert.strictEqual(setImageSpy.callCount, 1); + service.setUnreadCount(2); + assert.strictEqual(setImageSpy.callCount, 2); + service.setUnreadCount(2); + assert.strictEqual(setImageSpy.callCount, 2); service.setUnreadCount(0); - assert.match(path.parse(getImagePath()).base, /^icon_\d+\.png$/); + assert.strictEqual(setImageSpy.callCount, 3); }); it('uses a fallback image if the icon file cannot be found', () => { @@ -251,7 +241,7 @@ describe('SystemTrayService', function (this: Mocha.Suite) { } const setImageStub = sandbox.stub(tray, 'setImage'); - setImageStub.withArgs(sinon.match.string).throws('Failed to load'); + setImageStub.onFirstCall().throws('Failed to load'); service.setUnreadCount(4); @@ -259,7 +249,7 @@ describe('SystemTrayService', function (this: Mocha.Suite) { const NativeImage = nativeImage.createEmpty().constructor; sinon.assert.calledTwice(setImageStub); - sinon.assert.calledWith(setImageStub, sinon.match.string); + sinon.assert.calledWith(setImageStub, sinon.match.instanceOf(NativeImage)); sinon.assert.calledWith(setImageStub, sinon.match.instanceOf(NativeImage)); }); diff --git a/ts/types/Util.ts b/ts/types/Util.ts index a66741a74..27fa6b2f4 100644 --- a/ts/types/Util.ts +++ b/ts/types/Util.ts @@ -60,6 +60,11 @@ export enum ThemeType { 'dark' = 'dark', } +export enum SystemThemeType { + light = 'light', + dark = 'dark', +} + // These are strings so they can be interpolated into class names. export enum ScrollBehavior { Default = 'default', diff --git a/ts/util/getThemeType.ts b/ts/util/getThemeType.ts index 78d5eeae2..afac7dfc4 100644 --- a/ts/util/getThemeType.ts +++ b/ts/util/getThemeType.ts @@ -1,16 +1,19 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import { ThemeType } from '../types/Util'; +import { SystemThemeType, ThemeType } from '../types/Util'; export async function getThemeType(): Promise { const themeSetting = await window.Events.getThemeSetting(); - if (themeSetting === 'light') { + if ( + themeSetting === 'light' || + window.systemTheme === SystemThemeType.light + ) { return ThemeType.light; } - if (themeSetting === 'dark') { + if (themeSetting === 'dark' || window.systemTheme === SystemThemeType.dark) { return ThemeType.dark; } diff --git a/ts/window.d.ts b/ts/window.d.ts index 0f87eae3f..dfc765a2e 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -27,7 +27,7 @@ import type * as Crypto from './Crypto'; import type * as Curve from './Curve'; import type * as RemoteConfig from './RemoteConfig'; import type { OSType } from './util/os/shared'; -import type { LocalizerType, ThemeType } from './types/Util'; +import type { LocalizerType, SystemThemeType, ThemeType } from './types/Util'; import type { Receipt } from './types/Receipt'; import type { ConversationController } from './ConversationController'; import type { ReduxActions } from './state/types'; @@ -104,8 +104,8 @@ export type FeatureFlagType = { }; type AboutWindowPropsType = { + appEnv: string; arch: string; - environmentText: string; platform: string; }; @@ -224,7 +224,7 @@ declare global { sendChallengeRequest: (request: IPCChallengeRequest) => void; showKeyboardShortcuts: () => void; storage: Storage; - systemTheme: ThemeType; + systemTheme: SystemThemeType; Signal: SignalCoreType; diff --git a/ts/windows/about/app.tsx b/ts/windows/about/app.tsx index 5ab9aceb6..084c5ce51 100644 --- a/ts/windows/about/app.tsx +++ b/ts/windows/about/app.tsx @@ -12,21 +12,12 @@ const { AboutWindowProps } = window.Signal; strictAssert(AboutWindowProps, 'window values not provided'); -let platform = ''; -if (AboutWindowProps.platform === 'darwin') { - if (AboutWindowProps.arch === 'arm64') { - platform = ` (${i18n('icu:appleSilicon')})`; - } else { - platform = ' (Intel)'; - } -} - -const environmentText = `${AboutWindowProps.environmentText}${platform}`; - ReactDOM.render( window.SignalContext.executeMenuRole('close')} - environment={environmentText} + appEnv={AboutWindowProps.appEnv} + platform={AboutWindowProps.platform} + arch={AboutWindowProps.arch} i18n={i18n} version={window.SignalContext.getVersion()} />, diff --git a/ts/windows/about/preload.ts b/ts/windows/about/preload.ts index 5a9244ad6..17ccde3b1 100644 --- a/ts/windows/about/preload.ts +++ b/ts/windows/about/preload.ts @@ -14,8 +14,8 @@ if (config.appInstance) { const Signal = { AboutWindowProps: { + appEnv: environments.join(' - '), arch: process.arch, - environmentText: environments.join(' - '), platform: process.platform, }, };