Dark theme loading screen

This commit is contained in:
Fedor Indutny 2022-05-11 15:58:14 -07:00 committed by GitHub
parent af2c884c9f
commit b30c7f9c46
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 114 additions and 18 deletions

View file

@ -19,6 +19,7 @@ import {
dialog, dialog,
ipcMain as ipc, ipcMain as ipc,
Menu, Menu,
nativeTheme,
powerSaveBlocker, powerSaveBlocker,
protocol as electronProtocol, protocol as electronProtocol,
screen, screen,
@ -33,8 +34,11 @@ import * as GlobalErrors from './global_errors';
import { setup as setupCrashReports } from './crashReports'; import { setup as setupCrashReports } from './crashReports';
import { setup as setupSpellChecker } from './spell_check'; import { setup as setupSpellChecker } from './spell_check';
import { redactAll, addSensitivePath } from '../ts/util/privacy'; import { redactAll, addSensitivePath } from '../ts/util/privacy';
import { missingCaseError } from '../ts/util/missingCaseError';
import { strictAssert } from '../ts/util/assert'; import { strictAssert } from '../ts/util/assert';
import { consoleLogger } from '../ts/util/consoleLogger'; import { consoleLogger } from '../ts/util/consoleLogger';
import type { ThemeSettingType } from '../ts/types/Storage.d';
import { ThemeType } from '../ts/types/Util';
import './startup_config'; import './startup_config';
@ -230,6 +234,61 @@ async function getSpellCheckSetting() {
return slowValue; return slowValue;
} }
type GetThemeSettingOptionsType = Readonly<{
ephemeralOnly?: boolean;
}>;
async function getThemeSetting({
ephemeralOnly = false,
}: GetThemeSettingOptionsType = {}): Promise<ThemeSettingType> {
const fastValue = ephemeralConfig.get('theme-setting');
if (fastValue !== undefined) {
getLogger().info('got fast theme-setting value', fastValue);
return fastValue as ThemeSettingType;
}
if (ephemeralOnly) {
return 'system';
}
const json = await sql.sqlCall('getItemById', ['theme-setting']);
// Default to `system` if setting doesn't exist yet
const slowValue = json ? json.value : 'system';
ephemeralConfig.set('theme-setting', slowValue);
getLogger().info('got slow theme-setting value', slowValue);
return slowValue;
}
async function getResolvedThemeSetting(
options?: GetThemeSettingOptionsType
): Promise<ThemeType> {
const theme = await getThemeSetting(options);
if (theme === 'system') {
return nativeTheme.shouldUseDarkColors ? ThemeType.dark : ThemeType.light;
}
return ThemeType[theme];
}
async function getBackgroundColor(
options?: GetThemeSettingOptionsType
): Promise<string> {
const theme = await getResolvedThemeSetting(options);
if (theme === 'light') {
return '#3a76f0';
}
if (theme === 'dark') {
return '#121212';
}
throw missingCaseError(theme);
}
let systemTrayService: SystemTrayService | undefined; let systemTrayService: SystemTrayService | undefined;
const systemTraySettingCache = new SystemTraySettingCache( const systemTraySettingCache = new SystemTraySettingCache(
sql, sql,
@ -479,7 +538,7 @@ async function createWindow() {
: 'default', : 'default',
backgroundColor: isTestEnvironment(getEnvironment()) backgroundColor: isTestEnvironment(getEnvironment())
? '#ffffff' // Tests should always be rendered on a white background ? '#ffffff' // Tests should always be rendered on a white background
: '#3a76f0', : await getBackgroundColor(),
webPreferences: { webPreferences: {
...defaultWebPrefs, ...defaultWebPrefs,
nodeIntegration: false, nodeIntegration: false,
@ -599,6 +658,7 @@ async function createWindow() {
const moreKeys = { const moreKeys = {
isFullScreen: String(Boolean(mainWindow.isFullScreen())), isFullScreen: String(Boolean(mainWindow.isFullScreen())),
resolvedTheme: await getResolvedThemeSetting(),
}; };
if (getEnvironment() === Environment.Test) { if (getEnvironment() === Environment.Test) {
@ -996,7 +1056,7 @@ function showScreenShareWindow(sourceName: string) {
} }
let aboutWindow: BrowserWindow | undefined; let aboutWindow: BrowserWindow | undefined;
function showAbout() { async function showAbout() {
if (aboutWindow) { if (aboutWindow) {
aboutWindow.show(); aboutWindow.show();
return; return;
@ -1008,7 +1068,7 @@ function showAbout() {
resizable: false, resizable: false,
title: getLocale().i18n('aboutSignalDesktop'), title: getLocale().i18n('aboutSignalDesktop'),
autoHideMenuBar: true, autoHideMenuBar: true,
backgroundColor: '#3a76f0', backgroundColor: await getBackgroundColor(),
show: false, show: false,
webPreferences: { webPreferences: {
...defaultWebPrefs, ...defaultWebPrefs,
@ -1038,7 +1098,7 @@ function showAbout() {
} }
let settingsWindow: BrowserWindow | undefined; let settingsWindow: BrowserWindow | undefined;
function showSettingsWindow() { async function showSettingsWindow() {
if (settingsWindow) { if (settingsWindow) {
settingsWindow.show(); settingsWindow.show();
return; return;
@ -1051,7 +1111,7 @@ function showSettingsWindow() {
resizable: false, resizable: false,
title: getLocale().i18n('signalDesktopPreferences'), title: getLocale().i18n('signalDesktopPreferences'),
autoHideMenuBar: true, autoHideMenuBar: true,
backgroundColor: '#3a76f0', backgroundColor: await getBackgroundColor(),
show: false, show: false,
webPreferences: { webPreferences: {
...defaultWebPrefs, ...defaultWebPrefs,
@ -1121,7 +1181,7 @@ async function showStickerCreator() {
height: 650, height: 650,
title: getLocale().i18n('signalDesktopStickerCreator'), title: getLocale().i18n('signalDesktopStickerCreator'),
autoHideMenuBar: true, autoHideMenuBar: true,
backgroundColor: '#3a76f0', backgroundColor: await getBackgroundColor(),
show: false, show: false,
webPreferences: { webPreferences: {
...defaultWebPrefs, ...defaultWebPrefs,
@ -1172,16 +1232,14 @@ async function showDebugLogWindow() {
return; return;
} }
const theme = settingsChannel const theme = await getThemeSetting();
? await settingsChannel.getSettingFromMainWindow('themeSetting')
: undefined;
const options = { const options = {
width: 700, width: 700,
height: 500, height: 500,
resizable: false, resizable: false,
title: getLocale().i18n('debugLog'), title: getLocale().i18n('debugLog'),
autoHideMenuBar: true, autoHideMenuBar: true,
backgroundColor: '#3a76f0', backgroundColor: await getBackgroundColor(),
show: false, show: false,
webPreferences: { webPreferences: {
...defaultWebPrefs, ...defaultWebPrefs,
@ -1235,9 +1293,7 @@ function showPermissionsPopupWindow(forCalling: boolean, forCamera: boolean) {
return; return;
} }
const theme = settingsChannel const theme = await getThemeSetting();
? await settingsChannel.getSettingFromMainWindow('themeSetting')
: undefined;
const size = mainWindow.getSize(); const size = mainWindow.getSize();
const options = { const options = {
width: Math.min(400, size[0]), width: Math.min(400, size[0]),
@ -1245,7 +1301,7 @@ function showPermissionsPopupWindow(forCalling: boolean, forCamera: boolean) {
resizable: false, resizable: false,
title: getLocale().i18n('allowAccess'), title: getLocale().i18n('allowAccess'),
autoHideMenuBar: true, autoHideMenuBar: true,
backgroundColor: '#3a76f0', backgroundColor: await getBackgroundColor(),
show: false, show: false,
modal: true, modal: true,
webPreferences: { webPreferences: {
@ -1509,6 +1565,12 @@ app.on('ready', async () => {
const timeout = new Promise(resolveFn => const timeout = new Promise(resolveFn =>
setTimeout(resolveFn, 3000, 'timeout') setTimeout(resolveFn, 3000, 'timeout')
); );
// This color is to be used only in loading screen and in this case we should
// never wait for the database to be initialized. Thus the theme setting
// lookup should be done only in ephemeral config.
const backgroundColor = await getBackgroundColor({ ephemeralOnly: true });
// eslint-disable-next-line more/no-then // eslint-disable-next-line more/no-then
Promise.race([sqlInitPromise, timeout]).then(maybeTimeout => { Promise.race([sqlInitPromise, timeout]).then(maybeTimeout => {
if (maybeTimeout !== 'timeout') { if (maybeTimeout !== 'timeout') {
@ -1525,7 +1587,7 @@ app.on('ready', async () => {
height: 265, height: 265,
resizable: false, resizable: false,
frame: false, frame: false,
backgroundColor: '#3a76f0', backgroundColor,
webPreferences: { webPreferences: {
...defaultWebPrefs, ...defaultWebPrefs,
nodeIntegration: false, nodeIntegration: false,

View file

@ -15,6 +15,7 @@ try {
const { strictAssert } = require('./ts/util/assert'); const { strictAssert } = require('./ts/util/assert');
const { parseIntWithFallback } = require('./ts/util/parseIntWithFallback'); const { parseIntWithFallback } = require('./ts/util/parseIntWithFallback');
const { UUIDKind } = require('./ts/types/UUID'); const { UUIDKind } = require('./ts/types/UUID');
const { ThemeType } = require('./ts/types/Util');
// It is important to call this as early as possible // It is important to call this as early as possible
const { SignalContext } = require('./ts/windows/context'); const { SignalContext } = require('./ts/windows/context');
@ -247,6 +248,12 @@ try {
}); });
} }
if (config.resolvedTheme === 'light') {
window.initialTheme = ThemeType.light;
} else if (config.resolvedTheme === 'dark') {
window.initialTheme = ThemeType.dark;
}
// Settings-related events // Settings-related events
window.showSettings = () => ipc.send('show-settings'); window.showSettings = () => ipc.send('show-settings');

View file

@ -235,7 +235,16 @@ $loading-height: 16px;
right: 0; right: 0;
top: 0; top: 0;
bottom: 0; bottom: 0;
background-color: $color-ultramarine-icon;
/* Note: background-color is intentionally transparent until body has the
* theme class.
*/
@include explicit-light-theme {
background-color: $color-ultramarine-icon;
}
@include dark-theme {
background-color: $color-gray-95;
}
color: $color-white; color: $color-white;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View file

@ -97,6 +97,12 @@
@content; @content;
} }
@mixin explicit-light-theme() {
.light-theme & {
@content;
}
}
@mixin dark-theme() { @mixin dark-theme() {
.dark-theme & { .dark-theme & {
@content; @content;

View file

@ -29,6 +29,7 @@ import type { Receipt } from './types/Receipt';
import { getTitleBarVisibility, TitleBarVisibility } from './types/Settings'; import { getTitleBarVisibility, TitleBarVisibility } from './types/Settings';
import { SocketStatus } from './types/SocketStatus'; import { SocketStatus } from './types/SocketStatus';
import { DEFAULT_CONVERSATION_COLOR } from './types/Colors'; import { DEFAULT_CONVERSATION_COLOR } from './types/Colors';
import { ThemeType } from './types/Util';
import { ChallengeHandler } from './challenge'; import { ChallengeHandler } from './challenge';
import * as durations from './util/durations'; import * as durations from './util/durations';
import { explodePromise } from './util/explodePromise'; import { explodePromise } from './util/explodePromise';
@ -166,6 +167,13 @@ export async function cleanupSessionResets(): Promise<void> {
} }
export async function startApp(): Promise<void> { export async function startApp(): Promise<void> {
if (window.initialTheme === ThemeType.light) {
document.body.classList.add('light-theme');
}
if (window.initialTheme === ThemeType.dark) {
document.body.classList.add('dark-theme');
}
const idleDetector = new IdleDetector(); const idleDetector = new IdleDetector();
await KeyboardLayout.initialize(); await KeyboardLayout.initialize();

View file

@ -17,6 +17,7 @@ import type {
const EPHEMERAL_NAME_MAP = new Map([ const EPHEMERAL_NAME_MAP = new Map([
['spellCheck', 'spell-check'], ['spellCheck', 'spell-check'],
['systemTraySetting', 'system-tray-setting'], ['systemTraySetting', 'system-tray-setting'],
['themeSetting', 'theme-setting'],
]); ]);
type ResponseQueueEntry = Readonly<{ type ResponseQueueEntry = Readonly<{
@ -68,7 +69,9 @@ export class SettingsChannel {
this.installSetting('readReceiptSetting', { setter: false }); this.installSetting('readReceiptSetting', { setter: false });
this.installSetting('typingIndicatorSetting', { setter: false }); this.installSetting('typingIndicatorSetting', { setter: false });
this.installSetting('themeSetting'); this.installSetting('themeSetting', {
isEphemeral: true,
});
this.installSetting('hideMenuBar'); this.installSetting('hideMenuBar');
this.installSetting('systemTraySetting', { this.installSetting('systemTraySetting', {
isEphemeral: true, isEphemeral: true,

3
ts/window.d.ts vendored
View file

@ -29,7 +29,7 @@ import * as Curve from './Curve';
import * as RemoteConfig from './RemoteConfig'; import * as RemoteConfig from './RemoteConfig';
import * as OS from './OS'; import * as OS from './OS';
import { getEnvironment } from './environment'; import { getEnvironment } from './environment';
import { LocalizerType } from './types/Util'; import { LocalizerType, ThemeType } from './types/Util';
import type { Receipt } from './types/Receipt'; import type { Receipt } from './types/Receipt';
import { ConversationController } from './ConversationController'; import { ConversationController } from './ConversationController';
import { ReduxActions } from './state/types'; import { ReduxActions } from './state/types';
@ -212,6 +212,7 @@ declare global {
isAfterVersion: (version: string, anotherVersion: string) => boolean; isAfterVersion: (version: string, anotherVersion: string) => boolean;
isBeforeVersion: (version: string, anotherVersion: string) => boolean; isBeforeVersion: (version: string, anotherVersion: string) => boolean;
isFullScreen: () => boolean; isFullScreen: () => boolean;
initialTheme?: ThemeType;
libphonenumber: { libphonenumber: {
util: { util: {
getRegionCodeForNumber: (number: string) => string; getRegionCodeForNumber: (number: string) => string;