2024-07-12 02:13:08 +03:00
|
|
|
import { getPreset, applyTheme, tryLoadCache } from '../services/style_setter/style_setter.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'
|
|
|
|
|
|
2018-09-09 19:36:13 +03:00
|
|
|
const defaultState = {
|
2024-06-26 17:05:59 +03:00
|
|
|
localFonts: null,
|
2024-04-03 22:52:12 +03:00
|
|
|
themeApplied: false,
|
2024-05-22 19:54:19 +03:00
|
|
|
temporaryChangesTimeoutId: null, // used for temporary options that revert after a timeout
|
|
|
|
|
temporaryChangesConfirm: () => {}, // used for applying temporary options
|
|
|
|
|
temporaryChangesRevert: () => {}, // used for reverting temporary options
|
2020-05-03 17:36:12 +03:00
|
|
|
settingsModalState: 'hidden',
|
2023-03-14 21:50:43 +02:00
|
|
|
settingsModalLoadedUser: false,
|
|
|
|
|
settingsModalLoadedAdmin: false,
|
2020-09-03 15:45:13 +03:00
|
|
|
settingsModalTargetTab: null,
|
2023-03-14 21:50:43 +02:00
|
|
|
settingsModalMode: 'user',
|
2018-09-09 19:36:13 +03:00
|
|
|
settings: {
|
|
|
|
|
currentSaveStateNotice: null,
|
2018-12-13 18:04:09 +07:00
|
|
|
noticeClearTimeout: null,
|
|
|
|
|
notificationPermission: null
|
2018-11-30 16:39:07 +03:00
|
|
|
},
|
|
|
|
|
browserSupport: {
|
|
|
|
|
cssFilter: window.CSS && window.CSS.supports && (
|
|
|
|
|
window.CSS.supports('filter', 'drop-shadow(0 0)') ||
|
2018-12-13 18:22:15 +07:00
|
|
|
window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)')
|
2024-06-26 17:05:59 +03:00
|
|
|
),
|
|
|
|
|
localFonts: typeof window.queryLocalFonts === 'function'
|
2019-03-03 16:33:40 +02:00
|
|
|
},
|
2022-04-05 18:38:05 +03:00
|
|
|
layoutType: 'normal',
|
2020-05-07 16:10:53 +03:00
|
|
|
globalNotices: [],
|
2020-07-23 15:09:32 +03:00
|
|
|
layoutHeight: 0,
|
|
|
|
|
lastTimeline: null
|
2018-09-09 19:36:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const interfaceMod = {
|
|
|
|
|
state: defaultState,
|
|
|
|
|
mutations: {
|
|
|
|
|
settingsSaved (state, { success, error }) {
|
|
|
|
|
if (success) {
|
|
|
|
|
if (state.noticeClearTimeout) {
|
|
|
|
|
clearTimeout(state.noticeClearTimeout)
|
|
|
|
|
}
|
2021-04-25 13:24:08 +03:00
|
|
|
state.settings.currentSaveStateNotice = { error: false, data: success }
|
|
|
|
|
state.settings.noticeClearTimeout = setTimeout(() => delete state.settings.currentSaveStateNotice, 2000)
|
2018-09-09 19:36:13 +03:00
|
|
|
} else {
|
2021-04-25 13:24:08 +03:00
|
|
|
state.settings.currentSaveStateNotice = { error: true, errorData: error }
|
2018-09-09 19:36:13 +03:00
|
|
|
}
|
2018-12-13 18:04:09 +07:00
|
|
|
},
|
2024-05-22 19:54:19 +03:00
|
|
|
setTemporaryChanges (state, { timeoutId, confirm, revert }) {
|
|
|
|
|
state.temporaryChangesTimeoutId = timeoutId
|
|
|
|
|
state.temporaryChangesConfirm = confirm
|
|
|
|
|
state.temporaryChangesRevert = revert
|
|
|
|
|
},
|
|
|
|
|
clearTemporaryChanges (state) {
|
|
|
|
|
clearTimeout(state.temporaryChangesTimeoutId)
|
|
|
|
|
state.temporaryChangesTimeoutId = null
|
|
|
|
|
state.temporaryChangesConfirm = () => {}
|
|
|
|
|
state.temporaryChangesRevert = () => {}
|
|
|
|
|
},
|
2024-04-03 22:52:12 +03:00
|
|
|
setThemeApplied (state) {
|
|
|
|
|
state.themeApplied = true
|
|
|
|
|
},
|
2018-12-13 18:04:09 +07:00
|
|
|
setNotificationPermission (state, permission) {
|
|
|
|
|
state.notificationPermission = permission
|
2019-03-03 16:33:40 +02:00
|
|
|
},
|
2022-04-05 18:38:05 +03:00
|
|
|
setLayoutType (state, value) {
|
|
|
|
|
state.layoutType = value
|
2020-05-03 17:36:12 +03:00
|
|
|
},
|
|
|
|
|
closeSettingsModal (state) {
|
|
|
|
|
state.settingsModalState = 'hidden'
|
|
|
|
|
},
|
|
|
|
|
togglePeekSettingsModal (state) {
|
|
|
|
|
switch (state.settingsModalState) {
|
|
|
|
|
case 'minimized':
|
|
|
|
|
state.settingsModalState = 'visible'
|
|
|
|
|
return
|
|
|
|
|
case 'visible':
|
|
|
|
|
state.settingsModalState = 'minimized'
|
|
|
|
|
return
|
|
|
|
|
default:
|
|
|
|
|
throw new Error('Illegal minimization state of settings modal')
|
|
|
|
|
}
|
|
|
|
|
},
|
2023-03-14 21:50:43 +02:00
|
|
|
openSettingsModal (state, value) {
|
|
|
|
|
state.settingsModalMode = value
|
2020-05-03 17:36:12 +03:00
|
|
|
state.settingsModalState = 'visible'
|
2023-03-14 21:50:43 +02:00
|
|
|
if (value === 'user') {
|
|
|
|
|
if (!state.settingsModalLoadedUser) {
|
|
|
|
|
state.settingsModalLoadedUser = true
|
|
|
|
|
}
|
|
|
|
|
} else if (value === 'admin') {
|
|
|
|
|
if (!state.settingsModalLoadedAdmin) {
|
|
|
|
|
state.settingsModalLoadedAdmin = true
|
|
|
|
|
}
|
2020-06-02 01:10:52 +03:00
|
|
|
}
|
2020-07-01 19:15:28 +03:00
|
|
|
},
|
2020-09-03 15:45:13 +03:00
|
|
|
setSettingsModalTargetTab (state, value) {
|
|
|
|
|
state.settingsModalTargetTab = value
|
|
|
|
|
},
|
2020-07-02 10:40:41 +03:00
|
|
|
pushGlobalNotice (state, notice) {
|
|
|
|
|
state.globalNotices.push(notice)
|
|
|
|
|
},
|
|
|
|
|
removeGlobalNotice (state, notice) {
|
|
|
|
|
state.globalNotices = state.globalNotices.filter(n => n !== notice)
|
2020-05-07 16:10:53 +03:00
|
|
|
},
|
|
|
|
|
setLayoutHeight (state, value) {
|
|
|
|
|
state.layoutHeight = value
|
2020-07-23 15:09:32 +03:00
|
|
|
},
|
2022-04-12 21:18:06 +03:00
|
|
|
setLayoutWidth (state, value) {
|
|
|
|
|
state.layoutWidth = value
|
|
|
|
|
},
|
2020-07-23 15:09:32 +03:00
|
|
|
setLastTimeline (state, value) {
|
|
|
|
|
state.lastTimeline = value
|
2024-06-26 17:05:59 +03:00
|
|
|
},
|
|
|
|
|
setFontsList (state, value) {
|
|
|
|
|
state.localFonts = new Set(value.map(font => font.family))
|
2018-09-09 19:36:13 +03:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
actions: {
|
2018-09-09 21:21:23 +03:00
|
|
|
setPageTitle ({ rootState }, option = '') {
|
|
|
|
|
document.title = `${option} ${rootState.instance.name}`
|
2018-09-09 19:36:13 +03:00
|
|
|
},
|
|
|
|
|
settingsSaved ({ commit, dispatch }, { success, error }) {
|
|
|
|
|
commit('settingsSaved', { success, error })
|
2018-12-13 18:04:09 +07:00
|
|
|
},
|
|
|
|
|
setNotificationPermission ({ commit }, permission) {
|
|
|
|
|
commit('setNotificationPermission', permission)
|
2019-03-03 16:33:40 +02:00
|
|
|
},
|
2020-05-03 17:36:12 +03:00
|
|
|
closeSettingsModal ({ commit }) {
|
|
|
|
|
commit('closeSettingsModal')
|
|
|
|
|
},
|
2023-03-14 21:50:43 +02:00
|
|
|
openSettingsModal ({ commit }, value = 'user') {
|
|
|
|
|
commit('openSettingsModal', value)
|
2020-05-03 17:36:12 +03:00
|
|
|
},
|
|
|
|
|
togglePeekSettingsModal ({ commit }) {
|
|
|
|
|
commit('togglePeekSettingsModal')
|
2020-07-01 19:15:28 +03:00
|
|
|
},
|
2020-09-03 15:45:13 +03:00
|
|
|
clearSettingsModalTargetTab ({ commit }) {
|
|
|
|
|
commit('setSettingsModalTargetTab', null)
|
|
|
|
|
},
|
|
|
|
|
openSettingsModalTab ({ commit }, value) {
|
|
|
|
|
commit('setSettingsModalTargetTab', value)
|
2023-06-13 14:00:20 -04:00
|
|
|
commit('openSettingsModal', 'user')
|
2020-09-03 15:45:13 +03:00
|
|
|
},
|
2020-07-02 10:40:41 +03:00
|
|
|
pushGlobalNotice (
|
2022-03-27 12:21:33 +03:00
|
|
|
{ commit, dispatch, state },
|
2020-07-02 10:40:41 +03:00
|
|
|
{
|
|
|
|
|
messageKey,
|
|
|
|
|
messageArgs = {},
|
|
|
|
|
level = 'error',
|
|
|
|
|
timeout = 0
|
|
|
|
|
}) {
|
|
|
|
|
const notice = {
|
|
|
|
|
messageKey,
|
|
|
|
|
messageArgs,
|
|
|
|
|
level
|
|
|
|
|
}
|
2022-03-27 12:21:33 +03:00
|
|
|
commit('pushGlobalNotice', notice)
|
|
|
|
|
// Adding a new element to array wraps it in a Proxy, which breaks the comparison
|
|
|
|
|
// TODO: Generate UUID or something instead or relying on !== operator?
|
|
|
|
|
const newNotice = state.globalNotices[state.globalNotices.length - 1]
|
2020-07-02 10:40:41 +03:00
|
|
|
if (timeout) {
|
2022-03-27 12:21:33 +03:00
|
|
|
setTimeout(() => dispatch('removeGlobalNotice', newNotice), timeout)
|
2020-07-02 10:40:41 +03:00
|
|
|
}
|
2022-03-27 12:21:33 +03:00
|
|
|
return newNotice
|
2020-07-02 10:40:41 +03:00
|
|
|
},
|
|
|
|
|
removeGlobalNotice ({ commit }, notice) {
|
|
|
|
|
commit('removeGlobalNotice', notice)
|
2020-05-07 16:10:53 +03:00
|
|
|
},
|
|
|
|
|
setLayoutHeight ({ commit }, value) {
|
|
|
|
|
commit('setLayoutHeight', value)
|
2020-07-23 15:09:32 +03:00
|
|
|
},
|
2022-04-12 21:18:06 +03:00
|
|
|
// value is optional, assuming it was cached prior
|
2022-05-09 23:24:35 +03:00
|
|
|
setLayoutWidth ({ commit, state, rootGetters, rootState }, value) {
|
2022-04-12 21:18:06 +03:00
|
|
|
let width = value
|
|
|
|
|
if (value !== undefined) {
|
|
|
|
|
commit('setLayoutWidth', value)
|
|
|
|
|
} else {
|
|
|
|
|
width = state.layoutWidth
|
|
|
|
|
}
|
|
|
|
|
const mobileLayout = width <= 800
|
|
|
|
|
const normalOrMobile = mobileLayout ? 'mobile' : 'normal'
|
|
|
|
|
const { thirdColumnMode } = rootGetters.mergedConfig
|
2022-05-09 23:24:35 +03:00
|
|
|
if (thirdColumnMode === 'none' || !rootState.users.currentUser) {
|
2022-04-12 21:18:06 +03:00
|
|
|
commit('setLayoutType', normalOrMobile)
|
|
|
|
|
} else {
|
|
|
|
|
const wideLayout = width >= 1300
|
|
|
|
|
commit('setLayoutType', wideLayout ? 'wide' : normalOrMobile)
|
|
|
|
|
}
|
|
|
|
|
},
|
2024-06-27 00:34:25 +03:00
|
|
|
queryLocalFonts ({ commit, dispatch, state }) {
|
|
|
|
|
if (state.localFonts !== null) return
|
|
|
|
|
commit('setFontsList', [])
|
|
|
|
|
if (!state.browserSupport.localFonts) {
|
|
|
|
|
return
|
|
|
|
|
}
|
2024-06-26 17:05:59 +03:00
|
|
|
window
|
|
|
|
|
.queryLocalFonts()
|
|
|
|
|
.then((fonts) => {
|
2024-06-27 00:34:25 +03:00
|
|
|
console.log(fonts)
|
2024-06-26 17:05:59 +03:00
|
|
|
commit('setFontsList', fonts)
|
|
|
|
|
})
|
|
|
|
|
.catch((e) => {
|
|
|
|
|
dispatch('pushGlobalNotice', {
|
|
|
|
|
messageKey: 'settings.style.themes3.font.font_list_unavailable',
|
|
|
|
|
messageArgs: {
|
|
|
|
|
error: e
|
|
|
|
|
},
|
|
|
|
|
level: 'error'
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
},
|
2020-07-23 15:09:32 +03:00
|
|
|
setLastTimeline ({ commit }, value) {
|
|
|
|
|
commit('setLastTimeline', value)
|
2024-07-12 02:13:08 +03:00
|
|
|
},
|
2024-07-17 22:10:11 +03:00
|
|
|
setTheme ({ commit, rootState }, { themeName, themeData, recompile, saveData } = {}) {
|
2024-07-12 02:13:08 +03:00
|
|
|
const {
|
|
|
|
|
theme: instanceThemeName
|
|
|
|
|
} = rootState.instance
|
|
|
|
|
|
|
|
|
|
const {
|
2024-07-17 22:10:11 +03:00
|
|
|
theme: userThemeName,
|
2024-07-12 02:13:08 +03:00
|
|
|
customTheme: userThemeSnapshot,
|
|
|
|
|
customThemeSource: userThemeSource,
|
|
|
|
|
forceThemeRecompilation,
|
2024-07-16 21:01:20 +03:00
|
|
|
themeDebug,
|
|
|
|
|
theme3hacks
|
2024-07-12 02:13:08 +03:00
|
|
|
} = rootState.config
|
|
|
|
|
|
2024-07-17 22:10:11 +03:00
|
|
|
const actualThemeName = userThemeName || instanceThemeName
|
|
|
|
|
|
2024-07-12 02:13:08 +03:00
|
|
|
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')
|
2024-07-12 02:19:38 +03:00
|
|
|
return
|
2024-07-12 02:13:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let promise = null
|
|
|
|
|
|
2024-07-17 22:10:11 +03:00
|
|
|
if (themeData) {
|
2024-07-12 02:13:08 +03:00
|
|
|
promise = Promise.resolve(normalizeThemeData(themeData))
|
2024-07-17 22:10:11 +03:00
|
|
|
} else if (themeName) {
|
|
|
|
|
promise = getPreset(themeName).then(themeData => normalizeThemeData(themeData))
|
|
|
|
|
} else if (userThemeSource || userThemeSnapshot) {
|
|
|
|
|
if (userThemeSource && userThemeSource.themeEngineVersion === CURRENT_VERSION) {
|
|
|
|
|
promise = Promise.resolve(normalizeThemeData(userThemeSource))
|
|
|
|
|
} else {
|
|
|
|
|
promise = Promise.resolve(normalizeThemeData(userThemeSnapshot))
|
2024-07-12 02:13:08 +03:00
|
|
|
}
|
2024-07-17 22:10:11 +03:00
|
|
|
} else if (actualThemeName && actualThemeName !== 'custom') {
|
|
|
|
|
promise = getPreset(actualThemeName).then(themeData => normalizeThemeData(themeData))
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error('Cannot load any theme!')
|
2024-07-12 02:13:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
promise
|
|
|
|
|
.then(realThemeData => {
|
2024-07-16 21:01:20 +03:00
|
|
|
const theme2ruleset = convertTheme2To3(realThemeData)
|
2024-07-17 22:10:11 +03:00
|
|
|
|
|
|
|
|
if (saveData) {
|
|
|
|
|
commit('setOption', { name: 'theme', value: themeName || actualThemeName })
|
|
|
|
|
commit('setOption', { name: 'customTheme', value: realThemeData })
|
|
|
|
|
commit('setOption', { name: 'customThemeSource', value: realThemeData })
|
|
|
|
|
}
|
2024-07-16 21:01:20 +03:00
|
|
|
const hacks = []
|
|
|
|
|
|
|
|
|
|
Object.entries(theme3hacks).forEach(([key, value]) => {
|
|
|
|
|
switch (key) {
|
|
|
|
|
case 'underlay': {
|
|
|
|
|
if (value !== 'none') {
|
|
|
|
|
const newRule = {
|
|
|
|
|
component: 'Underlay',
|
|
|
|
|
directives: {}
|
|
|
|
|
}
|
|
|
|
|
if (value === 'opaque') {
|
|
|
|
|
newRule.directives.opacity = 1
|
|
|
|
|
newRule.directives.background = '--wallpaper'
|
|
|
|
|
}
|
|
|
|
|
if (value === 'transparent') {
|
|
|
|
|
newRule.directives.opacity = 0
|
|
|
|
|
}
|
|
|
|
|
console.log('NEW RULE', newRule)
|
|
|
|
|
hacks.push(newRule)
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const ruleset = [
|
|
|
|
|
...theme2ruleset,
|
|
|
|
|
...hacks
|
|
|
|
|
]
|
2024-07-12 02:13:08 +03:00
|
|
|
|
|
|
|
|
applyTheme(
|
|
|
|
|
ruleset,
|
|
|
|
|
() => commit('setThemeApplied'),
|
|
|
|
|
themeDebug
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return promise
|
2018-09-09 19:36:13 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default interfaceMod
|
2024-07-17 17:19:57 +03:00
|
|
|
|
|
|
|
|
export const normalizeThemeData = (input) => {
|
|
|
|
|
let themeData = input
|
|
|
|
|
|
|
|
|
|
if (Array.isArray(themeData)) {
|
|
|
|
|
themeData = { colors: {} }
|
|
|
|
|
themeData.colors.bg = input[1]
|
|
|
|
|
themeData.colors.fg = input[2]
|
|
|
|
|
themeData.colors.text = input[3]
|
|
|
|
|
themeData.colors.link = input[4]
|
|
|
|
|
themeData.colors.cRed = input[5]
|
|
|
|
|
themeData.colors.cGreen = input[6]
|
|
|
|
|
themeData.colors.cBlue = input[7]
|
|
|
|
|
themeData.colors.cOrange = input[8]
|
|
|
|
|
return generatePreset(themeData).theme
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|