MASSIVELY streamlined theme setting process, now EVERYTHING happens in a vuex action "setTheme" instead of several different applyTheme()s scattered around

appearance-tab
Henry Jameson 2 months ago
parent 8a41313bb4
commit c6ccab778f

@ -13,8 +13,7 @@ import VBodyScrollLock from 'src/directives/body_scroll_lock'
import { windowWidth, windowHeight } from '../services/window_utils/window_utils' import { windowWidth, windowHeight } from '../services/window_utils/window_utils'
import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js' import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js'
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js' import { applyConfig } from '../services/style_setter/style_setter.js'
import { applyTheme, applyConfig, tryLoadCache } from '../services/style_setter/style_setter.js'
import FaviconService from '../services/favicon_service/favicon_service.js' import FaviconService from '../services/favicon_service/favicon_service.js'
import { initServiceWorker, updateFocus } from '../services/sw/sw.js' import { initServiceWorker, updateFocus } from '../services/sw/sw.js'
@ -160,8 +159,6 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
copyInstanceOption('showFeaturesPanel') copyInstanceOption('showFeaturesPanel')
copyInstanceOption('hideSitename') copyInstanceOption('hideSitename')
copyInstanceOption('sidebarRight') copyInstanceOption('sidebarRight')
return store.dispatch('setTheme', config.theme)
} }
const getTOS = async ({ store }) => { const getTOS = async ({ store }) => {
@ -352,29 +349,7 @@ const afterStoreSetup = async ({ store, i18n }) => {
store.dispatch('setInstanceOption', { name: 'server', value: server }) store.dispatch('setInstanceOption', { name: 'server', value: server })
await setConfig({ store }) await setConfig({ store })
await store.dispatch('setTheme')
const { customTheme, customThemeSource, forceThemeRecompilation, themeDebug } = store.state.config
const { theme } = store.state.instance
const customThemePresent = customThemeSource || customTheme
console.log('DEBUG INITIAL', themeDebug, forceThemeRecompilation)
if (!forceThemeRecompilation && !themeDebug && tryLoadCache()) {
store.commit('setThemeApplied')
} else {
if (customThemePresent) {
if (customThemeSource && customThemeSource.themeEngineVersion === CURRENT_VERSION) {
applyTheme(customThemeSource, () => {}, themeDebug)
} else {
applyTheme(customTheme, () => {}, themeDebug)
}
store.commit('setThemeApplied')
} else if (theme) {
// do nothing, it will load asynchronously
} else {
console.error('Failed to load any theme!')
}
}
applyConfig(store.state.config) applyConfig(store.state.config)

@ -29,6 +29,11 @@ const AppearanceTab = {
key: mode, key: mode,
value: i - 1, value: i - 1,
label: this.$t(`settings.forced_roundness_mode_${mode}`) label: this.$t(`settings.forced_roundness_mode_${mode}`)
})),
underlayOverrideModes: ['none', 'opaque', 'transparent'].map((mode, i) => ({
key: mode,
value: mode,
label: this.$t(`settings.underlay_override_mode_${mode}`)
})) }))
} }
}, },

@ -182,6 +182,15 @@
{{ $t('settings.force_interface_roundness') }} {{ $t('settings.force_interface_roundness') }}
</ChoiceSetting> </ChoiceSetting>
</li> </li>
<li>
<ChoiceSetting
id="underlayOverride"
path="theme3hacks.underlay"
:options="underlayOverrideModes"
>
{{ $t('settings.underlay_overrides') }}
</ChoiceSetting>
</li>
<li v-if="instanceWallpaperUsed"> <li v-if="instanceWallpaperUsed">
<BooleanSetting path="hideInstanceWallpaper"> <BooleanSetting path="hideInstanceWallpaper">
{{ $t('settings.hide_wallpaper') }} {{ $t('settings.hide_wallpaper') }}

@ -502,6 +502,7 @@ export default {
this.$store.dispatch('setOption', { this.$store.dispatch('setOption', {
name: 'customTheme', name: 'customTheme',
value: { value: {
ignore: true,
themeFileVersion: this.selectedVersion, themeFileVersion: this.selectedVersion,
themeEngineVersion: CURRENT_VERSION, themeEngineVersion: CURRENT_VERSION,
...this.previewTheme ...this.previewTheme
@ -699,13 +700,13 @@ export default {
} }
}, },
updateTheme3Preview () { updateTheme3Preview () {
console.log(this.previewTheme)
const theme2 = convertTheme2To3(this.previewTheme) const theme2 = convertTheme2To3(this.previewTheme)
const theme3 = init({ const theme3 = init({
extraRuleset: theme2, inputRuleset: theme2,
ultimateBackgroundColor: '#000000', ultimateBackgroundColor: '#000000',
liteMode: true liteMode: true
}) })
this.themeV3Preview = getCssRules(theme3.eager) this.themeV3Preview = getCssRules(theme3.eager)
.map(x => { .map(x => {
if (x.startsWith('html')) { if (x.startsWith('html')) {
@ -722,7 +723,7 @@ export default {
watch: { watch: {
currentRadii () { currentRadii () {
try { try {
this.previewTheme.radii = generateRadii({ radii: this.currentRadii }).theme this.previewTheme.radii = generateRadii({ radii: this.currentRadii }).theme.radii
this.radiiInvalid = false this.radiiInvalid = false
} catch (e) { } catch (e) {
this.radiiInvalid = true this.radiiInvalid = true
@ -744,7 +745,7 @@ export default {
fontsLocal: { fontsLocal: {
handler () { handler () {
try { try {
this.previewTheme.fonts = generateFonts({ fonts: this.fontsLocal }).theme this.previewTheme.fonts = generateFonts({ fonts: this.fontsLocal }).theme.fonts
this.fontsInvalid = false this.fontsInvalid = false
} catch (e) { } catch (e) {
this.fontsInvalid = true this.fontsInvalid = true

@ -1,10 +1,21 @@
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
import { setPreset, applyTheme, applyConfig } from '../services/style_setter/style_setter.js' import { applyConfig } from '../services/style_setter/style_setter.js'
import messages from '../i18n/messages' import messages from '../i18n/messages'
import { set } from 'lodash' import { set } from 'lodash'
import localeService from '../services/locale/locale.service.js' import localeService from '../services/locale/locale.service.js'
const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage' const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage'
const APPEARANCE_SETTINGS_KEYS = new Set([
'sidebarColumnWidth',
'contentColumnWidth',
'notifsColumnWidth',
'textSize',
'navbarSize',
'panelHeaderSize',
'forcedRoundness',
'emojiSize',
'emojiReactionsScale'
])
const browserLocale = (window.navigator.language || 'en').split('-')[0] const browserLocale = (window.navigator.language || 'en').split('-')[0]
@ -81,6 +92,11 @@ export const defaultState = {
chatMention: true, chatMention: true,
polls: true polls: true
}, },
palette: null,
theme3hacks: {
underlay: 'none',
badgeColor: null
},
webPushNotifications: false, webPushNotifications: false,
webPushAlwaysShowNotifications: false, webPushAlwaysShowNotifications: false,
muteWords: [], muteWords: [],
@ -185,7 +201,6 @@ const config = {
applyConfig(state) applyConfig(state)
}, },
setOption (state, { name, value }) { setOption (state, { name, value }) {
console.log('SET OPTION', state, name, value)
set(state, name, value) set(state, name, value)
}, },
setHighlight (state, { user, color, type }) { setHighlight (state, { user, color, type }) {
@ -261,30 +276,23 @@ const config = {
} }
} else { } else {
commit('setOption', { name, value }) commit('setOption', { name, value })
if (
name.startsWith('theme3hacks') ||
APPEARANCE_SETTINGS_KEYS.has(name)
) {
applyConfig(state)
}
switch (name) { switch (name) {
case 'theme': case 'theme':
setPreset(value) dispatch('setTheme', { themeName: value, recompile: true })
break
case 'sidebarColumnWidth':
case 'contentColumnWidth':
case 'notifsColumnWidth':
case 'textSize':
case 'navbarSize':
case 'panelHeaderSize':
case 'forcedRoundness':
case 'emojiSize':
case 'emojiReactionsScale':
applyConfig(state)
break break
case 'customTheme': case 'customTheme':
case 'customThemeSource': { case 'customThemeSource': {
const { themeDebug } = state if (!value.ignore) dispatch('setTheme', { themeData: value })
applyTheme(value, () => {}, themeDebug)
break break
} }
case 'themeDebug': { case 'themeDebug': {
const { customTheme, customThemeSource } = state dispatch('setTheme', { recompile: true })
applyTheme(customTheme || customThemeSource, () => {}, value)
break break
} }
case 'interfaceLanguage': case 'interfaceLanguage':

@ -1,5 +1,6 @@
import { getPreset, applyTheme } from '../services/style_setter/style_setter.js' import { getPreset, applyTheme, tryLoadCache } from '../services/style_setter/style_setter.js'
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js' import { CURRENT_VERSION, generatePreset } from 'src/services/theme_data/theme_data.service.js'
import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js'
import apiService from '../services/api/api.service.js' import apiService from '../services/api/api.service.js'
import { instanceDefaultProperties } from './config.js' import { instanceDefaultProperties } from './config.js'
import { langCodeToCldrName, ensureFinalFallback } from '../i18n/languages.js' import { langCodeToCldrName, ensureFinalFallback } from '../i18n/languages.js'
@ -286,9 +287,6 @@ const instance = {
dispatch('initializeSocket') dispatch('initializeSocket')
} }
break break
case 'theme':
dispatch('setTheme', value)
break
} }
}, },
async getStaticEmoji ({ commit }) { async getStaticEmoji ({ commit }) {
@ -378,25 +376,86 @@ const instance = {
} }
}, },
setTheme ({ commit, rootState }, themeName) { setTheme ({ commit, state, rootState }, { themeName, themeData, recompile } = {}) {
commit('setInstanceOption', { name: 'theme', value: themeName }) // const {
getPreset(themeName) // themeApplied
.then(themeData => { // } = rootState.interface
commit('setInstanceOption', { name: 'themeData', value: themeData }) const {
// No need to apply theme if there's user theme already theme: instanceThemeName
const { customTheme, themeDebug } = rootState.config } = state
const { themeApplied } = rootState.interface
if (customTheme || themeApplied) return const {
customTheme: userThemeSnapshot,
// New theme presets don't have 'theme' property, they use 'source' customThemeSource: userThemeSource,
const themeSource = themeData.source forceThemeRecompilation,
if (!themeData.theme || (themeSource && themeSource.themeEngineVersion === CURRENT_VERSION)) { themeDebug
applyTheme(themeSource, null, themeDebug) } = rootState.config
const forceRecompile = forceThemeRecompilation || recompile
// If we're not not forced to recompile try using
// cache (tryLoadCache return true if load successful)
if (!forceRecompile && !themeDebug && tryLoadCache()) {
commit('setThemeApplied')
}
const normalizeThemeData = (themeData) => {
console.log('NORMAL', themeData)
if (themeData.themeFileVerison === 1) {
return generatePreset(themeData).theme
}
// New theme presets don't have 'theme' property, they use 'source'
const themeSource = themeData.source
let out // shout, shout let it all out
if (!themeData.theme || (themeSource && themeSource.themeEngineVersion === CURRENT_VERSION)) {
out = themeSource || themeData
} else {
out = themeData.theme
}
// generatePreset here basically creates/updates "snapshot",
// while also fixing the 2.2 -> 2.3 colors/shadows/etc
return generatePreset(out).theme
}
let promise = null
if (themeName) {
// commit('setInstanceOption', { name: 'theme', value: themeName })
promise = getPreset(themeName)
.then(themeData => {
// commit('setInstanceOption', { name: 'themeData', value: themeData })
return normalizeThemeData(themeData)
})
} else if (themeData) {
promise = Promise.resolve(normalizeThemeData(themeData))
} else {
if (userThemeSource || userThemeSnapshot) {
if (userThemeSource && userThemeSource.themeEngineVersion === CURRENT_VERSION) {
promise = Promise.resolve(normalizeThemeData(userThemeSource))
} else { } else {
applyTheme(themeData.theme, null, themeDebug) promise = Promise.resolve(normalizeThemeData(userThemeSnapshot))
} }
commit('setThemeApplied') } else if (instanceThemeName) {
promise = getPreset(themeName).then(themeData => normalizeThemeData(themeData))
}
}
promise
.then(realThemeData => {
console.log('FR FR 1', realThemeData)
const ruleset = convertTheme2To3(realThemeData)
console.log('FR FR 2', ruleset)
applyTheme(
ruleset,
() => commit('setThemeApplied'),
themeDebug
)
}) })
return promise
}, },
fetchEmoji ({ dispatch, state }) { fetchEmoji ({ dispatch, state }) {
if (!state.customEmojiFetched) { if (!state.customEmojiFetched) {

@ -1,7 +1,5 @@
import { hex2rgb } from '../color_convert/color_convert.js' import { hex2rgb } from '../color_convert/color_convert.js'
import { generatePreset } from '../theme_data/theme_data.service.js'
import { init, getEngineChecksum } from '../theme_data/theme_data_3.service.js' import { init, getEngineChecksum } from '../theme_data/theme_data_3.service.js'
import { convertTheme2To3 } from '../theme_data/theme2_to_theme3.js'
import { getCssRules } from '../theme_data/css_utils.js' import { getCssRules } from '../theme_data/css_utils.js'
import { defaultState } from '../../modules/config.js' import { defaultState } from '../../modules/config.js'
import { chunk } from 'lodash' import { chunk } from 'lodash'
@ -45,30 +43,20 @@ const adoptStyleSheets = (styles) => {
// is nothing to do here. // is nothing to do here.
} }
export const generateTheme = async (input, callbacks, debug) => { export const generateTheme = async (inputRuleset, callbacks, debug) => {
const { const {
onNewRule = (rule, isLazy) => {}, onNewRule = (rule, isLazy) => {},
onLazyFinished = () => {}, onLazyFinished = () => {},
onEagerFinished = () => {} onEagerFinished = () => {}
} = callbacks } = callbacks
let extraRules
if (input.themeFileVersion === 1) {
extraRules = convertTheme2To3(input)
} else {
const { theme } = generatePreset(input)
extraRules = convertTheme2To3(theme)
}
// Assuming that "worst case scenario background" is panel background since it's the most likely one // Assuming that "worst case scenario background" is panel background since it's the most likely one
const themes3 = init({ const themes3 = init({
extraRules, inputRuleset,
ultimateBackgroundColor: extraRules[0].directives['--bg'].split('|')[1].trim(), ultimateBackgroundColor: inputRuleset[0].directives['--bg'].split('|')[1].trim(),
debug debug
}) })
console.log('DEBUG 2 IS', debug)
getCssRules(themes3.eager, debug).forEach(rule => { getCssRules(themes3.eager, debug).forEach(rule => {
// Hacks to support multiple selectors on same component // Hacks to support multiple selectors on same component
if (rule.match(/::-webkit-scrollbar-button/)) { if (rule.match(/::-webkit-scrollbar-button/)) {
@ -162,8 +150,6 @@ export const applyTheme = async (input, onFinish = (data) => {}, debug) => {
const eagerStyles = createStyleSheet(EAGER_STYLE_ID) const eagerStyles = createStyleSheet(EAGER_STYLE_ID)
const lazyStyles = createStyleSheet(LAZY_STYLE_ID) const lazyStyles = createStyleSheet(LAZY_STYLE_ID)
console.log('DEBUG IS', debug)
const { lazyProcessFunc } = await generateTheme( const { lazyProcessFunc } = await generateTheme(
input, input,
{ {
@ -216,7 +202,6 @@ const extractStyleConfig = ({
textSize textSize
} }
console.log(forcedRoundness)
switch (forcedRoundness) { switch (forcedRoundness) {
case 'disable': case 'disable':
break break
@ -325,5 +310,3 @@ export const getPreset = (val) => {
return { theme: data, source: theme.source } return { theme: data, source: theme.source }
}) })
} }
export const setPreset = (val) => getPreset(val).then(data => applyTheme(data))

@ -265,6 +265,7 @@ export const convertTheme2To3 = (data) => {
const newRules = [] const newRules = []
Object.keys(data.fonts || {}).forEach(key => { Object.keys(data.fonts || {}).forEach(key => {
if (!fontsKeys.has(key)) return if (!fontsKeys.has(key)) return
if (!data.fonts[key]) return
const originalFont = data.fonts[key].family const originalFont = data.fonts[key].family
const rule = { source: '2to3' } const rule = { source: '2to3' }

@ -150,12 +150,13 @@ const ruleToSelector = genericRuleToSelector(components)
export const getEngineChecksum = () => engineChecksum export const getEngineChecksum = () => engineChecksum
export const init = ({ export const init = ({
extraRuleset, inputRuleset,
ultimateBackgroundColor, ultimateBackgroundColor,
debug = false, debug = false,
liteMode = false, liteMode = false,
rootComponentName = 'Root' rootComponentName = 'Root'
}) => { }) => {
if (!inputRuleset) throw new Error('Ruleset is null or undefined!')
const staticVars = {} const staticVars = {}
const stacked = {} const stacked = {}
const computed = {} const computed = {}
@ -164,7 +165,7 @@ export const init = ({
...Object.values(components) ...Object.values(components)
.map(c => (c.defaultRules || []).map(r => ({ component: c.name, ...r, source: 'Built-in' }))) .map(c => (c.defaultRules || []).map(r => ({ component: c.name, ...r, source: 'Built-in' })))
.reduce((acc, arr) => [...acc, ...arr], []), .reduce((acc, arr) => [...acc, ...arr], []),
...extraRuleset ...inputRuleset
].map(rule => { ].map(rule => {
normalizeCombination(rule) normalizeCombination(rule)
let currentParent = rule.parent let currentParent = rule.parent

@ -66,7 +66,7 @@ describe('Theme Data 3', () => {
this.timeout(5000) this.timeout(5000)
it('Test initialization without anything', () => { it('Test initialization without anything', () => {
const out = init([], '#DEADAF') const out = init({ ruleset: [], ultimateBackgroundColor: '#DEADAF' })
expect(out).to.have.property('eager') expect(out).to.have.property('eager')
expect(out).to.have.property('lazy') expect(out).to.have.property('lazy')
@ -85,13 +85,16 @@ describe('Theme Data 3', () => {
}) })
it('Test initialization with a basic palette', () => { it('Test initialization with a basic palette', () => {
const out = init([{ const out = init({
component: 'Root', ruleset: [{
directives: { component: 'Root',
'--bg': 'color | #008080', directives: {
'--fg': 'color | #00C0A0' '--bg': 'color | #008080',
} '--fg': 'color | #00C0A0'
}], '#DEADAF') }
}],
ultimateBackgroundColor: '#DEADAF'
})
expect(out.staticVars).to.have.property('bg').equal('#008080') expect(out.staticVars).to.have.property('bg').equal('#008080')
expect(out.staticVars).to.have.property('fg').equal('#00C0A0') expect(out.staticVars).to.have.property('fg').equal('#00C0A0')
@ -105,17 +108,20 @@ describe('Theme Data 3', () => {
}) })
it('Test initialization with opacity', () => { it('Test initialization with opacity', () => {
const out = init([{ const out = init({
component: 'Root', ruleset: [{
directives: { component: 'Root',
'--bg': 'color | #008080' directives: {
} '--bg': 'color | #008080'
}, { }
component: 'Panel', }, {
directives: { component: 'Panel',
opacity: 0.5 directives: {
} opacity: 0.5
}], '#DEADAF') }
}],
ultimateBackgroundColor: '#DEADAF'
})
expect(out.staticVars).to.have.property('bg').equal('#008080') expect(out.staticVars).to.have.property('bg').equal('#008080')

Loading…
Cancel
Save