System theme preference (#1800)

* Add system preference to theme-settings

* Add watching logic to check for dark theme settings

* Add en system theme translation

* Add explicit darkTheme option to browserWindow

* Remove unnecessary themeSource line

* Fix lint errors

* Move to using main process messaging for theme change

* Add system dataset selectors for themes

* Fix lint errors

* Change system theme to system default

* Use system default for deciding background color of newWindow

* Add baseTheme to state persistance

* Use baseTheme on browserWindow creation

* Fix lint errors

* Improve window background logic

* Catch settingsDb errors

* Remove dark flash on light themes

* Fix lint issues

* Fix system default sync on multiple windows

* Load database on each new window

* Fix lint errors

* Update compatibility for shared electron storage

* Remove unused console log

* Revert unnecessary changes

* Fix window maximize white flash

* Fix handleBaseTheme usage

* Use data-system-theme instead of data-theme

* Revert window maximize changes

* Fix theme flash on new window open
This commit is contained in:
vallode 2022-05-11 16:30:40 +02:00 committed by GitHub
parent ea04ef0a90
commit 3dcea52b75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 70 additions and 10 deletions

View File

@ -12,6 +12,7 @@ const IpcChannels = {
START_POWER_SAVE_BLOCKER: 'start-power-save-blocker', START_POWER_SAVE_BLOCKER: 'start-power-save-blocker',
CREATE_NEW_WINDOW: 'create-new-window', CREATE_NEW_WINDOW: 'create-new-window',
OPEN_IN_EXTERNAL_PLAYER: 'open-in-external-player', OPEN_IN_EXTERNAL_PLAYER: 'open-in-external-player',
NATIVE_THEME_UPDATE: 'native-theme-update',
DB_SETTINGS: 'db-settings', DB_SETTINGS: 'db-settings',
DB_HISTORY: 'db-history', DB_HISTORY: 'db-history',

View File

@ -27,6 +27,10 @@ class Settings {
return db.settings.findOne({ _id: 'bounds' }) return db.settings.findOne({ _id: 'bounds' })
} }
static _findTheme() {
return db.settings.findOne({ _id: 'baseTheme' })
}
static _updateBounds(value) { static _updateBounds(value) {
return db.settings.update({ _id: 'bounds' }, { _id: 'bounds', value }, { upsert: true }) return db.settings.update({ _id: 'bounds' }, { _id: 'bounds', value }, { upsert: true })
} }

View File

@ -16,7 +16,7 @@
<% } %> <% } %>
</head> </head>
<body class="dark mainRed secBlue"> <body>
<div id="app"></div> <div id="app"></div>
<!-- Set `__static` path to static files in production --> <!-- Set `__static` path to static files in production -->
<script> <script>

View File

@ -1,6 +1,6 @@
import { import {
app, BrowserWindow, dialog, Menu, ipcMain, app, BrowserWindow, dialog, Menu, ipcMain,
powerSaveBlocker, screen, session, shell powerSaveBlocker, screen, session, shell, nativeTheme
} from 'electron' } from 'electron'
import path from 'path' import path from 'path'
import cp from 'child_process' import cp from 'child_process'
@ -35,6 +35,7 @@ function runApp() {
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true' process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true'
const isDev = process.env.NODE_ENV === 'development' const isDev = process.env.NODE_ENV === 'development'
const isDebug = process.argv.includes('--debug') const isDebug = process.argv.includes('--debug')
let mainWindow let mainWindow
let startupUrl let startupUrl
@ -172,11 +173,33 @@ function runApp() {
} }
async function createWindow({ replaceMainWindow = true, windowStartupUrl = null, showWindowNow = false } = { }) { async function createWindow({ replaceMainWindow = true, windowStartupUrl = null, showWindowNow = false } = { }) {
// Syncing new window background to theme choice.
const windowBackground = await baseHandlers.settings._findTheme().then(({ value }) => {
switch (value) {
case 'dark':
return '#212121'
case 'light':
return '#f1f1f1'
case 'black':
return '#000000'
case 'dracula':
return '#282a36'
case 'system':
default:
return nativeTheme.shouldUseDarkColors ? '#212121' : '#f1f1f1'
}
}).catch((error) => {
console.log(error)
// Default to nativeTheme settings if nothing is found.
return nativeTheme.shouldUseDarkColors ? '#212121' : '#f1f1f1'
})
/** /**
* Initial window options * Initial window options
*/ */
const commonBrowserWindowOptions = { const commonBrowserWindowOptions = {
backgroundColor: '#212121', backgroundColor: windowBackground,
darkTheme: nativeTheme.shouldUseDarkColors,
icon: isDev icon: isDev
? path.join(__dirname, '../../_icons/iconColor.png') ? path.join(__dirname, '../../_icons/iconColor.png')
/* eslint-disable-next-line */ /* eslint-disable-next-line */
@ -191,6 +214,7 @@ function runApp() {
contextIsolation: false contextIsolation: false
} }
} }
const newWindow = new BrowserWindow( const newWindow = new BrowserWindow(
Object.assign( Object.assign(
{ {
@ -241,6 +265,7 @@ function runApp() {
height: bounds.height height: bounds.height
}) })
} }
if (maximized) { if (maximized) {
newWindow.maximize() newWindow.maximize()
} }
@ -345,6 +370,14 @@ function runApp() {
app.quit() app.quit()
}) })
nativeTheme.on('updated', () => {
const allWindows = BrowserWindow.getAllWindows()
allWindows.forEach((window) => {
window.webContents.send(IpcChannels.NATIVE_THEME_UPDATE, nativeTheme.shouldUseDarkColors)
})
})
ipcMain.on(IpcChannels.ENABLE_PROXY, (_, url) => { ipcMain.on(IpcChannels.ENABLE_PROXY, (_, url) => {
console.log(url) console.log(url)
session.defaultSession.setProxy({ session.defaultSession.setProxy({

View File

@ -12,6 +12,7 @@ import FtProgressBar from './components/ft-progress-bar/ft-progress-bar.vue'
import $ from 'jquery' import $ from 'jquery'
import { marked } from 'marked' import { marked } from 'marked'
import Parser from 'rss-parser' import Parser from 'rss-parser'
import { IpcChannels } from '../constants'
let ipcRenderer = null let ipcRenderer = null
@ -113,6 +114,10 @@ export default Vue.extend({
return this.$store.getters.getSecColor return this.$store.getters.getSecColor
}, },
systemTheme: function () {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
},
externalLinkOpeningPromptNames: function () { externalLinkOpeningPromptNames: function () {
return [ return [
this.$t('Yes'), this.$t('Yes'),
@ -140,11 +145,13 @@ export default Vue.extend({
} }
}, },
created () { created () {
this.checkThemeSettings() this.setBodyTheme()
this.setWindowTitle() this.setWindowTitle()
}, },
mounted: function () { mounted: function () {
this.grabUserSettings().then(async () => { this.grabUserSettings().then(async () => {
this.checkThemeSettings()
await this.fetchInvidiousInstances({ isDev: this.isDev }) await this.fetchInvidiousInstances({ isDev: this.isDev })
if (this.defaultInvidiousInstance === '') { if (this.defaultInvidiousInstance === '') {
await this.setRandomCurrentInvidiousInstance() await this.setRandomCurrentInvidiousInstance()
@ -161,6 +168,7 @@ export default Vue.extend({
this.activateKeyboardShortcuts() this.activateKeyboardShortcuts()
this.openAllLinksExternally() this.openAllLinksExternally()
this.enableOpenUrl() this.enableOpenUrl()
this.watchSystemTheme()
await this.checkExternalPlayer() await this.checkExternalPlayer()
} }
@ -191,9 +199,8 @@ export default Vue.extend({
updateTheme: function (theme) { updateTheme: function (theme) {
console.group('updateTheme') console.group('updateTheme')
console.log('Theme: ', theme) console.log('Theme: ', theme)
const className = `${theme.baseTheme} main${theme.mainColor} sec${theme.secColor}` document.body.className = `${theme.baseTheme} main${theme.mainColor} sec${theme.secColor}`
const body = document.getElementsByTagName('body')[0] document.body.dataset.systemTheme = this.systemTheme
body.className = className
console.groupEnd() console.groupEnd()
}, },
@ -441,6 +448,16 @@ export default Vue.extend({
}) })
}, },
/**
* Linux fix for dynamically updating theme preference, this works on
* all systems running the electron app.
*/
watchSystemTheme: function () {
ipcRenderer.on(IpcChannels.NATIVE_THEME_UPDATE, (event, shouldUseDarkColors) => {
document.body.dataset.systemTheme = shouldUseDarkColors ? 'dark' : 'light'
})
},
enableOpenUrl: function () { enableOpenUrl: function () {
ipcRenderer.on('openUrl', (event, url) => { ipcRenderer.on('openUrl', (event, url) => {
if (url) { if (url) {

View File

@ -1,5 +1,6 @@
<template> <template>
<div <div
v-if="dataReady"
id="app" id="app"
:class="{ :class="{
hideOutlines: hideOutlines, hideOutlines: hideOutlines,

View File

@ -29,6 +29,7 @@ export default Vue.extend({
'no' 'no'
], ],
baseThemeValues: [ baseThemeValues: [
'system',
'light', 'light',
'dark', 'dark',
'black', 'black',
@ -111,6 +112,7 @@ export default Vue.extend({
baseThemeNames: function () { baseThemeNames: function () {
return [ return [
this.$t('Settings.Theme Settings.Base Theme.System Default'),
this.$t('Settings.Theme Settings.Base Theme.Light'), this.$t('Settings.Theme Settings.Base Theme.Light'),
this.$t('Settings.Theme Settings.Base Theme.Dark'), this.$t('Settings.Theme Settings.Base Theme.Dark'),
this.$t('Settings.Theme Settings.Base Theme.Black'), this.$t('Settings.Theme Settings.Base Theme.Black'),

View File

@ -167,7 +167,7 @@ const state = {
barColor: false, barColor: false,
checkForBlogPosts: true, checkForBlogPosts: true,
checkForUpdates: true, checkForUpdates: true,
baseTheme: 'dark', baseTheme: 'system',
mainColor: 'Red', mainColor: 'Red',
secColor: 'Blue', secColor: 'Blue',
defaultCaptionSettings: '{}', defaultCaptionSettings: '{}',

View File

@ -1,4 +1,4 @@
.light { .system[data-system-theme*='light'], .light {
--primary-text-color: #212121; --primary-text-color: #212121;
--secondary-text-color: #424242; --secondary-text-color: #424242;
--tertiary-text-color: #757575; --tertiary-text-color: #757575;
@ -22,7 +22,7 @@
--logo-text: url("~../../_icons/textColorSmall.png"); --logo-text: url("~../../_icons/textColorSmall.png");
} }
.dark { .system[data-system-theme*='dark'], .dark {
--primary-text-color: #EEEEEE; --primary-text-color: #EEEEEE;
--secondary-text-color: #ddd; --secondary-text-color: #ddd;
--tertiary-text-color: #999; --tertiary-text-color: #999;

View File

@ -167,6 +167,7 @@ Settings:
Base Theme: Base Theme Base Theme: Base Theme
Black: Black Black: Black
Dark: Dark Dark: Dark
System Default: System Default
Light: Light Light: Light
Dracula: Dracula Dracula: Dracula
Main Color Theme: Main Color Theme:

View File

@ -162,6 +162,7 @@ Settings:
Base Theme: 'Base theme' Base Theme: 'Base theme'
Black: 'Black' Black: 'Black'
Dark: 'Dark' Dark: 'Dark'
System Default: 'System Default'
Light: 'Light' Light: 'Light'
Dracula: 'Dracula' Dracula: 'Dracula'
Main Color Theme: Main Color Theme: