Get native theme through IPC not remote

This commit is contained in:
Fedor Indutny 2021-06-30 11:57:43 -07:00 committed by GitHub
parent d2cc8e5aa9
commit 71572db7a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 227 additions and 60 deletions

View file

@ -15,7 +15,7 @@ const {
const { Context: SignalContext } = require('./ts/context'); const { Context: SignalContext } = require('./ts/context');
window.SignalContext = new SignalContext(); window.SignalContext = new SignalContext(ipcRenderer);
const config = url.parse(window.location.toString(), true).query; const config = url.parse(window.location.toString(), true).query;
const { locale } = config; const { locale } = config;

View file

@ -20,7 +20,7 @@ async function applyTheme() {
applyTheme(); applyTheme();
window.subscribeToSystemThemeChange(() => { window.SignalContext.nativeThemeListener.subscribe(() => {
applyTheme(); applyTheme();
}); });

View file

@ -20,7 +20,7 @@ async function applyTheme() {
applyTheme(); applyTheme();
window.subscribeToSystemThemeChange(() => { window.SignalContext.nativeThemeListener.subscribe(() => {
applyTheme(); applyTheme();
}); });

View file

@ -122,6 +122,7 @@ const {
} = require('./ts/types/Settings'); } = require('./ts/types/Settings');
const { Environment } = require('./ts/environment'); const { Environment } = require('./ts/environment');
const { ChallengeMainHandler } = require('./ts/main/challengeMain'); const { ChallengeMainHandler } = require('./ts/main/challengeMain');
const { NativeThemeNotifier } = require('./ts/main/NativeThemeNotifier');
const { PowerChannel } = require('./ts/main/powerChannel'); const { PowerChannel } = require('./ts/main/powerChannel');
const { maybeParseUrl, setUrlSearchParams } = require('./ts/util/url'); const { maybeParseUrl, setUrlSearchParams } = require('./ts/util/url');
@ -136,6 +137,9 @@ const systemTraySettingCache = new SystemTraySettingCache(
const challengeHandler = new ChallengeMainHandler(); const challengeHandler = new ChallengeMainHandler();
const nativeThemeNotifier = new NativeThemeNotifier();
nativeThemeNotifier.initialize();
let sqlInitTimeStart = 0; let sqlInitTimeStart = 0;
let sqlInitTimeEnd = 0; let sqlInitTimeEnd = 0;
@ -303,6 +307,8 @@ function handleCommonWindowEvents(window) {
window.webContents.on('preload-error', (event, preloadPath, error) => { window.webContents.on('preload-error', (event, preloadPath, error) => {
console.error(`Preload error in ${preloadPath}: `, error.message); console.error(`Preload error in ${preloadPath}: `, error.message);
}); });
nativeThemeNotifier.addWindow(window);
} }
const DEFAULT_WIDTH = 800; const DEFAULT_WIDTH = 800;

View file

@ -6,7 +6,7 @@
window.React = require('react'); window.React = require('react');
window.ReactDOM = require('react-dom'); window.ReactDOM = require('react-dom');
const { ipcRenderer, remote } = require('electron'); const { ipcRenderer } = require('electron');
const url = require('url'); const url = require('url');
const i18n = require('./js/modules/i18n'); const i18n = require('./js/modules/i18n');
const { ConfirmationDialog } = require('./ts/components/ConfirmationDialog'); const { ConfirmationDialog } = require('./ts/components/ConfirmationDialog');
@ -17,8 +17,6 @@ const {
parseEnvironment, parseEnvironment,
} = require('./ts/environment'); } = require('./ts/environment');
const { nativeTheme } = remote.require('electron');
const { Context: SignalContext } = require('./ts/context'); const { Context: SignalContext } = require('./ts/context');
const config = url.parse(window.location.toString(), true).query; const config = url.parse(window.location.toString(), true).query;
@ -26,7 +24,7 @@ const { locale } = config;
const localeMessages = ipcRenderer.sendSync('locale-data'); const localeMessages = ipcRenderer.sendSync('locale-data');
setEnvironment(parseEnvironment(config.environment)); setEnvironment(parseEnvironment(config.environment));
window.SignalContext = new SignalContext(); window.SignalContext = new SignalContext(ipcRenderer);
window.getEnvironment = getEnvironment; window.getEnvironment = getEnvironment;
window.getVersion = () => config.version; window.getVersion = () => config.version;
@ -40,19 +38,6 @@ window.Signal = {
}, },
}; };
function setSystemTheme() {
window.systemTheme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
}
setSystemTheme();
window.subscribeToSystemThemeChange = fn => {
nativeTheme.on('updated', () => {
setSystemTheme();
fn();
});
};
require('./ts/logging/set_up_renderer_logging').initialize(); require('./ts/logging/set_up_renderer_logging').initialize();
window.closePermissionsPopup = () => window.closePermissionsPopup = () =>

View file

@ -19,14 +19,14 @@ try {
parseEnvironment, parseEnvironment,
Environment, Environment,
} = require('./ts/environment'); } = require('./ts/environment');
const ipc = electron.ipcRenderer;
const { remote } = electron; const { remote } = electron;
const { app } = remote; const { app } = remote;
const { nativeTheme } = remote.require('electron');
const { Context: SignalContext } = require('./ts/context'); const { Context: SignalContext } = require('./ts/context');
window.SignalContext = new SignalContext(); window.SignalContext = new SignalContext(ipc);
window.sqlInitializer = require('./ts/sql/initialize'); window.sqlInitializer = require('./ts/sql/initialize');
@ -77,19 +77,6 @@ try {
app.setLoginItemSettings({ openAtLogin: Boolean(value) }); app.setLoginItemSettings({ openAtLogin: Boolean(value) });
}; };
function setSystemTheme() {
window.systemTheme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
}
setSystemTheme();
window.subscribeToSystemThemeChange = fn => {
nativeTheme.on('updated', () => {
setSystemTheme();
fn();
});
};
window.isBeforeVersion = (toCheck, baseVersion) => { window.isBeforeVersion = (toCheck, baseVersion) => {
try { try {
return semver.lt(toCheck, baseVersion); return semver.lt(toCheck, baseVersion);
@ -113,7 +100,6 @@ try {
} }
}; };
const ipc = electron.ipcRenderer;
const localeMessages = ipc.sendSync('locale-data'); const localeMessages = ipc.sendSync('locale-data');
window.setBadgeCount = count => ipc.send('set-badge-count', count); window.setBadgeCount = count => ipc.send('set-badge-count', count);

View file

@ -20,7 +20,7 @@ const {
const { Context: SignalContext } = require('./ts/context'); const { Context: SignalContext } = require('./ts/context');
window.SignalContext = new SignalContext(); window.SignalContext = new SignalContext(ipcRenderer);
const config = url.parse(window.location.toString(), true).query; const config = url.parse(window.location.toString(), true).query;
const { locale } = config; const { locale } = config;

View file

@ -3,7 +3,7 @@
/* global window */ /* global window */
const { ipcRenderer, remote } = require('electron'); const { ipcRenderer } = require('electron');
const url = require('url'); const url = require('url');
const i18n = require('./js/modules/i18n'); const i18n = require('./js/modules/i18n');
@ -18,11 +18,9 @@ const { locale } = config;
const localeMessages = ipcRenderer.sendSync('locale-data'); const localeMessages = ipcRenderer.sendSync('locale-data');
setEnvironment(parseEnvironment(config.environment)); setEnvironment(parseEnvironment(config.environment));
const { nativeTheme } = remote.require('electron');
const { Context: SignalContext } = require('./ts/context'); const { Context: SignalContext } = require('./ts/context');
window.SignalContext = new SignalContext(); window.SignalContext = new SignalContext(ipcRenderer);
window.platform = process.platform; window.platform = process.platform;
window.theme = config.theme; window.theme = config.theme;
@ -30,19 +28,6 @@ window.i18n = i18n.setup(locale, localeMessages);
window.appStartInitialSpellcheckSetting = window.appStartInitialSpellcheckSetting =
config.appStartInitialSpellcheckSetting === 'true'; config.appStartInitialSpellcheckSetting === 'true';
function setSystemTheme() {
window.systemTheme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
}
setSystemTheme();
window.subscribeToSystemThemeChange = fn => {
nativeTheme.on('updated', () => {
setSystemTheme();
fn();
});
};
window.getEnvironment = getEnvironment; window.getEnvironment = getEnvironment;
window.getVersion = () => config.version; window.getVersion = () => config.version;
window.getAppInstance = () => config.appInstance; window.getAppInstance = () => config.appInstance;

View file

@ -19,7 +19,6 @@ const {
const { makeGetter } = require('../preload_utils'); const { makeGetter } = require('../preload_utils');
const { dialog } = remote; const { dialog } = remote;
const { nativeTheme } = remote.require('electron');
const { Context: SignalContext } = require('../ts/context'); const { Context: SignalContext } = require('../ts/context');
@ -29,7 +28,7 @@ const MAX_STICKER_DIMENSION = STICKER_SIZE;
const MAX_WEBP_STICKER_BYTE_LENGTH = 100 * 1024; const MAX_WEBP_STICKER_BYTE_LENGTH = 100 * 1024;
const MAX_ANIMATED_STICKER_BYTE_LENGTH = 300 * 1024; const MAX_ANIMATED_STICKER_BYTE_LENGTH = 300 * 1024;
window.SignalContext = new SignalContext(); window.SignalContext = new SignalContext(ipc);
setEnvironment(parseEnvironment(config.environment)); setEnvironment(parseEnvironment(config.environment));
@ -280,6 +279,7 @@ const getThemeSetting = makeGetter('theme-setting');
async function resolveTheme() { async function resolveTheme() {
const theme = (await getThemeSetting()) || 'system'; const theme = (await getThemeSetting()) || 'system';
if (process.platform === 'darwin' && theme === 'system') { if (process.platform === 'darwin' && theme === 'system') {
const { theme: nativeTheme } = window.SignalContext.nativeThemeListener;
return nativeTheme.shouldUseDarkColors ? 'dark' : 'light'; return nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
} }
return theme; return theme;
@ -293,8 +293,6 @@ async function applyTheme() {
window.addEventListener('DOMContentLoaded', applyTheme); window.addEventListener('DOMContentLoaded', applyTheme);
nativeTheme.on('updated', () => { window.SignalContext.nativeThemeListener.subscribe(() => applyTheme());
applyTheme();
});
window.log.info('sticker-creator preload complete...'); window.log.info('sticker-creator preload complete...');

View file

@ -20,7 +20,7 @@ const storageMap = new Map();
// To replicate logic we have on the client side // To replicate logic we have on the client side
global.window = { global.window = {
SignalContext: new SignalContext(), SignalContext: undefined,
log: { log: {
info: (...args) => console.log(...args), info: (...args) => console.log(...args),
warn: (...args) => console.warn(...args), warn: (...args) => console.warn(...args),
@ -38,6 +38,21 @@ global.window = {
isValidGuid, isValidGuid,
}; };
const fakeIPC = {
sendSync(channel) {
// See `ts/context/NativeThemeListener.ts`
if (channel === 'native-theme:init') {
return { shouldUseDarkColors: true };
}
throw new Error(`Unsupported sendSync channel: ${channel}`);
},
on() {},
};
global.window.SignalContext = new SignalContext(fakeIPC);
// For ducks/network.getEmptyState() // For ducks/network.getEmptyState()
global.navigator = {}; global.navigator = {};
global.WebSocket = {}; global.WebSocket = {};

View file

@ -2295,6 +2295,10 @@ export async function startApp(): Promise<void> {
} }
} }
window.SignalContext.nativeThemeListener.subscribe(() => {
onChangeTheme();
});
const FIVE_MINUTES = 5 * 60 * 1000; const FIVE_MINUTES = 5 * 60 * 1000;
// Note: once this function returns, there still might be messages being processed on // Note: once this function returns, there still might be messages being processed on

View file

@ -0,0 +1,48 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* eslint-disable no-restricted-syntax */
import { NativeThemeState } from '../types/NativeThemeNotifier.d';
export type Callback = (change: NativeThemeState) => void;
export interface MinimalIPC {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sendSync(channel: string): any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
on(channel: string, listener: (event: unknown, ...args: any[]) => void): this;
}
export type SystemThemeHolder = { systemTheme: 'dark' | 'light' };
export class NativeThemeListener {
private readonly subscribers = new Array<Callback>();
public theme: NativeThemeState;
constructor(ipc: MinimalIPC, private readonly holder: SystemThemeHolder) {
this.theme = ipc.sendSync('native-theme:init');
this.update();
ipc.on(
'native-theme:changed',
(_event: unknown, change: NativeThemeState) => {
this.theme = change;
this.update();
for (const fn of this.subscribers) {
fn(change);
}
}
);
}
public subscribe(fn: Callback): void {
this.subscribers.push(fn);
}
private update(): void {
this.holder.systemTheme = this.theme.shouldUseDarkColors ? 'dark' : 'light';
}
}

View file

@ -2,7 +2,14 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { Bytes } from './Bytes'; import { Bytes } from './Bytes';
import { NativeThemeListener, MinimalIPC } from './NativeThemeListener';
export class Context { export class Context {
public readonly bytes = new Bytes(); public readonly bytes = new Bytes();
public readonly nativeThemeListener;
constructor(ipc: MinimalIPC) {
this.nativeThemeListener = new NativeThemeListener(ipc, window);
}
} }

View file

@ -0,0 +1,46 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* eslint-disable no-restricted-syntax */
import { ipcMain as ipc, nativeTheme, BrowserWindow } from 'electron';
import { NativeThemeState } from '../types/NativeThemeNotifier.d';
function getState(): NativeThemeState {
return {
shouldUseDarkColors: nativeTheme.shouldUseDarkColors,
};
}
export class NativeThemeNotifier {
private readonly listeners = new Set<BrowserWindow>();
public initialize(): void {
nativeTheme.on('updated', () => {
this.notifyListeners();
});
ipc.on('native-theme:init', event => {
// eslint-disable-next-line no-param-reassign
event.returnValue = getState();
});
}
public addWindow(window: BrowserWindow): void {
if (this.listeners.has(window)) {
return;
}
this.listeners.add(window);
window.once('closed', () => {
this.listeners.delete(window);
});
}
private notifyListeners(): void {
for (const window of this.listeners) {
window.webContents.send('native-theme:changed', getState());
}
}
}

View file

@ -0,0 +1,81 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import { EventEmitter } from 'events';
import {
NativeThemeListener,
MinimalIPC,
SystemThemeHolder,
} from '../../context/NativeThemeListener';
import { NativeThemeState } from '../../types/NativeThemeNotifier.d';
class FakeIPC extends EventEmitter implements MinimalIPC {
constructor(private readonly state: NativeThemeState) {
super();
}
public sendSync(channel: string) {
assert.strictEqual(channel, 'native-theme:init');
return this.state;
}
}
describe('NativeThemeListener', () => {
const holder: SystemThemeHolder = { systemTheme: 'dark' };
it('syncs the initial native theme', () => {
const dark = new NativeThemeListener(
new FakeIPC({
shouldUseDarkColors: true,
}),
holder
);
assert.strictEqual(holder.systemTheme, 'dark');
assert.isTrue(dark.theme.shouldUseDarkColors);
const light = new NativeThemeListener(
new FakeIPC({
shouldUseDarkColors: false,
}),
holder
);
assert.strictEqual(holder.systemTheme, 'light');
assert.isFalse(light.theme.shouldUseDarkColors);
});
it('should react to native theme changes', () => {
const ipc = new FakeIPC({
shouldUseDarkColors: true,
});
const listener = new NativeThemeListener(ipc, holder);
ipc.emit('native-theme:changed', null, <NativeThemeState>{
shouldUseDarkColors: false,
});
assert.strictEqual(holder.systemTheme, 'light');
assert.isFalse(listener.theme.shouldUseDarkColors);
});
it('should notify subscribers of native theme changes', done => {
const ipc = new FakeIPC({
shouldUseDarkColors: true,
});
const listener = new NativeThemeListener(ipc, holder);
listener.subscribe(state => {
assert.isFalse(state.shouldUseDarkColors);
done();
});
ipc.emit('native-theme:changed', null, <NativeThemeState>{
shouldUseDarkColors: false,
});
});
});

6
ts/types/NativeThemeNotifier.d.ts vendored Normal file
View file

@ -0,0 +1,6 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
export type NativeThemeState = Readonly<{
shouldUseDarkColors: boolean;
}>;