Merge branch 'setttingssync' into shigusegubu-themes3

This commit is contained in:
Henry Jameson 2026-04-10 01:09:10 +03:00
commit 8bffe9f0aa
7 changed files with 198 additions and 68 deletions

View file

@ -42,6 +42,7 @@ import VBodyScrollLock from 'src/directives/body_scroll_lock'
import { import {
INSTANCE_DEFAULT_CONFIG_DEFINITIONS, INSTANCE_DEFAULT_CONFIG_DEFINITIONS,
INSTANCE_IDENTITY_DEFAULT_DEFINITIONS, INSTANCE_IDENTITY_DEFAULT_DEFINITIONS,
INSTANCE_IDENTIY_EXTERNAL,
} from 'src/modules/default_config_state.js' } from 'src/modules/default_config_state.js'
let staticInitialResults = null let staticInitialResults = null
@ -170,12 +171,14 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
config = Object.assign({}, staticConfig, apiConfig) config = Object.assign({}, staticConfig, apiConfig)
} }
Object.keys(INSTANCE_IDENTITY_DEFAULT_DEFINITIONS).forEach((source) => Object.keys(INSTANCE_IDENTITY_DEFAULT_DEFINITIONS).forEach((source) => {
if (source === 'name') return
if (INSTANCE_IDENTIY_EXTERNAL.has(source)) return
useInstanceStore().set({ useInstanceStore().set({
value: config[source], value: config[source],
path: `instanceIdentity.${source}`, path: `instanceIdentity.${source}`,
}), })
) })
Object.keys(INSTANCE_DEFAULT_CONFIG_DEFINITIONS).forEach((source) => Object.keys(INSTANCE_DEFAULT_CONFIG_DEFINITIONS).forEach((source) =>
useInstanceStore().set({ useInstanceStore().set({
@ -277,7 +280,7 @@ const getNodeInfo = async ({ store }) => {
const metadata = data.metadata const metadata = data.metadata
const features = metadata.features const features = metadata.features
useInstanceStore().set({ useInstanceStore().set({
path: 'name', path: 'instanceIdentity.name',
value: metadata.nodeName, value: metadata.nodeName,
}) })
useInstanceStore().set({ useInstanceStore().set({
@ -527,6 +530,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
useInterfaceStore().setLayoutWidth(windowWidth()) useInterfaceStore().setLayoutWidth(windowWidth())
useInterfaceStore().setLayoutHeight(windowHeight()) useInterfaceStore().setLayoutHeight(windowHeight())
window.syncConfig = useSyncConfigStore() window.syncConfig = useSyncConfigStore()
window.mergedConfig = useMergedConfigStore() window.mergedConfig = useMergedConfigStore()
window.localConfig = useLocalConfigStore() window.localConfig = useLocalConfigStore()

View file

@ -13,7 +13,12 @@ import { useLocalConfigStore } from 'src/stores/local_config.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js' import { useSyncConfigStore } from 'src/stores/sync_config.js'
import { LOCAL_ONLY_KEYS } from 'src/modules/default_config_state.js' import {
LOCAL_ONLY_KEYS,
ROOT_CONFIG,
ROOT_CONFIG_DEFINITIONS,
validateSetting,
} from 'src/modules/default_config_state.js'
import { import {
newExporter, newExporter,
newImporter, newImporter,
@ -138,37 +143,52 @@ const SettingsModal = {
}) })
} }
}, },
onImport(data) { onImport(input) {
if (data) { if (!input) return
Object.entries(data).forEach(([path, value]) => { const { _pleroma_settings_version, ...data } = input
if (LOCAL_ONLY_KEYS.has(path)) {
useLocalConfigStore().set({ path, value })
} else {
if (path.startsWith('muteFilters')) {
Object.keys(
useMergedConfigStore().mergedConfig.muteFilters,
).forEach((key) => {
useSyncConfigStore().unsetPreference({
path: `simple.${path}.${key}`,
})
})
Object.entries(value).forEach(([key, filter]) => { Object.entries(data).forEach(([path, value]) => {
useSyncConfigStore().setPreference({ const definition = ROOT_CONFIG_DEFINITIONS[path]
path: `simple.${path}.${key}`,
value: filter, const finalValue = validateSetting({
}) path,
value,
definition,
throwError: false,
defaultState: ROOT_CONFIG,
})
if (finalValue === undefined) return
if (LOCAL_ONLY_KEYS.has(path)) {
useLocalConfigStore().set({ path, value: finalValue })
} else {
if (path.startsWith('muteFilters')) {
Object.keys(
useMergedConfigStore().mergedConfig.muteFilters,
).forEach((key) => {
useSyncConfigStore().unsetPreference({
path: `simple.${path}.${key}`,
}) })
} else { })
Object.entries(value).forEach(([key, filter]) => {
useSyncConfigStore().setPreference({
path: `simple.${path}.${key}`,
value: filter,
})
})
} else {
if (finalValue !== undefined) {
useSyncConfigStore().setPreference({ useSyncConfigStore().setPreference({
path: `simple.${path}`, path: `simple.${path}`,
value, value: finalValue,
}) })
} }
} }
}) }
useSyncConfigStore().pushSyncConfig() })
} useSyncConfigStore().pushSyncConfig()
}, },
restore() { restore() {
this.dataImporter.importData() this.dataImporter.importData()
@ -184,10 +204,17 @@ const SettingsModal = {
let sample = config let sample = config
if (!theme) { if (!theme) {
const ignoreList = new Set([ const ignoreList = new Set([
'theme',
'customTheme', 'customTheme',
'customThemeSource', 'customThemeSource',
'colors', 'colors',
'style',
'styleCustomData',
'palette',
'paletteCustomData',
'themeChecksum',
]) ])
sample = Object.fromEntries( sample = Object.fromEntries(
Object.entries(sample).filter( Object.entries(sample).filter(
([key, value]) => !ignoreList.has(key) && value !== undefined, ([key, value]) => !ignoreList.has(key) && value !== undefined,

View file

@ -112,10 +112,19 @@ export const INSTANCE_IDENTITY_DEFAULT_DEFINITIONS = {
type: 'string', type: 'string',
required: false, required: false,
}, },
name: {
description: 'Instance Name',
type: 'string',
required: false,
},
} }
export const INSTANCE_IDENTITY_DEFAULT = convertDefinitions( export const INSTANCE_IDENTITY_DEFAULT = convertDefinitions(
INSTANCE_IDENTITY_DEFAULT_DEFINITIONS, INSTANCE_IDENTITY_DEFAULT_DEFINITIONS,
) )
export const INSTANCE_IDENTIY_EXTERNAL = new Set([
'tos',
'instanceSpecificPanelContent',
])
/// This object contains setting entries that makes sense /// This object contains setting entries that makes sense
/// at the user level. The defaults can also be overriden by /// at the user level. The defaults can also be overriden by
@ -522,6 +531,18 @@ export const INSTANCE_DEFAULT_CONFIG_DEFINITIONS = {
type: 'string', type: 'string',
required: false, required: false,
}, },
theme3hacks: {
description: 'Theme 3 hacks (need separation)',
type: 'object',
required: false,
default: {},
},
highlights: {
description: 'User highlights',
type: 'object',
required: false,
default: {},
},
} }
export const INSTANCE_DEFAULT_CONFIG = convertDefinitions( export const INSTANCE_DEFAULT_CONFIG = convertDefinitions(
INSTANCE_DEFAULT_CONFIG_DEFINITIONS, INSTANCE_DEFAULT_CONFIG_DEFINITIONS,
@ -663,6 +684,11 @@ export const SYNC_DEFAULT_CONFIG_DEFINITIONS = {
description: 'Collapse navigation panel to header only', description: 'Collapse navigation panel to header only',
default: false, default: false,
}, },
muteFilters: {
description: 'Object containing mute filters',
type: 'object',
default: {},
}
} }
export const SYNC_DEFAULT_CONFIG = convertDefinitions( export const SYNC_DEFAULT_CONFIG = convertDefinitions(
SYNC_DEFAULT_CONFIG_DEFINITIONS, SYNC_DEFAULT_CONFIG_DEFINITIONS,
@ -731,14 +757,27 @@ export const ROOT_CONFIG_DEFINITIONS = {
export const validateSetting = ({ export const validateSetting = ({
value, value,
path, path: fullPath,
definition, definition,
throwError, throwError,
defaultState, defaultState,
validateObjects = true,
}) => { }) => {
if (value === undefined) return // only null is allowed as missing value const path = fullPath.replace(/^simple./, '')
if (get(defaultState, path) === undefined) { if (validateObjects && definition.type === 'object' && path.split('.').length <= 1) {
const string = `Unknown instance option ${path}, value: ${value}` console.error(`attempt to set object ${fullPath} instead of its children. ignoring.`)
return undefined
}
if (path.includes('muteFilters')) {
console.log('##', path, value, definition)
console.log(value)
console.log(path)
console.log('====')
}
if (value === undefined) return undefined // only null is allowed as missing value
if (get(defaultState, path.split('.')[0]) === undefined) {
const string = `Unknown option ${fullPath}, value: ${value}`
if (throwError) { if (throwError) {
throw new Error(string) throw new Error(string)

View file

@ -201,6 +201,7 @@ export const useInstanceStore = defineStore('instance', {
definition, definition,
throwError: true, throwError: true,
defaultState: DEFAULT_STATE, defaultState: DEFAULT_STATE,
validateObjects: false,
}) })
set(this, path, finalValue) set(this, path, finalValue)

View file

@ -5,18 +5,10 @@ import { useLocalConfigStore } from 'src/stores/local_config.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js' import { useSyncConfigStore } from 'src/stores/sync_config.js'
import { import {
INSTANCE_DEFAULT_CONFIG,
LOCAL_DEFAULT_CONFIG,
LOCAL_ONLY_KEYS, LOCAL_ONLY_KEYS,
THEME_CONFIG, ROOT_CONFIG,
} from 'src/modules/default_config_state.js' } from 'src/modules/default_config_state.js'
const ROOT_CONFIG = {
...INSTANCE_DEFAULT_CONFIG,
...LOCAL_DEFAULT_CONFIG,
...THEME_CONFIG,
}
export const useMergedConfigStore = defineStore('merged_config', { export const useMergedConfigStore = defineStore('merged_config', {
getters: { getters: {
mergedConfig: () => { mergedConfig: () => {
@ -50,16 +42,15 @@ export const useMergedConfigStore = defineStore('merged_config', {
return result return result
}, },
mergedConfigWithoutDefaults: () => { mergedConfigWithoutDefaults: () => {
const instancePrefs = useInstanceStore().prefsStorage
const tempPrefs = useLocalConfigStore().tempStorage const tempPrefs = useLocalConfigStore().tempStorage
const localPrefs = useLocalConfigStore().prefsStorage const localPrefs = useLocalConfigStore().prefsStorage
const syncPrefs = useSyncConfigStore().prefsStorage const syncPrefs = useSyncConfigStore().prefsStorage
const getValue = (k) => const getValue = (k) =>
tempPrefs[k] ?? localPrefs[k] ?? syncPrefs.simple[k] ?? instancePrefs[k] tempPrefs[k] ?? localPrefs[k] ?? syncPrefs.simple[k]
const result = Object.fromEntries( const result = Object.fromEntries(
Object.keys(ROOT_CONFIG).map(([k, value]) => [k, getValue(k)]), Object.keys(ROOT_CONFIG).map((k) => [k, getValue(k)]),
) )
return result return result
}, },

View file

@ -295,12 +295,14 @@ export const _mergePrefs = (recent, stale) => {
const entry = path.split('.')[1] const entry = path.split('.')[1]
if (operation === 'unset') return ROOT_CONFIG[entry] !== undefined if (operation === 'unset') return ROOT_CONFIG[entry] !== undefined
if (operation !== 'set') return true
const definition = path.startsWith('simple.muteFilters') const definition = path.startsWith('simple.muteFilters')
? { default: {} } ? { default: {} }
: ROOT_CONFIG_DEFINITIONS[entry] : ROOT_CONFIG_DEFINITIONS[entry]
const finalValue = validateSetting({ const finalValue = validateSetting({
path: entry, path,
value: args[0], value: args[0],
definition, definition,
throwError: false, throwError: false,
@ -507,12 +509,14 @@ export const useSyncConfigStore = defineStore('sync_config', {
) )
} }
if (path.startsWith('collections.')) return value
const definition = path.startsWith('simple.muteFilters') const definition = path.startsWith('simple.muteFilters')
? { default: {} } ? { default: {} }
: ROOT_CONFIG_DEFINITIONS[path.split('.')[1]] : ROOT_CONFIG_DEFINITIONS[path.split('.')[1]]
const finalValue = validateSetting({ const finalValue = validateSetting({
path: path.split('.')[1], path,
value, value,
definition, definition,
throwError: false, throwError: false,
@ -773,21 +777,19 @@ export const useSyncConfigStore = defineStore('sync_config', {
afterLoad(state) { afterLoad(state) {
console.debug('Validating persisted state of SyncConfig') console.debug('Validating persisted state of SyncConfig')
const newState = { ...state } const newState = { ...state }
const newEntries = Object.entries(newState.prefsStorage.simple).map( const newEntries = Object.entries(ROOT_CONFIG).map(
([path, value]) => { ([path, value]) => {
if (path === 'muteFilters') {
return value
}
const definition = ROOT_CONFIG_DEFINITIONS[path] const definition = ROOT_CONFIG_DEFINITIONS[path]
const finalValue = validateSetting({ const finalValue = validateSetting({
path, path,
value, value: newState.prefsStorage.simple[path],
definition, definition,
throwError: false, throwError: false,
validateObjects: false,
defaultState: ROOT_CONFIG, defaultState: ROOT_CONFIG,
}) })
return finalValue === undefined ? undefined : [path, finalValue] return finalValue === undefined ? definition.default : [path, finalValue]
}, },
) )
newState.prefsStorage.simple = Object.fromEntries( newState.prefsStorage.simple = Object.fromEntries(

View file

@ -241,7 +241,9 @@ describe('The SyncConfig store', () => {
store.setPreference({ path: 'simple.fontInput.family', value: 'test' }) store.setPreference({ path: 'simple.fontInput.family', value: 'test' })
store.unsetPreference({ path: 'simple.fontInput.family' }) store.unsetPreference({ path: 'simple.fontInput.family' })
store.updateCache(store, { username: 'test' }) store.updateCache(store, { username: 'test' })
expect(store.prefsStorage.simple.fontInput).to.not.have.property('family') expect(store.prefsStorage.simple.fontInput).to.not.have.property(
'family',
)
expect(store.prefsStorage._journal.length).to.eql(1) expect(store.prefsStorage._journal.length).to.eql(1)
}) })
@ -395,7 +397,12 @@ describe('The SyncConfig store', () => {
{ {
simple: { theme: '1', style: '0', hideISP: true }, simple: { theme: '1', style: '0', hideISP: true },
_journal: [ _journal: [
{ path: 'simple.style', operation: 'set', args: ['0'], timestamp: 2 }, {
path: 'simple.style',
operation: 'set',
args: ['0'],
timestamp: 2,
},
{ {
path: 'simple.hideISP', path: 'simple.hideISP',
operation: 'set', operation: 'set',
@ -408,17 +415,42 @@ describe('The SyncConfig store', () => {
{ {
simple: { theme: '1', style: '1', hideISP: false }, simple: { theme: '1', style: '1', hideISP: false },
_journal: [ _journal: [
{ path: 'simple.theme', operation: 'set', args: ['1'], timestamp: 1 }, {
{ path: 'simple.style', operation: 'set', args: ['1'], timestamp: 3 }, path: 'simple.theme',
operation: 'set',
args: ['1'],
timestamp: 1,
},
{
path: 'simple.style',
operation: 'set',
args: ['1'],
timestamp: 3,
},
], ],
}, },
), ),
).to.eql({ ).to.eql({
simple: { theme: '1', style: '1', hideISP: true }, simple: { theme: '1', style: '1', hideISP: true },
_journal: [ _journal: [
{ path: 'simple.theme', operation: 'set', args: ['1'], timestamp: 1 }, {
{ path: 'simple.style', operation: 'set', args: ['1'], timestamp: 3 }, path: 'simple.theme',
{ path: 'simple.hideISP', operation: 'set', args: [true], timestamp: 4 }, operation: 'set',
args: ['1'],
timestamp: 1,
},
{
path: 'simple.style',
operation: 'set',
args: ['1'],
timestamp: 3,
},
{
path: 'simple.hideISP',
operation: 'set',
args: [true],
timestamp: 4,
},
], ],
}) })
}) })
@ -430,7 +462,12 @@ describe('The SyncConfig store', () => {
{ {
simple: { theme: '1', style: '0', hideISP: false }, simple: { theme: '1', style: '0', hideISP: false },
_journal: [ _journal: [
{ path: 'simple.style', operation: 'set', args: ['0'], timestamp: 2 }, {
path: 'simple.style',
operation: 'set',
args: ['0'],
timestamp: 2,
},
{ {
path: 'simple.hideISP', path: 'simple.hideISP',
operation: 'set', operation: 'set',
@ -443,18 +480,42 @@ describe('The SyncConfig store', () => {
{ {
simple: { theme: '0', style: '0', hideISP: true }, simple: { theme: '0', style: '0', hideISP: true },
_journal: [ _journal: [
{ path: 'simple.theme', operation: 'set', args: ['0'], timestamp: 1 }, {
{ path: 'simple.style', operation: 'set', args: ['0'], timestamp: 3 }, path: 'simple.theme',
operation: 'set',
args: ['0'],
timestamp: 1,
},
{
path: 'simple.style',
operation: 'set',
args: ['0'],
timestamp: 3,
},
], ],
}, },
), ),
).to.eql({ ).to.eql({
simple: { a: 0, b: 0, c: false },
simple: { theme: '0', style: '0', hideISP: false }, simple: { theme: '0', style: '0', hideISP: false },
_journal: [ _journal: [
{ path: 'simple.theme', operation: 'set', args: ['0'], timestamp: 1 }, {
{ path: 'simple.style', operation: 'set', args: ['0'], timestamp: 3 }, path: 'simple.theme',
{ path: 'simple.hideISP', operation: 'set', args: [false], timestamp: 4 }, operation: 'set',
args: ['0'],
timestamp: 1,
},
{
path: 'simple.style',
operation: 'set',
args: ['0'],
timestamp: 3,
},
{
path: 'simple.hideISP',
operation: 'set',
args: [false],
timestamp: 4,
},
], ],
}) })
}) })
@ -490,7 +551,12 @@ describe('The SyncConfig store', () => {
).to.eql({ ).to.eql({
simple: { theme: 'bar' }, simple: { theme: 'bar' },
_journal: [ _journal: [
{ path: 'simple.theme', operation: 'set', args: ['bar'], timestamp: 4 }, {
path: 'simple.theme',
operation: 'set',
args: ['bar'],
timestamp: 4,
},
], ],
}) })
}) })