Add "system" theme setting for MacOS

This commit is contained in:
Ken Powers 2019-05-16 15:32:38 -07:00 committed by Scott Nonnenberg
parent 089a232a60
commit fd36720079
13 changed files with 174 additions and 35 deletions

View File

@ -1625,6 +1625,10 @@
"message": "Dark", "message": "Dark",
"description": "Label text for dark theme" "description": "Label text for dark theme"
}, },
"themeSystem": {
"message": "System",
"description": "Label text for system theme"
},
"noteToSelf": { "noteToSelf": {
"message": "Note to Self", "message": "Note to Self",
"description": "Name for the conversation with your own phone number" "description": "Name for the conversation with your own phone number"

View File

@ -208,6 +208,7 @@
switch (theme) { switch (theme) {
case 'dark': case 'dark':
case 'light': case 'light':
case 'system':
return theme; return theme;
case 'android-dark': case 'android-dark':
return 'dark'; return 'dark';
@ -231,7 +232,11 @@
window.Events = { window.Events = {
getDeviceName: () => textsecure.storage.user.getDeviceName(), getDeviceName: () => textsecure.storage.user.getDeviceName(),
getThemeSetting: () => storage.get('theme-setting', 'light'), getThemeSetting: () =>
storage.get(
'theme-setting',
window.platform === 'darwin' ? 'system' : 'light'
),
setThemeSetting: value => { setThemeSetting: value => {
storage.put('theme-setting', value); storage.put('theme-setting', value);
onChangeTheme(); onChangeTheme();
@ -326,6 +331,19 @@
`New version detected: ${currentVersion}; previous: ${lastVersion}` `New version detected: ${currentVersion}; previous: ${lastVersion}`
); );
const themeSetting = window.Events.getThemeSetting();
const newThemeSetting = mapOldThemeToNew(themeSetting);
if (
window.isBeforeVersion(lastVersion, 'v1.25.0') &&
window.platform === 'darwin' &&
newThemeSetting === window.systemTheme
) {
window.Events.setThemeSetting('system');
} else {
window.Events.setThemeSetting(newThemeSetting);
}
if (window.isBeforeVersion(lastVersion, 'v1.25.0')) { if (window.isBeforeVersion(lastVersion, 'v1.25.0')) {
// Stickers flags // Stickers flags
await Promise.all([ await Promise.all([
@ -334,6 +352,7 @@
]); ]);
} }
// This one should always be last - it could restart the app
if (window.isBeforeVersion(lastVersion, 'v1.15.0-beta.5')) { if (window.isBeforeVersion(lastVersion, 'v1.15.0-beta.5')) {
await window.Signal.Logs.deleteAll(); await window.Signal.Logs.deleteAll();
window.restart(); window.restart();
@ -411,10 +430,6 @@
}; };
startSpellCheck(); startSpellCheck();
const themeSetting = window.Events.getThemeSetting();
const newThemeSetting = mapOldThemeToNew(themeSetting);
window.Events.setThemeSetting(newThemeSetting);
try { try {
await Promise.all([ await Promise.all([
ConversationController.load(), ConversationController.load(),

View File

@ -9,7 +9,23 @@ $(document).on('keyup', e => {
}); });
const $body = $(document.body); const $body = $(document.body);
$body.addClass(`${window.theme}-theme`);
async function applyTheme() {
'use strict';
const theme = await window.getThemeSetting();
$body.removeClass('light-theme');
$body.removeClass('dark-theme');
$body.addClass(`${theme === 'system' ? window.systemTheme : theme}-theme`);
}
applyTheme();
window.subscribeToSystemThemeChange(() => {
'use strict';
applyTheme();
});
window.view = new Whisper.ConfirmationDialogView({ window.view = new Whisper.ConfirmationDialogView({
message: i18n('audioPermissionNeeded'), message: i18n('audioPermissionNeeded'),

View File

@ -9,7 +9,23 @@ $(document).on('keyup', e => {
}); });
const $body = $(document.body); const $body = $(document.body);
$body.addClass(`${window.theme}-theme`);
async function applyTheme() {
'use strict';
const theme = await window.getThemeSetting();
$body.removeClass('light-theme');
$body.removeClass('dark-theme');
$body.addClass(`${theme === 'system' ? window.systemTheme : theme}-theme`);
}
applyTheme();
window.subscribeToSystemThemeChange(() => {
'use strict';
applyTheme();
});
// eslint-disable-next-line strict // eslint-disable-next-line strict
const getInitialData = async () => ({ const getInitialData = async () => ({

View File

@ -8,6 +8,14 @@
window.Whisper = window.Whisper || {}; window.Whisper = window.Whisper || {};
function resolveTheme() {
const theme = storage.get('theme-setting') || 'light';
if (window.platform === 'darwin' && theme === 'system') {
return window.systemTheme;
}
return theme;
}
Whisper.AppView = Backbone.View.extend({ Whisper.AppView = Backbone.View.extend({
initialize() { initialize() {
this.inboxView = null; this.inboxView = null;
@ -15,14 +23,18 @@
this.applyTheme(); this.applyTheme();
this.applyHideMenu(); this.applyHideMenu();
window.subscribeToSystemThemeChange(() => {
this.applyTheme();
});
}, },
events: { events: {
'click .openInstaller': 'openInstaller', // NetworkStatusView has this button 'click .openInstaller': 'openInstaller', // NetworkStatusView has this button
openInbox: 'openInbox', openInbox: 'openInbox',
}, },
applyTheme() { applyTheme() {
const theme = resolveTheme();
const iOS = storage.get('userAgent') === 'OWI'; const iOS = storage.get('userAgent') === 'OWI';
const theme = storage.get('theme-setting') || 'light';
this.$el this.$el
.removeClass('light-theme') .removeClass('light-theme')
.removeClass('dark-theme') .removeClass('dark-theme')

View File

@ -88,7 +88,9 @@
$(document.body) $(document.body)
.removeClass('dark-theme') .removeClass('dark-theme')
.removeClass('light-theme') .removeClass('light-theme')
.addClass(`${theme}-theme`); .addClass(
`${theme === 'system' ? window.systemTheme : theme}-theme`
);
window.setThemeSetting(theme); window.setThemeSetting(theme);
}, },
}); });
@ -143,8 +145,10 @@
audioNotificationDescription: i18n('audioNotificationDescription'), audioNotificationDescription: i18n('audioNotificationDescription'),
isAudioNotificationSupported: Settings.isAudioNotificationSupported(), isAudioNotificationSupported: Settings.isAudioNotificationSupported(),
isHideMenuBarSupported: Settings.isHideMenuBarSupported(), isHideMenuBarSupported: Settings.isHideMenuBarSupported(),
hasSystemTheme: window.platform === 'darwin',
themeLight: i18n('themeLight'), themeLight: i18n('themeLight'),
themeDark: i18n('themeDark'), themeDark: i18n('themeDark'),
themeSystem: i18n('themeSystem'),
hideMenuBar: i18n('hideMenuBar'), hideMenuBar: i18n('hideMenuBar'),
clearDataHeader: i18n('clearDataHeader'), clearDataHeader: i18n('clearDataHeader'),
clearDataButton: i18n('clearDataButton'), clearDataButton: i18n('clearDataButton'),

View File

@ -507,7 +507,6 @@ async function showSettingsWindow() {
return; return;
} }
const theme = await pify(getDataFromMainWindow)('theme-setting');
const size = mainWindow.getSize(); const size = mainWindow.getSize();
const options = { const options = {
width: Math.min(500, size[0]), width: Math.min(500, size[0]),
@ -532,7 +531,7 @@ async function showSettingsWindow() {
captureClicks(settingsWindow); captureClicks(settingsWindow);
settingsWindow.loadURL(prepareURL([__dirname, 'settings.html'], { theme })); settingsWindow.loadURL(prepareURL([__dirname, 'settings.html']));
settingsWindow.on('closed', () => { settingsWindow.on('closed', () => {
removeDarkOverlay(); removeDarkOverlay();

View File

@ -181,6 +181,7 @@
"mac": { "mac": {
"artifactName": "${name}-mac-${version}.${ext}", "artifactName": "${name}-mac-${version}.${ext}",
"category": "public.app-category.social-networking", "category": "public.app-category.social-networking",
"darkModeSupport": true,
"icon": "build/icons/mac/icon.icns", "icon": "build/icons/mac/icon.icns",
"publish": [ "publish": [
{ {

View File

@ -1,9 +1,11 @@
/* global window */ /* global window */
const { ipcRenderer } = require('electron'); const { ipcRenderer, remote } = require('electron');
const url = require('url'); const url = require('url');
const i18n = require('./js/modules/i18n'); const i18n = require('./js/modules/i18n');
const { systemPreferences } = remote.require('electron');
const config = url.parse(window.location.toString(), true).query; const config = url.parse(window.location.toString(), true).query;
const { locale } = config; const { locale } = config;
const localeMessages = ipcRenderer.sendSync('locale-data'); const localeMessages = ipcRenderer.sendSync('locale-data');
@ -12,6 +14,25 @@ window.getVersion = () => config.version;
window.theme = config.theme; window.theme = config.theme;
window.i18n = i18n.setup(locale, localeMessages); window.i18n = i18n.setup(locale, localeMessages);
function setSystemTheme() {
window.systemTheme = systemPreferences.isDarkMode() ? 'dark' : 'light';
}
setSystemTheme();
window.subscribeToSystemThemeChange = fn => {
if (!systemPreferences.subscribeNotification) {
return;
}
systemPreferences.subscribeNotification(
'AppleInterfaceThemeChangedNotification',
() => {
setSystemTheme();
fn();
}
);
};
require('./js/logging'); require('./js/logging');
window.closePermissionsPopup = () => window.closePermissionsPopup = () =>
@ -19,6 +40,8 @@ window.closePermissionsPopup = () =>
window.getMediaPermissions = makeGetter('media-permissions'); window.getMediaPermissions = makeGetter('media-permissions');
window.setMediaPermissions = makeSetter('media-permissions'); window.setMediaPermissions = makeSetter('media-permissions');
window.getThemeSetting = makeGetter('theme-setting');
window.setThemeSetting = makeSetter('theme-setting');
function makeGetter(name) { function makeGetter(name) {
return () => return () =>

View File

@ -7,6 +7,7 @@ const semver = require('semver');
const { deferredToPromise } = require('./js/modules/deferred_to_promise'); const { deferredToPromise } = require('./js/modules/deferred_to_promise');
const { app } = electron.remote; const { app } = electron.remote;
const { systemPreferences } = electron.remote.require('electron');
window.PROTO_ROOT = 'protos'; window.PROTO_ROOT = 'protos';
const config = require('url').parse(window.location.toString(), true).query; const config = require('url').parse(window.location.toString(), true).query;
@ -31,6 +32,25 @@ window.getHostName = () => config.hostname;
window.getServerTrustRoot = () => config.serverTrustRoot; window.getServerTrustRoot = () => config.serverTrustRoot;
window.isBehindProxy = () => Boolean(config.proxyUrl); window.isBehindProxy = () => Boolean(config.proxyUrl);
function setSystemTheme() {
window.systemTheme = systemPreferences.isDarkMode() ? 'dark' : 'light';
}
setSystemTheme();
window.subscribeToSystemThemeChange = fn => {
if (!systemPreferences.subscribeNotification) {
return;
}
systemPreferences.subscribeNotification(
'AppleInterfaceThemeChangedNotification',
() => {
setSystemTheme();
fn();
}
);
};
window.isBeforeVersion = (toCheck, baseVersion) => { window.isBeforeVersion = (toCheck, baseVersion) => {
try { try {
return semver.lt(toCheck, baseVersion); return semver.lt(toCheck, baseVersion);

View File

@ -44,6 +44,12 @@
<hr> <hr>
<div class='theme-settings'> <div class='theme-settings'>
<h3>{{ theme }}</h3> <h3>{{ theme }}</h3>
{{#hasSystemTheme}}
<div>
<input type='radio' name='theme' id='theme-setting-system' value='system'>
<label for='theme-setting-system'>{{ themeSystem }}</label>
</div>
{{/hasSystemTheme}}
<div> <div>
<input type='radio' name='theme' id='theme-setting-light' value='light'> <input type='radio' name='theme' id='theme-setting-light' value='light'>
<label for='theme-setting-light'>{{ themeLight }}</label> <label for='theme-setting-light'>{{ themeLight }}</label>

View File

@ -1,6 +1,7 @@
/* global window */ /* global window */
const { ipcRenderer } = require('electron'); const { ipcRenderer, remote } = require('electron');
const url = require('url'); const url = require('url');
const i18n = require('./js/modules/i18n'); const i18n = require('./js/modules/i18n');
@ -8,9 +9,31 @@ const config = url.parse(window.location.toString(), true).query;
const { locale } = config; const { locale } = config;
const localeMessages = ipcRenderer.sendSync('locale-data'); const localeMessages = ipcRenderer.sendSync('locale-data');
const { systemPreferences } = remote.require('electron');
window.platform = process.platform;
window.theme = config.theme; window.theme = config.theme;
window.i18n = i18n.setup(locale, localeMessages); window.i18n = i18n.setup(locale, localeMessages);
function setSystemTheme() {
window.systemTheme = systemPreferences.isDarkMode() ? 'dark' : 'light';
}
setSystemTheme();
window.subscribeToSystemThemeChange = fn => {
if (!systemPreferences.subscribeNotification) {
return;
}
systemPreferences.subscribeNotification(
'AppleInterfaceThemeChangedNotification',
() => {
setSystemTheme();
fn();
}
);
};
window.getEnvironment = () => config.environment; window.getEnvironment = () => config.environment;
window.getVersion = () => config.version; window.getVersion = () => config.version;
window.getAppInstance = () => config.appInstance; window.getAppInstance = () => config.appInstance;

View File

@ -257,7 +257,7 @@
"rule": "jQuery-appendTo(", "rule": "jQuery-appendTo(",
"path": "js/permissions_popup_start.js", "path": "js/permissions_popup_start.js",
"line": "window.view.$el.appendTo($body);", "line": "window.view.$el.appendTo($body);",
"lineNumber": 26, "lineNumber": 42,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T18:13:29.628Z", "updated": "2018-09-19T18:13:29.628Z",
"reasonDetail": "Interacting with already-existing DOM nodes" "reasonDetail": "Interacting with already-existing DOM nodes"
@ -284,7 +284,7 @@
"rule": "jQuery-appendTo(", "rule": "jQuery-appendTo(",
"path": "js/settings_start.js", "path": "js/settings_start.js",
"line": " window.view.$el.appendTo($body);", "line": " window.view.$el.appendTo($body);",
"lineNumber": 41, "lineNumber": 57,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T18:13:29.628Z", "updated": "2018-09-19T18:13:29.628Z",
"reasonDetail": "Interacting with already-existing DOM nodes" "reasonDetail": "Interacting with already-existing DOM nodes"
@ -317,7 +317,7 @@
"rule": "DOM-innerHTML", "rule": "DOM-innerHTML",
"path": "js/views/app_view.js", "path": "js/views/app_view.js",
"line": " this.el.innerHTML = '';", "line": " this.el.innerHTML = '';",
"lineNumber": 43, "lineNumber": 55,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-15T00:38:04.183Z", "updated": "2018-09-15T00:38:04.183Z",
"reasonDetail": "Hard-coded string" "reasonDetail": "Hard-coded string"
@ -326,7 +326,7 @@
"rule": "jQuery-append(", "rule": "jQuery-append(",
"path": "js/views/app_view.js", "path": "js/views/app_view.js",
"line": " this.el.append(view.el);", "line": " this.el.append(view.el);",
"lineNumber": 44, "lineNumber": 56,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T18:13:29.628Z", "updated": "2018-09-19T18:13:29.628Z",
"reasonDetail": "Interacting with already-existing DOM nodes" "reasonDetail": "Interacting with already-existing DOM nodes"
@ -335,7 +335,7 @@
"rule": "jQuery-appendTo(", "rule": "jQuery-appendTo(",
"path": "js/views/app_view.js", "path": "js/views/app_view.js",
"line": " this.debugLogView.$el.appendTo(this.el);", "line": " this.debugLogView.$el.appendTo(this.el);",
"lineNumber": 50, "lineNumber": 62,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T18:13:29.628Z", "updated": "2018-09-19T18:13:29.628Z",
"reasonDetail": "Interacting with already-existing DOM nodes" "reasonDetail": "Interacting with already-existing DOM nodes"
@ -1008,7 +1008,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/settings_view.js", "path": "js/views/settings_view.js",
"line": " el: this.$('.audio-notification-setting'),", "line": " el: this.$('.audio-notification-setting'),",
"lineNumber": 97, "lineNumber": 99,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z", "updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -1017,7 +1017,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/settings_view.js", "path": "js/views/settings_view.js",
"line": " el: this.$('.spell-check-setting'),", "line": " el: this.$('.spell-check-setting'),",
"lineNumber": 104, "lineNumber": 106,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z", "updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -1026,7 +1026,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/settings_view.js", "path": "js/views/settings_view.js",
"line": " el: this.$('.menu-bar-setting'),", "line": " el: this.$('.menu-bar-setting'),",
"lineNumber": 111, "lineNumber": 113,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2019-04-08T18:24:35.255Z", "updated": "2019-04-08T18:24:35.255Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -1035,7 +1035,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/settings_view.js", "path": "js/views/settings_view.js",
"line": " el: this.$('.media-permissions'),", "line": " el: this.$('.media-permissions'),",
"lineNumber": 118, "lineNumber": 120,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z", "updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -1044,7 +1044,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/settings_view.js", "path": "js/views/settings_view.js",
"line": " this.$('.sync-setting').append(syncView.el);", "line": " this.$('.sync-setting').append(syncView.el);",
"lineNumber": 124, "lineNumber": 126,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z", "updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -1053,7 +1053,7 @@
"rule": "jQuery-append(", "rule": "jQuery-append(",
"path": "js/views/settings_view.js", "path": "js/views/settings_view.js",
"line": " this.$('.sync-setting').append(syncView.el);", "line": " this.$('.sync-setting').append(syncView.el);",
"lineNumber": 124, "lineNumber": 126,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T18:13:29.628Z", "updated": "2018-09-19T18:13:29.628Z",
"reasonDetail": "Interacting with already-existing DOM nodes" "reasonDetail": "Interacting with already-existing DOM nodes"
@ -1062,25 +1062,25 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/settings_view.js", "path": "js/views/settings_view.js",
"line": " this.$('.sync').text(i18n('syncNow'));", "line": " this.$('.sync').text(i18n('syncNow'));",
"lineNumber": 179,
"reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input"
},
{
"rule": "jQuery-$(",
"path": "js/views/settings_view.js",
"line": " this.$('.sync').attr('disabled', 'disabled');",
"lineNumber": 183, "lineNumber": 183,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z", "updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
}, },
{
"rule": "jQuery-$(",
"path": "js/views/settings_view.js",
"line": " this.$('.sync').attr('disabled', 'disabled');",
"lineNumber": 187,
"reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input"
},
{ {
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/settings_view.js", "path": "js/views/settings_view.js",
"line": " this.$('.synced_at').hide();", "line": " this.$('.synced_at').hide();",
"lineNumber": 195, "lineNumber": 199,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z", "updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -1089,7 +1089,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/settings_view.js", "path": "js/views/settings_view.js",
"line": " this.$('.sync_failed').hide();", "line": " this.$('.sync_failed').hide();",
"lineNumber": 200, "lineNumber": 204,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z", "updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"