From dc22386599c77fdd5a8b88ccfd167cff36d14c67 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 26 Feb 2024 21:37:40 +0200 Subject: [PATCH] optimization and refactoring, rules are first flattened and then processed, letting us to set individual rules as "lazy" --- src/boot/after_store.js | 17 +- src/services/style_setter/style_setter.js | 26 +- .../theme_data/theme_data_3.service.js | 450 +++++++++--------- 3 files changed, 243 insertions(+), 250 deletions(-) diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 84fea954..c5de888b 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -327,17 +327,14 @@ const setConfig = async ({ store }) => { } const checkOAuthToken = async ({ store }) => { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - if (store.getters.getUserToken()) { - try { - await store.dispatch('loginUser', store.getters.getUserToken()) - } catch (e) { - console.error(e) - } + if (store.getters.getUserToken()) { + try { + await store.dispatch('loginUser', store.getters.getUserToken()) + } catch (e) { + console.error(e) } - resolve() - }) + } + return Promise.resolve() } const afterStoreSetup = async ({ store, i18n }) => { diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 1cda7213..b1722295 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -5,23 +5,16 @@ import { convertTheme2To3 } from '../theme_data/theme2_to_theme3.js' import { getCssRules } from '../theme_data/css_utils.js' import { defaultState } from '../../modules/config.js' -export const applyTheme = (input) => { +export const applyTheme = async (input) => { let extraRules if (input.themeType !== 1) { - const t0 = performance.now() const { theme } = generatePreset(input) - const t1 = performance.now() - console.debug('Themes 2 initialization took ' + (t1 - t0) + 'ms') extraRules = convertTheme2To3(theme) } else { - console.debug(input) extraRules = convertTheme2To3(input) } - const t1 = performance.now() const themes3 = init(extraRules, '#FFFFFF') - const t2 = performance.now() - console.debug('Themes 3 (eager) initialization took ' + (t2 - t1) + 'ms') const head = document.head const body = document.body body.classList.add('hidden') @@ -47,14 +40,21 @@ export const applyTheme = (input) => { styleSheet.insertRule(rule, 'index-max') } }) + body.classList.remove('hidden') - themes3.lazy.then(lazyRules => { - getCssRules(lazyRules, themes3.staticVars).forEach(rule => { - styleSheet.insertRule(rule, 'index-max') + + setTimeout(() => { + themes3.lazy().then(lazyRules => { + const t2 = performance.now() + getCssRules(lazyRules, themes3.staticVars).forEach(rule => { + styleSheet.insertRule(rule, 'index-max') + }) + const t3 = performance.now() + console.debug('Themes 3 finalization (lazy) took ' + (t3 - t2) + 'ms') }) - const t3 = performance.now() - console.debug('Themes 3 finalization (lazy) took ' + (t3 - t2) + 'ms') }) + + return Promise.resolve() } const configColumns = ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth, emojiReactionsScale }) => diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index 13caef9e..926988f7 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -151,6 +151,7 @@ export const init = (extraRuleset, ultimateBackgroundColor) => { const eagerRules = [] const lazyRules = [] + const lazyPromises = [] const rulesetUnsorted = [ ...Object.values(components) @@ -187,25 +188,210 @@ export const init = (extraRuleset, ultimateBackgroundColor) => { const virtualComponents = new Set(Object.values(components).filter(c => c.virtual).map(c => c.name)) - let counter = 0 - const promises = [] - const processInnerComponent = (component, rules, parent) => { + const processCombination = (combination, rules) => { const addRule = (rule) => { rules.push(rule) } - const parentSelector = ruleToSelector(parent, true) - // const parentList = parent ? unroll(parent).reverse().map(c => c.component) : [] - // if (!component.virtual) { - // const path = [...parentList, component.name].join(' > ') - // console.log('Component ' + path + ' process starting') - // } - // const t0 = performance.now() + const selector = ruleToSelector(combination, true) + const cssSelector = ruleToSelector(combination) + + const parentSelector = selector.split(/ /g).slice(0, -1).join(' ') + const soloSelector = selector.split(/ /g).slice(-1)[0] + + const lowerLevelSelector = parentSelector + const lowerLevelBackground = computed[lowerLevelSelector]?.background + const lowerLevelVirtualDirectives = computed[lowerLevelSelector]?.virtualDirectives + const lowerLevelVirtualDirectivesRaw = computed[lowerLevelSelector]?.virtualDirectivesRaw + + const dynamicVars = computed[selector] || { + lowerLevelBackground, + lowerLevelVirtualDirectives, + lowerLevelVirtualDirectivesRaw + } + + // Inheriting all of the applicable rules + const existingRules = ruleset.filter(findRules(combination)) + const computedDirectives = existingRules.map(r => r.directives).reduce((acc, directives) => ({ ...acc, ...directives }), {}) + const computedRule = { + ...combination, + directives: computedDirectives + } + + computed[selector] = computed[selector] || {} + computed[selector].computedRule = computedRule + computed[selector].dynamicVars = dynamicVars + + if (virtualComponents.has(combination.component)) { + const virtualName = [ + '--', + combination.component.toLowerCase(), + combination.variant === 'normal' + ? '' + : combination.variant[0].toUpperCase() + combination.variant.slice(1).toLowerCase(), + ...combination.state.filter(x => x !== 'normal').toSorted().map(state => state[0].toUpperCase() + state.slice(1).toLowerCase()) + ].join('') + + let inheritedTextColor = computedDirectives.textColor + let inheritedTextAuto = computedDirectives.textAuto + let inheritedTextOpacity = computedDirectives.textOpacity + let inheritedTextOpacityMode = computedDirectives.textOpacityMode + const lowerLevelTextSelector = [...selector.split(/ /g).slice(0, -1), soloSelector].join(' ') + const lowerLevelTextRule = computed[lowerLevelTextSelector] + + if (inheritedTextColor == null || inheritedTextOpacity == null || inheritedTextOpacityMode == null) { + inheritedTextColor = computedDirectives.textColor ?? lowerLevelTextRule.textColor + inheritedTextAuto = computedDirectives.textAuto ?? lowerLevelTextRule.textAuto + inheritedTextOpacity = computedDirectives.textOpacity ?? lowerLevelTextRule.textOpacity + inheritedTextOpacityMode = computedDirectives.textOpacityMode ?? lowerLevelTextRule.textOpacityMode + } + + const newTextRule = { + ...computedRule, + directives: { + ...computedRule.directives, + textColor: inheritedTextColor, + textAuto: inheritedTextAuto ?? 'preserve', + textOpacity: inheritedTextOpacity, + textOpacityMode: inheritedTextOpacityMode + } + } + + dynamicVars.inheritedBackground = lowerLevelBackground + dynamicVars.stacked = convert(stacked[lowerLevelSelector]).rgb + + const intendedTextColor = convert(findColor(inheritedTextColor, { dynamicVars, staticVars })).rgb + const textColor = newTextRule.directives.textAuto === 'no-auto' + ? intendedTextColor + : getTextColor( + convert(stacked[lowerLevelSelector]).rgb, + intendedTextColor, + newTextRule.directives.textAuto === 'preserve' + ) + + // Updating previously added rule + const earlyLowerLevelRules = rules.filter(findRules(combination.parent, true)) + const earlyLowerLevelRule = earlyLowerLevelRules.slice(-1)[0] + + const virtualDirectives = earlyLowerLevelRule.virtualDirectives || {} + const virtualDirectivesRaw = earlyLowerLevelRule.virtualDirectivesRaw || {} + + // Storing color data in lower layer to use as custom css properties + virtualDirectives[virtualName] = getTextColorAlpha(newTextRule.directives, textColor, dynamicVars) + virtualDirectivesRaw[virtualName] = textColor + earlyLowerLevelRule.virtualDirectives = virtualDirectives + earlyLowerLevelRule.virtualDirectivesRaw = virtualDirectivesRaw + computed[lowerLevelSelector].virtualDirectives = virtualDirectives + computed[lowerLevelSelector].virtualDirectivesRaw = virtualDirectivesRaw + } else { + computed[selector] = computed[selector] || {} + + // TODO: DEFAULT TEXT COLOR + const lowerLevelStackedBackground = stacked[lowerLevelSelector] || convert(ultimateBackgroundColor).rgb + + if (computedDirectives.background) { + let inheritRule = null + const variantRules = ruleset.filter( + findRules({ + component: combination.component, + variant: combination.variant, + parent: combination.parent + }) + ) + const lastVariantRule = variantRules[variantRules.length - 1] + if (lastVariantRule) { + inheritRule = lastVariantRule + } else { + const normalRules = ruleset.filter(findRules({ + component: combination.component, + parent: combination.parent + })) + const lastNormalRule = normalRules[normalRules.length - 1] + inheritRule = lastNormalRule + } + + const inheritSelector = ruleToSelector({ ...inheritRule, parent: combination.parent }, true) + const inheritedBackground = computed[inheritSelector].background + + dynamicVars.inheritedBackground = inheritedBackground + + const rgb = convert(findColor(computedDirectives.background, { dynamicVars, staticVars })).rgb + + if (!stacked[selector]) { + let blend + const alpha = computedDirectives.opacity ?? 1 + if (alpha >= 1) { + blend = rgb + } else if (alpha <= 0) { + blend = lowerLevelStackedBackground + } else { + blend = alphaBlend(rgb, computedDirectives.opacity, lowerLevelStackedBackground) + } + stacked[selector] = blend + computed[selector].background = { ...rgb, a: computedDirectives.opacity ?? 1 } + } + } + + if (computedDirectives.shadow) { + dynamicVars.shadow = flattenDeep(findShadow(flattenDeep(computedDirectives.shadow), { dynamicVars, staticVars })) + } + + if (!stacked[selector]) { + computedDirectives.background = 'transparent' + computedDirectives.opacity = 0 + stacked[selector] = lowerLevelStackedBackground + computed[selector].background = { ...lowerLevelStackedBackground, a: 0 } + } + + dynamicVars.stacked = stacked[selector] + dynamicVars.background = computed[selector].background + + const dynamicSlots = Object.entries(computedDirectives).filter(([k, v]) => k.startsWith('--')) + + dynamicSlots.forEach(([k, v]) => { + const [type, ...value] = v.split('|').map(x => x.trim()) // woah, Extreme! + switch (type) { + case 'color': { + const color = findColor(value[0], { dynamicVars, staticVars }) + dynamicVars[k] = color + if (combination.component === 'Root') { + staticVars[k.substring(2)] = color + } + break + } + case 'shadow': { + const shadow = value + dynamicVars[k] = shadow + if (combination.component === 'Root') { + staticVars[k.substring(2)] = shadow + } + break + } + case 'generic': { + dynamicVars[k] = value + if (combination.component === 'Root') { + staticVars[k.substring(2)] = value + } + break + } + } + }) + + addRule({ + dynamicVars, + selector: cssSelector, + ...combination, + directives: computedDirectives + }) + } + } + + const processInnerComponent = (component, parent) => { + const combinations = [] const { validInnerComponents = [], states: originalStates = {}, - variants: originalVariants = {}, - name + variants: originalVariants = {} } = component // Normalizing states and variants to always include "normal" @@ -241,233 +427,43 @@ export const init = (extraRuleset, ultimateBackgroundColor) => { }).reduce((acc, x) => [...acc, ...x], []) stateVariantCombination.forEach(combination => { - counter++ - // const tt0 = performance.now() - combination.component = component.name - const soloSelector = ruleToSelector(combination, true) - const soloCssSelector = ruleToSelector(combination) - const selector = [parentSelector, soloSelector].filter(x => x).join(' ') - const cssSelector = [parentSelector, soloCssSelector].filter(x => x).join(' ') - - const lowerLevelSelector = parentSelector - const lowerLevelBackground = computed[lowerLevelSelector]?.background - const lowerLevelVirtualDirectives = computed[lowerLevelSelector]?.virtualDirectives - const lowerLevelVirtualDirectivesRaw = computed[lowerLevelSelector]?.virtualDirectivesRaw - - const dynamicVars = computed[selector] || { - lowerLevelBackground, - lowerLevelVirtualDirectives, - lowerLevelVirtualDirectivesRaw + combination.lazy = component.lazy || parent?.lazy + combination.parent = parent + if (combination.state.indexOf('hover') >= 0) { + combination.lazy = true } - // Inheriting all of the applicable rules - const existingRules = ruleset.filter(findRules({ component: component.name, ...combination, parent })) - const computedDirectives = existingRules.map(r => r.directives).reduce((acc, directives) => ({ ...acc, ...directives }), {}) - const computedRule = { - component: component.name, - ...combination, - parent, - directives: computedDirectives - } - - computed[selector] = computed[selector] || {} - computed[selector].computedRule = computedRule - computed[selector].dynamicVars = dynamicVars - - if (virtualComponents.has(component.name)) { - const virtualName = [ - '--', - component.name.toLowerCase(), - combination.variant === 'normal' - ? '' - : combination.variant[0].toUpperCase() + combination.variant.slice(1).toLowerCase(), - ...combination.state.filter(x => x !== 'normal').toSorted().map(state => state[0].toUpperCase() + state.slice(1).toLowerCase()) - ].join('') - - let inheritedTextColor = computedDirectives.textColor - let inheritedTextAuto = computedDirectives.textAuto - let inheritedTextOpacity = computedDirectives.textOpacity - let inheritedTextOpacityMode = computedDirectives.textOpacityMode - const lowerLevelTextSelector = [...selector.split(/ /g).slice(0, -1), soloSelector].join(' ') - const lowerLevelTextRule = computed[lowerLevelTextSelector] - - if (inheritedTextColor == null || inheritedTextOpacity == null || inheritedTextOpacityMode == null) { - inheritedTextColor = computedDirectives.textColor ?? lowerLevelTextRule.textColor - inheritedTextAuto = computedDirectives.textAuto ?? lowerLevelTextRule.textAuto - inheritedTextOpacity = computedDirectives.textOpacity ?? lowerLevelTextRule.textOpacity - inheritedTextOpacityMode = computedDirectives.textOpacityMode ?? lowerLevelTextRule.textOpacityMode - } - - const newTextRule = { - ...computedRule, - directives: { - ...computedRule.directives, - textColor: inheritedTextColor, - textAuto: inheritedTextAuto ?? 'preserve', - textOpacity: inheritedTextOpacity, - textOpacityMode: inheritedTextOpacityMode - } - } - - dynamicVars.inheritedBackground = lowerLevelBackground - dynamicVars.stacked = convert(stacked[lowerLevelSelector]).rgb - - const intendedTextColor = convert(findColor(inheritedTextColor, { dynamicVars, staticVars })).rgb - const textColor = newTextRule.directives.textAuto === 'no-auto' - ? intendedTextColor - : getTextColor( - convert(stacked[lowerLevelSelector]).rgb, - intendedTextColor, - newTextRule.directives.textAuto === 'preserve' - ) - - // Updating previously added rule - const earlyLowerLevelRules = rules.filter(findRules(parent, true)) - const earlyLowerLevelRule = earlyLowerLevelRules.slice(-1)[0] - - const virtualDirectives = earlyLowerLevelRule.virtualDirectives || {} - const virtualDirectivesRaw = earlyLowerLevelRule.virtualDirectivesRaw || {} - - // Storing color data in lower layer to use as custom css properties - virtualDirectives[virtualName] = getTextColorAlpha(newTextRule.directives, textColor, dynamicVars) - virtualDirectivesRaw[virtualName] = textColor - earlyLowerLevelRule.virtualDirectives = virtualDirectives - earlyLowerLevelRule.virtualDirectivesRaw = virtualDirectivesRaw - computed[lowerLevelSelector].virtualDirectives = virtualDirectives - computed[lowerLevelSelector].virtualDirectivesRaw = virtualDirectivesRaw - } else { - computed[selector] = computed[selector] || {} - - // TODO: DEFAULT TEXT COLOR - const lowerLevelStackedBackground = stacked[lowerLevelSelector] || convert(ultimateBackgroundColor).rgb - - if (computedDirectives.background) { - let inheritRule = null - const variantRules = ruleset.filter(findRules({ component: component.name, variant: combination.variant, parent })) - const lastVariantRule = variantRules[variantRules.length - 1] - if (lastVariantRule) { - inheritRule = lastVariantRule - } else { - const normalRules = ruleset.filter(findRules({ component: component.name, parent })) - const lastNormalRule = normalRules[normalRules.length - 1] - inheritRule = lastNormalRule - } - - const inheritSelector = ruleToSelector({ ...inheritRule, parent }, true) - const inheritedBackground = computed[inheritSelector].background - - dynamicVars.inheritedBackground = inheritedBackground - - const rgb = convert(findColor(computedDirectives.background, { dynamicVars, staticVars })).rgb - - if (!stacked[selector]) { - let blend - const alpha = computedDirectives.opacity ?? 1 - if (alpha >= 1) { - blend = rgb - } else if (alpha <= 0) { - blend = lowerLevelStackedBackground - } else { - blend = alphaBlend(rgb, computedDirectives.opacity, lowerLevelStackedBackground) - } - stacked[selector] = blend - computed[selector].background = { ...rgb, a: computedDirectives.opacity ?? 1 } - } - } - - if (computedDirectives.shadow) { - dynamicVars.shadow = flattenDeep(findShadow(flattenDeep(computedDirectives.shadow), { dynamicVars, staticVars })) - } - - if (!stacked[selector]) { - computedDirectives.background = 'transparent' - computedDirectives.opacity = 0 - stacked[selector] = lowerLevelStackedBackground - computed[selector].background = { ...lowerLevelStackedBackground, a: 0 } - } - - dynamicVars.stacked = stacked[selector] - dynamicVars.background = computed[selector].background - - const dynamicSlots = Object.entries(computedDirectives).filter(([k, v]) => k.startsWith('--')) - - dynamicSlots.forEach(([k, v]) => { - const [type, ...value] = v.split('|').map(x => x.trim()) // woah, Extreme! - switch (type) { - case 'color': { - const color = findColor(value[0], { dynamicVars, staticVars }) - dynamicVars[k] = color - if (component.name === 'Root') { - staticVars[k.substring(2)] = color - } - break - } - case 'shadow': { - const shadow = value - dynamicVars[k] = shadow - if (component.name === 'Root') { - staticVars[k.substring(2)] = shadow - } - break - } - case 'generic': { - dynamicVars[k] = value - if (component.name === 'Root') { - staticVars[k.substring(2)] = value - } - break - } - } - }) - - addRule({ - dynamicVars, - selector: cssSelector, - component: component.name, - ...combination, - parent, - directives: computedDirectives - }) - } + combinations.push(combination) innerComponents.forEach(innerComponent => { - if (innerComponent.lazy) { - promises.push(new Promise((resolve, reject) => { - setTimeout(() => { - try { - processInnerComponent(innerComponent, lazyRules, { parent, component: name, ...combination }) - resolve() - } catch (e) { - reject(e) - } - }, 0) - })) - } else { - processInnerComponent(innerComponent, rules, { parent, component: name, ...combination }) - } + combinations.push(...processInnerComponent(innerComponent, combination)) }) - // const tt1 = performance.now() - // if (!component.virtual) { - // console.log('State-variant ' + combination.variant + ' : ' + combination.state.join('+') + ' procession time: ' + (tt1 - tt0) + 'ms') - // } }) - // const t1 = performance.now() - // if (!component.virtual) { - // const path = [...parentList, component.name].join(' > ') - // console.log('Component ' + path + ' procession time: ' + (t1 - t0) + 'ms') - // } + return combinations } - processInnerComponent(components.Root, eagerRules) - console.debug('Eager combinations processed:' + counter) - const lazyExec = Promise.all(promises).then(() => { - console.debug('Total combinations processed: ' + counter) - }).then(() => lazyRules) + const t0 = performance.now() + const combinations = processInnerComponent(components.Root) + const t1 = performance.now() + console.debug('Tree tranveral took ' + (t1 - t0) + ' ms') + + combinations.forEach((combination) => { + if (combination.lazy) { + lazyPromises.push(async () => processCombination(combination, lazyRules)) + } else { + processCombination(combination, eagerRules) + } + }) + const t2 = performance.now() + console.debug('Eager processing took ' + (t2 - t1) + ' ms') return { - lazy: lazyExec, + lazy: async () => { + await Promise.all(lazyPromises.map(x => x())) + return lazyRules + }, eager: eagerRules, staticVars }