implement a simple caching system for themes 3

neckbeard^2
Henry Jameson 4 months ago
parent 15dde2d372
commit 5505a89e8a

@ -32,6 +32,7 @@
"click-outside-vue3": "4.0.1", "click-outside-vue3": "4.0.1",
"cropperjs": "1.5.13", "cropperjs": "1.5.13",
"escape-html": "1.0.3", "escape-html": "1.0.3",
"hash-sum": "^2.0.0",
"js-cookie": "3.0.5", "js-cookie": "3.0.5",
"localforage": "1.10.0", "localforage": "1.10.0",
"parse-link-header": "2.0.0", "parse-link-header": "2.0.0",

@ -14,7 +14,7 @@ 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 { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
import { applyTheme, 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'
@ -353,21 +353,25 @@ const afterStoreSetup = async ({ store, i18n }) => {
await setConfig({ store }) await setConfig({ store })
const { customTheme, customThemeSource } = store.state.config const { customTheme, customThemeSource, forceThemeRecompilation } = store.state.config
const { theme } = store.state.instance const { theme } = store.state.instance
const customThemePresent = customThemeSource || customTheme const customThemePresent = customThemeSource || customTheme
if (customThemePresent) { if (!forceThemeRecompilation && tryLoadCache()) {
if (customThemeSource && customThemeSource.themeEngineVersion === CURRENT_VERSION) {
applyTheme(customThemeSource)
} else {
applyTheme(customTheme)
}
store.commit('setThemeApplied') store.commit('setThemeApplied')
} else if (theme) {
// do nothing, it will load asynchronously
} else { } else {
console.error('Failed to load any theme!') if (customThemePresent) {
if (customThemeSource && customThemeSource.themeEngineVersion === CURRENT_VERSION) {
applyTheme(customThemeSource)
} else {
applyTheme(customTheme)
}
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)

@ -200,6 +200,14 @@
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('settings.post_look_feel') }}</h2> <h2>{{ $t('settings.post_look_feel') }}</h2>
<ul class="setting-list"> <ul class="setting-list">
<li>
<BooleanSetting
path="forceThemeRecompilation"
:expert="1"
>
{{ $t('settings.force_theme_recompilation_debug') }}
</BooleanSetting>
</li>
<li> <li>
<ChoiceSetting <ChoiceSetting
id="conversationDisplay" id="conversationDisplay"

@ -634,6 +634,7 @@
"subject_line_email": "Like email: \"re: subject\"", "subject_line_email": "Like email: \"re: subject\"",
"subject_line_mastodon": "Like mastodon: copy as is", "subject_line_mastodon": "Like mastodon: copy as is",
"subject_line_noop": "Do not copy", "subject_line_noop": "Do not copy",
"force_theme_recompilation_debug": "Disable theme cahe, force recompile on each boot (DEBUG)",
"conversation_display": "Conversation display style", "conversation_display": "Conversation display style",
"conversation_display_tree": "Tree-style", "conversation_display_tree": "Tree-style",
"conversation_display_tree_quick": "Tree view", "conversation_display_tree_quick": "Tree view",

@ -28,6 +28,7 @@ export const defaultState = {
theme: undefined, theme: undefined,
customTheme: undefined, customTheme: undefined,
customThemeSource: undefined, customThemeSource: undefined,
forceThemeRecompilation: false,
hideISP: false, hideISP: false,
hideInstanceWallpaper: false, hideInstanceWallpaper: false,
hideShoutbox: false, hideShoutbox: false,

@ -377,7 +377,8 @@ const instance = {
commit('setInstanceOption', { name: 'themeData', value: themeData }) commit('setInstanceOption', { name: 'themeData', value: themeData })
// No need to apply theme if there's user theme already // No need to apply theme if there's user theme already
const { customTheme } = rootState.config const { customTheme } = rootState.config
if (customTheme) return const { themeApplied } = rootState.interface
if (customTheme || themeApplied) return
// New theme presets don't have 'theme' property, they use 'source' // New theme presets don't have 'theme' property, they use 'source'
const themeSource = themeData.source const themeSource = themeData.source

@ -1,6 +1,6 @@
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 { generatePreset } from '../theme_data/theme_data.service.js'
import { init } from '../theme_data/theme_data_3.service.js' import { init, getChecksum } from '../theme_data/theme_data_3.service.js'
import { convertTheme2To3 } from '../theme_data/theme2_to_theme3.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'
@ -87,9 +87,37 @@ export const generateTheme = async (input, callbacks) => {
return { lazyProcessFunc: processChunk } return { lazyProcessFunc: processChunk }
} }
export const applyTheme = async (input) => { export const tryLoadCache = () => {
const json = localStorage.getItem('pleroma-fe-theme-cache')
if (!json) return null
let cache
try {
cache = JSON.parse(json)
} catch (e) {
console.error('Failed to decode theme cache:', e)
return false
}
if (cache.checksum === getChecksum()) {
const styleSheet = new CSSStyleSheet()
const lazyStyleSheet = new CSSStyleSheet()
cache.data[0].forEach(rule => styleSheet.insertRule(rule, 'index-max'))
cache.data[1].forEach(rule => lazyStyleSheet.insertRule(rule, 'index-max'))
document.adoptedStyleSheets = [styleSheet, lazyStyleSheet]
return true
} else {
console.warn('Checksum doesn\'t match, cache not usable, clearing')
localStorage.removeItem('pleroma-fe-theme-cache')
}
}
export const applyTheme = async (input, onFinish = (data) => {}) => {
const styleSheet = new CSSStyleSheet() const styleSheet = new CSSStyleSheet()
const styleArray = []
const lazyStyleSheet = new CSSStyleSheet() const lazyStyleSheet = new CSSStyleSheet()
const lazyStyleArray = []
const { lazyProcessFunc } = await generateTheme( const { lazyProcessFunc } = await generateTheme(
input, input,
@ -97,8 +125,10 @@ export const applyTheme = async (input) => {
onNewRule (rule, isLazy) { onNewRule (rule, isLazy) {
if (isLazy) { if (isLazy) {
lazyStyleSheet.insertRule(rule, 'index-max') lazyStyleSheet.insertRule(rule, 'index-max')
lazyStyleArray.push(rule)
} else { } else {
styleSheet.insertRule(rule, 'index-max') styleSheet.insertRule(rule, 'index-max')
styleArray.push(rule)
} }
}, },
onEagerFinished () { onEagerFinished () {
@ -106,6 +136,9 @@ export const applyTheme = async (input) => {
}, },
onLazyFinished () { onLazyFinished () {
document.adoptedStyleSheets = [styleSheet, lazyStyleSheet] document.adoptedStyleSheets = [styleSheet, lazyStyleSheet]
const cache = { checksum: getChecksum(), data: [styleArray, lazyStyleArray] }
onFinish(cache)
localStorage.setItem('pleroma-fe-theme-cache', JSON.stringify(cache))
} }
} }
) )

@ -1,4 +1,5 @@
import { convert, brightness } from 'chromatism' import { convert, brightness } from 'chromatism'
import sum from 'hash-sum'
import { flattenDeep } from 'lodash' import { flattenDeep } from 'lodash'
import { import {
alphaBlend, alphaBlend,
@ -142,8 +143,12 @@ componentsContext.keys().forEach(key => {
components[component.name] = component components[component.name] = component
}) })
const checksum = sum(components)
const ruleToSelector = genericRuleToSelector(components) const ruleToSelector = genericRuleToSelector(components)
export const getChecksum = () => checksum
export const init = (extraRuleset, ultimateBackgroundColor) => { export const init = (extraRuleset, ultimateBackgroundColor) => {
const staticVars = {} const staticVars = {}
const stacked = {} const stacked = {}
@ -463,6 +468,7 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
return { return {
lazy: result.filter(x => typeof x === 'function'), lazy: result.filter(x => typeof x === 'function'),
eager: result.filter(x => typeof x !== 'function'), eager: result.filter(x => typeof x !== 'function'),
staticVars staticVars,
checksum
} }
} }

Loading…
Cancel
Save