pleroma-fe/src/stores/serverSideStorage.js

644 lines
19 KiB
JavaScript
Raw Normal View History

2025-01-11 20:02:53 +02:00
import {
2026-01-06 16:23:17 +02:00
merge as _merge,
2025-01-11 20:02:53 +02:00
clamp,
2026-01-06 16:23:17 +02:00
cloneDeep,
findLastIndex,
2025-01-11 20:02:53 +02:00
flatten,
2026-01-06 16:23:17 +02:00
get,
2025-01-11 20:02:53 +02:00
groupBy,
2026-01-06 16:23:17 +02:00
isEqual,
set,
2025-01-11 20:02:53 +02:00
takeRight,
2025-01-26 22:26:17 +02:00
uniqWith,
2026-01-06 16:23:17 +02:00
unset,
2025-01-11 20:02:53 +02:00
} from 'lodash'
2026-01-06 16:23:17 +02:00
import { defineStore } from 'pinia'
2022-08-04 22:09:42 +03:00
import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js'
2026-01-06 16:23:17 +02:00
import { toRaw } from 'vue'
2022-08-04 01:56:52 +03:00
2022-08-04 17:20:11 +03:00
export const VERSION = 1
2022-08-04 22:09:42 +03:00
export const NEW_USER_DATE = new Date('2022-08-04') // date of writing this, basically
2022-08-04 01:56:52 +03:00
2022-08-04 17:20:11 +03:00
export const COMMAND_TRIM_FLAGS = 1000
export const COMMAND_TRIM_FLAGS_AND_RESET = 1001
2022-08-04 01:56:52 +03:00
2022-08-04 22:09:42 +03:00
export const defaultState = {
2022-08-04 17:20:11 +03:00
// do we need to update data on server?
2022-08-04 01:56:52 +03:00
dirty: false,
// storage of flags - stuff that can only be set and incremented
flagStorage: {
updateCounter: 0, // Counter for most recent update notification seen
2025-03-25 19:01:32 +02:00
configMigration: 0, // Counter for config -> server-side migrations
2026-01-06 16:22:52 +02:00
reset: 0, // special flag that can be used to force-reset all flags, debug purposes only
2022-08-04 01:56:52 +03:00
// special reset codes:
// 1000: trim keys to those known by currently running FE
// 1001: same as above + reset everything to 0
},
2022-08-10 02:19:07 +03:00
prefsStorage: {
_journal: [],
simple: {
2022-08-11 18:06:28 +03:00
dontShowUpdateNotifs: false,
collapseNav: false,
2026-01-06 16:22:52 +02:00
muteFilters: {},
},
collections: {
2025-01-11 20:02:53 +02:00
pinnedStatusActions: ['reply', 'retweet', 'favorite', 'emoji'],
2026-01-06 16:22:52 +02:00
pinnedNavItems: ['home', 'dms', 'chats'],
},
2022-08-10 02:19:07 +03:00
},
2022-08-04 01:56:52 +03:00
// raw data
raw: null,
// local cache
2026-01-06 16:22:52 +02:00
cache: null,
2022-08-04 01:56:52 +03:00
}
2022-08-04 22:09:42 +03:00
export const newUserFlags = {
2022-08-04 01:56:52 +03:00
...defaultState.flagStorage,
2026-01-06 16:22:52 +02:00
updateCounter: CURRENT_UPDATE_COUNTER, // new users don't need to see update notification
2022-08-04 01:56:52 +03:00
}
export const _moveItemInArray = (array, value, movement) => {
const oldIndex = array.indexOf(value)
const newIndex = oldIndex + movement
const newArray = [...array]
// remove old
newArray.splice(oldIndex, 1)
// add new
newArray.splice(clamp(newIndex, 0, newArray.length + 1), 0, value)
return newArray
}
2022-08-12 01:19:19 +03:00
const _wrapData = (data, userName) => ({
2022-08-04 17:20:11 +03:00
...data,
2022-08-12 01:19:19 +03:00
_user: userName,
2022-08-04 17:20:11 +03:00
_timestamp: Date.now(),
2026-01-06 16:22:52 +02:00
_version: VERSION,
2022-08-04 17:20:11 +03:00
})
2022-08-04 22:09:42 +03:00
const _checkValidity = (data) => data._timestamp > 0 && data._version > 0
2022-08-04 17:20:11 +03:00
2022-08-11 18:06:28 +03:00
const _verifyPrefs = (state) => {
state.prefsStorage = state.prefsStorage || {
simple: {},
2026-01-06 16:22:52 +02:00
collections: {},
2022-08-11 18:06:28 +03:00
}
// Simple
2022-08-11 18:06:28 +03:00
Object.entries(defaultState.prefsStorage.simple).forEach(([k, v]) => {
if (typeof v === 'number' || typeof v === 'boolean') return
if (typeof v === 'object' && v != null) return
2022-08-11 18:06:28 +03:00
console.warn(`Preference simple.${k} as invalid type, reinitializing`)
set(state.prefsStorage.simple, k, defaultState.prefsStorage.simple[k])
})
// Collections
2022-08-11 18:06:28 +03:00
Object.entries(defaultState.prefsStorage.collections).forEach(([k, v]) => {
if (Array.isArray(v)) return
console.warn(`Preference collections.${k} as invalid type, reinitializing`)
2026-01-06 16:22:52 +02:00
set(
state.prefsStorage.collections,
k,
defaultState.prefsStorage.collections[k],
)
2022-08-11 18:06:28 +03:00
})
}
2025-01-19 18:22:30 +02:00
export const _getRecentData = (cache, live, isTest) => {
2022-08-04 17:20:11 +03:00
const result = { recent: null, stale: null, needUpload: false }
const cacheValid = _checkValidity(cache || {})
const liveValid = _checkValidity(live || {})
2022-08-04 22:09:42 +03:00
if (!liveValid && cacheValid) {
2022-08-04 17:20:11 +03:00
result.needUpload = true
2026-01-06 16:22:52 +02:00
console.debug(
'Nothing valid stored on server, assuming cache to be source of truth',
)
2022-08-04 17:20:11 +03:00
result.recent = cache
result.stale = live
2022-08-04 22:09:42 +03:00
} else if (!cacheValid && liveValid) {
2026-01-06 16:22:52 +02:00
console.debug(
'Valid storage on server found, no local cache found, using live as source of truth',
)
2022-08-04 17:20:11 +03:00
result.recent = live
result.stale = cache
2022-08-04 22:09:42 +03:00
} else if (cacheValid && liveValid) {
2022-08-04 17:20:11 +03:00
console.debug('Both sources have valid data, figuring things out...')
2026-01-06 16:22:52 +02:00
if (
live._timestamp === cache._timestamp &&
live._version === cache._version
) {
console.debug(
'Same version/timestamp on both source, source of truth irrelevant',
)
2022-08-04 17:20:11 +03:00
result.recent = cache
result.stale = live
} else {
2026-01-06 16:22:52 +02:00
console.debug(
'Different timestamp, figuring out which one is more recent',
)
2022-08-04 17:20:11 +03:00
if (live._timestamp < cache._timestamp) {
result.recent = cache
result.stale = live
} else {
result.recent = live
result.stale = cache
}
}
2022-08-04 22:09:42 +03:00
} else {
console.debug('Both sources are invalid, start from scratch')
result.needUpload = true
2022-08-04 17:20:11 +03:00
}
2025-01-11 20:02:53 +02:00
2025-01-12 01:46:10 +02:00
const merge = (a, b) => ({
2025-01-28 17:36:07 +02:00
_user: a._user ?? b._user,
2025-01-19 18:22:30 +02:00
_version: a._version ?? b._version,
_timestamp: a._timestamp ?? b._timestamp,
2025-01-12 01:46:10 +02:00
needUpload: b.needUpload ?? a.needUpload,
2025-01-26 22:26:17 +02:00
prefsStorage: _merge(a.prefsStorage, b.prefsStorage),
2026-01-06 16:22:52 +02:00
flagStorage: _merge(a.flagStorage, b.flagStorage),
2025-01-12 01:46:10 +02:00
})
2026-01-06 16:22:52 +02:00
result.recent = isTest
? result.recent
: result.recent && merge(defaultState, result.recent)
result.stale = isTest
? result.stale
: result.stale && merge(defaultState, result.stale)
2025-01-11 20:02:53 +02:00
2025-01-12 01:46:10 +02:00
return result
2022-08-04 17:20:11 +03:00
}
2022-08-04 22:09:42 +03:00
export const _getAllFlags = (recent, stale) => {
2026-01-06 16:22:52 +02:00
return Array.from(
new Set([
...Object.keys(toRaw((recent || {}).flagStorage || {})),
...Object.keys(toRaw((stale || {}).flagStorage || {})),
]),
)
2022-08-04 17:20:11 +03:00
}
export const _mergeFlags = (recent, stale, allFlagKeys) => {
2022-08-10 02:59:08 +03:00
if (!stale.flagStorage) return recent.flagStorage
if (!recent.flagStorage) return stale.flagStorage
2026-01-06 16:22:52 +02:00
return Object.fromEntries(
allFlagKeys.map((flag) => {
const recentFlag = recent.flagStorage[flag]
const staleFlag = stale.flagStorage[flag]
// use flag that is of higher value
return [
flag,
Number((recentFlag > staleFlag ? recentFlag : staleFlag) || 0),
]
}),
)
2022-08-04 17:20:11 +03:00
}
const _mergeJournal = (...journals) => {
2022-08-16 19:33:34 +03:00
// Ignore invalid journal entries
const allJournals = flatten(
2026-01-06 16:22:52 +02:00
journals.map((j) => (Array.isArray(j) ? j : [])),
).filter(
(entry) =>
Object.hasOwn(entry, 'path') &&
Object.hasOwn(entry, 'operation') &&
Object.hasOwn(entry, 'args') &&
Object.hasOwn(entry, 'timestamp'),
2022-08-16 19:33:34 +03:00
)
const grouped = groupBy(allJournals, 'path')
const trimmedGrouped = Object.entries(grouped).map(([path, journal]) => {
// side effect
2026-01-06 16:22:52 +02:00
journal.sort((a, b) => (a.timestamp > b.timestamp ? 1 : -1))
if (path.startsWith('collections')) {
2026-01-06 16:22:52 +02:00
const lastRemoveIndex = findLastIndex(
journal,
({ operation }) => operation === 'removeFromCollection',
)
// everything before last remove is unimportant
let remainder
if (lastRemoveIndex > 0) {
remainder = journal.slice(lastRemoveIndex)
} else {
// everything else doesn't need trimming
remainder = journal
}
return uniqWith(remainder, (a, b) => {
2026-01-06 16:22:52 +02:00
if (a.path !== b.path) {
return false
}
if (a.operation !== b.operation) {
return false
}
if (a.operation === 'addToCollection') {
return a.args[0] === b.args[0]
}
return false
})
} else if (path.startsWith('simple')) {
// Only the last record is important
return takeRight(journal)
} else {
return journal
}
})
2026-01-06 16:22:52 +02:00
return flatten(trimmedGrouped).sort((a, b) =>
a.timestamp > b.timestamp ? 1 : -1,
)
}
2025-02-04 15:23:21 +02:00
export const _mergePrefs = (recent, stale) => {
2022-08-10 02:19:07 +03:00
if (!stale) return recent
if (!recent) return stale
const { _journal: recentJournal, ...recentData } = recent
const { _journal: staleJournal } = stale
/** Journal entry format:
* path: path to entry in prefsStorage
* timestamp: timestamp of the change
* operation: operation type
* arguments: array of arguments, depends on operation type
*
* currently only supported operation type is "set" which just sets the value
* to requested one. Intended only to be used with simple preferences (boolean, number)
* shouldn't be used with collections!
*/
const resultOutput = { ...recentData }
const totalJournal = _mergeJournal(staleJournal, recentJournal)
2025-02-04 15:23:21 +02:00
totalJournal.forEach(({ path, operation, args }) => {
2022-08-10 02:19:07 +03:00
if (path.startsWith('_')) {
2026-01-06 16:22:52 +02:00
throw new Error(
`journal contains entry to edit internal (starts with _) field '${path}', something is incorrect here, ignoring.`,
)
2022-08-10 02:19:07 +03:00
}
switch (operation) {
case 'set':
2026-01-06 16:22:52 +02:00
if (
path.startsWith('collections') ||
path.startsWith('objectCollections')
) {
2025-03-23 17:23:47 +02:00
throw new Error('Illegal operation "set" on a collection')
}
if (path.split(/\./g).length <= 1) {
2026-01-06 16:22:52 +02:00
throw new Error(
`Calling set on depth <= 1 (path: ${path}) is not allowed`,
)
}
2022-08-10 02:19:07 +03:00
set(resultOutput, path, args[0])
break
case 'unset':
2026-01-06 16:22:52 +02:00
if (
path.startsWith('collections') ||
path.startsWith('objectCollections')
) {
2025-03-23 17:23:47 +02:00
throw new Error('Illegal operation "unset" on a collection')
}
if (path.split(/\./g).length <= 2) {
2026-01-06 16:22:52 +02:00
throw new Error(
`Calling unset on depth <= 2 (path: ${path}) is not allowed`,
)
}
unset(resultOutput, path)
break
case 'addToCollection':
2026-01-06 16:22:52 +02:00
set(
resultOutput,
path,
Array.from(new Set(get(resultOutput, path)).add(args[0])),
)
break
2022-08-11 18:06:28 +03:00
case 'removeFromCollection': {
const newSet = new Set(get(resultOutput, path))
newSet.delete(args[0])
set(resultOutput, path, Array.from(newSet))
break
2022-08-11 18:06:28 +03:00
}
case 'reorderCollection': {
const [value, movement] = args
2026-01-06 16:22:52 +02:00
set(
resultOutput,
path,
_moveItemInArray(get(resultOutput, path), value, movement),
)
break
}
2022-08-10 02:19:07 +03:00
default:
2026-01-06 16:22:52 +02:00
throw new Error(
`Unknown journal operation: '${operation}', did we forget to run reverse migrations beforehand?`,
)
2022-08-10 02:19:07 +03:00
}
})
return { ...resultOutput, _journal: totalJournal }
}
2026-01-06 16:22:52 +02:00
export const _resetFlags = (
totalFlags,
knownKeys = defaultState.flagStorage,
) => {
2022-08-04 22:09:42 +03:00
let result = { ...totalFlags }
const allFlagKeys = Object.keys(totalFlags)
2022-08-04 17:20:11 +03:00
// flag reset functionality
2026-01-06 16:22:52 +02:00
if (
totalFlags.reset >= COMMAND_TRIM_FLAGS &&
totalFlags.reset <= COMMAND_TRIM_FLAGS_AND_RESET
) {
2022-08-04 17:20:11 +03:00
console.debug('Received command to trim the flags')
2022-08-04 22:09:42 +03:00
const knownKeysSet = new Set(Object.keys(knownKeys))
// Trim
result = {}
2026-01-06 16:22:52 +02:00
allFlagKeys.forEach((flag) => {
2022-08-04 22:09:42 +03:00
if (knownKeysSet.has(flag)) {
result[flag] = totalFlags[flag]
2022-08-04 17:20:11 +03:00
}
})
2022-08-04 22:09:42 +03:00
// Reset
2022-08-04 17:20:11 +03:00
if (totalFlags.reset === COMMAND_TRIM_FLAGS_AND_RESET) {
// 1001 - and reset everything to 0
console.debug('Received command to reset the flags')
2026-01-06 16:22:52 +02:00
Object.keys(knownKeys).forEach((flag) => {
result[flag] = 0
})
2022-08-04 17:20:11 +03:00
}
} else if (totalFlags.reset > 0 && totalFlags.reset < 9000) {
console.debug('Received command to reset the flags')
2026-01-06 16:22:52 +02:00
allFlagKeys.forEach((flag) => {
result[flag] = 0
})
2022-08-04 17:20:11 +03:00
}
2022-08-04 22:09:42 +03:00
result.reset = 0
return result
2022-08-04 17:20:11 +03:00
}
export const _doMigrations = (cache) => {
2022-08-04 22:09:42 +03:00
if (!cache) return cache
2022-08-04 17:20:11 +03:00
if (cache._version < VERSION) {
2026-01-06 16:22:52 +02:00
console.debug(
'Local cached data has older version, seeing if there any migrations that can be applied',
)
2022-08-04 17:20:11 +03:00
// no migrations right now since we only have one version
console.debug('No migrations found')
}
if (cache._version > VERSION) {
2026-01-06 16:22:52 +02:00
console.debug(
'Local cached data has newer version, seeing if there any reverse migrations that can be applied',
)
2022-08-04 17:20:11 +03:00
// no reverse migrations right now but we leave a possibility of loading a hotpatch if need be
if (window._PLEROMA_HOTPATCH) {
if (window._PLEROMA_HOTPATCH.reverseMigrations) {
console.debug('Found hotpatch migration, applying')
2026-01-06 16:22:52 +02:00
return window._PLEROMA_HOTPATCH.reverseMigrations.call(
{},
'serverSideStorage',
{ from: cache._version, to: VERSION },
cache,
)
2022-08-04 17:20:11 +03:00
}
}
}
return cache
}
2025-03-23 20:03:25 +02:00
export const useServerSideStorageStore = defineStore('serverSideStorage', {
state() {
return cloneDeep(defaultState)
2022-08-12 01:19:19 +03:00
},
2025-03-23 20:03:25 +02:00
actions: {
2026-01-06 16:22:52 +02:00
setFlag({ flag, value }) {
2025-03-23 20:03:25 +02:00
this.flagStorage[flag] = value
this.dirty = true
},
2026-01-06 16:22:52 +02:00
setPreference({ path, value }) {
2025-03-23 20:03:25 +02:00
if (path.startsWith('_')) {
2026-01-06 16:22:52 +02:00
throw new Error(
`Tried to edit internal (starts with _) field '${path}', ignoring.`,
)
2025-03-23 20:03:25 +02:00
}
2026-01-06 16:22:52 +02:00
if (
path.startsWith('collections') ||
path.startsWith('objectCollections')
) {
throw new Error(
`Invalid operation 'set' for collection field '${path}', ignoring.`,
)
2025-03-23 20:03:25 +02:00
}
if (path.split(/\./g).length <= 1) {
2026-01-06 16:22:52 +02:00
throw new Error(
`Calling set on depth <= 1 (path: ${path}) is not allowed`,
)
2025-03-23 20:03:25 +02:00
}
if (path.split(/\./g).length > 3) {
2026-01-06 16:22:52 +02:00
throw new Error(
`Calling set on depth > 3 (path: ${path}) is not allowed`,
)
2025-03-23 20:03:25 +02:00
}
set(this.prefsStorage, path, value)
this.prefsStorage._journal = [
...this.prefsStorage._journal,
2026-01-06 16:22:52 +02:00
{ operation: 'set', path, args: [value], timestamp: Date.now() },
2025-03-23 20:03:25 +02:00
]
this.dirty = true
},
2026-01-06 16:22:52 +02:00
unsetPreference({ path, value }) {
2025-03-23 20:03:25 +02:00
if (path.startsWith('_')) {
2026-01-06 16:22:52 +02:00
throw new Error(
`Tried to edit internal (starts with _) field '${path}', ignoring.`,
)
2025-03-23 20:03:25 +02:00
}
2026-01-06 16:22:52 +02:00
if (
path.startsWith('collections') ||
path.startsWith('objectCollections')
) {
throw new Error(
`Invalid operation 'unset' for collection field '${path}', ignoring.`,
)
2025-03-23 20:03:25 +02:00
}
if (path.split(/\./g).length <= 2) {
2026-01-06 16:22:52 +02:00
throw new Error(
`Calling unset on depth <= 2 (path: ${path}) is not allowed`,
)
2025-03-23 20:03:25 +02:00
}
if (path.split(/\./g).length > 3) {
2026-01-06 16:22:52 +02:00
throw new Error(
`Calling unset on depth > 3 (path: ${path}) is not allowed`,
)
2025-03-23 20:03:25 +02:00
}
unset(this.prefsStorage, path, value)
this.prefsStorage._journal = [
...this.prefsStorage._journal,
2026-01-06 16:22:52 +02:00
{ operation: 'unset', path, args: [], timestamp: Date.now() },
2025-03-23 20:03:25 +02:00
]
this.dirty = true
},
2026-01-06 16:22:52 +02:00
addCollectionPreference({ path, value }) {
2025-03-23 20:03:25 +02:00
if (path.startsWith('_')) {
2026-01-06 16:22:52 +02:00
throw new Error(
`tried to edit internal (starts with _) field '${path}'`,
)
2025-03-23 20:03:25 +02:00
}
if (path.startsWith('collections')) {
const collection = new Set(get(this.prefsStorage, path))
collection.add(value)
set(this.prefsStorage, path, [...collection])
} else if (path.startsWith('objectCollections')) {
const { _key } = value
if (!_key && typeof _key !== 'string') {
throw new Error('Object for storage is missing _key field!')
}
const collection = new Set(get(this.prefsStorage, path + '.index'))
collection.add(_key)
set(this.prefsStorage, path + '.index', [...collection])
set(this.prefsStorage, path + '.data.' + _key, value)
}
this.prefsStorage._journal = [
...this.prefsStorage._journal,
2026-01-06 16:22:52 +02:00
{
operation: 'addToCollection',
path,
args: [value],
timestamp: Date.now(),
},
2025-03-23 20:03:25 +02:00
]
this.dirty = true
},
2026-01-06 16:22:52 +02:00
removeCollectionPreference({ path, value }) {
2025-03-23 20:03:25 +02:00
if (path.startsWith('_')) {
2026-01-06 16:22:52 +02:00
throw new Error(
`tried to edit internal (starts with _) field '${path}', ignoring.`,
)
2025-03-23 20:03:25 +02:00
}
const collection = new Set(get(this.prefsStorage, path))
collection.delete(value)
set(this.prefsStorage, path, [...collection])
this.prefsStorage._journal = [
...this.prefsStorage._journal,
2026-01-06 16:22:52 +02:00
{
operation: 'removeFromCollection',
path,
args: [value],
timestamp: Date.now(),
},
2025-03-23 20:03:25 +02:00
]
this.dirty = true
},
2026-01-06 16:22:52 +02:00
reorderCollectionPreference({ path, value, movement }) {
2025-03-23 20:03:25 +02:00
if (path.startsWith('_')) {
2026-01-06 16:22:52 +02:00
throw new Error(
`tried to edit internal (starts with _) field '${path}', ignoring.`,
)
2025-03-23 20:03:25 +02:00
}
const collection = get(this.prefsStorage, path)
const newCollection = _moveItemInArray(collection, value, movement)
set(this.prefsStorage, path, newCollection)
this.prefsStorage._journal = [
...this.prefsStorage._journal,
2026-01-06 16:22:52 +02:00
{
operation: 'arrangeCollection',
path,
args: [value],
timestamp: Date.now(),
},
2025-03-23 20:03:25 +02:00
]
this.dirty = true
},
2026-01-06 16:22:52 +02:00
updateCache({ username }) {
2025-03-23 20:03:25 +02:00
this.prefsStorage._journal = _mergeJournal(this.prefsStorage._journal)
2026-01-06 16:22:52 +02:00
this.cache = _wrapData(
{
flagStorage: toRaw(this.flagStorage),
prefsStorage: toRaw(this.prefsStorage),
},
username,
)
2025-03-23 20:03:25 +02:00
},
2026-01-06 16:22:52 +02:00
clearServerSideStorage() {
2025-03-24 01:12:26 +02:00
const blankState = { ...cloneDeep(defaultState) }
2026-01-06 16:22:52 +02:00
Object.keys(this).forEach((k) => {
2025-03-24 01:12:26 +02:00
this[k] = blankState[k]
})
},
2026-01-06 16:22:52 +02:00
setServerSideStorage(userData) {
2025-03-24 01:12:26 +02:00
const live = userData.storage
this.raw = live
let cache = this.cache
if (cache && cache._user !== userData.fqn) {
2026-01-06 16:22:52 +02:00
console.warn(
'Cache belongs to another user! reinitializing local cache!',
)
2025-03-24 01:12:26 +02:00
cache = null
}
cache = _doMigrations(cache)
let { recent, stale, needUpload } = _getRecentData(cache, live)
const userNew = userData.created_at > NEW_USER_DATE
const flagsTemplate = userNew ? newUserFlags : defaultState.flagStorage
let dirty = false
if (recent === null) {
2026-01-06 16:22:52 +02:00
console.debug(
`Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`,
)
2025-03-24 01:12:26 +02:00
recent = _wrapData({
flagStorage: { ...flagsTemplate },
2026-01-06 16:22:52 +02:00
prefsStorage: { ...defaultState.prefsStorage },
2025-03-24 01:12:26 +02:00
})
}
if (!needUpload && recent && stale) {
console.debug('Checking if data needs merging...')
// discarding timestamps and versions
/* eslint-disable no-unused-vars */
const { _timestamp: _0, _version: _1, ...recentData } = recent
const { _timestamp: _2, _version: _3, ...staleData } = stale
/* eslint-enable no-unused-vars */
dirty = !isEqual(recentData, staleData)
2026-01-06 16:22:52 +02:00
console.debug(`Data ${dirty ? 'needs' : "doesn't need"} merging`)
2025-03-24 01:12:26 +02:00
}
const allFlagKeys = _getAllFlags(recent, stale)
let totalFlags
let totalPrefs
if (dirty) {
// Merge the flags
console.debug('Merging the data...')
totalFlags = _mergeFlags(recent, stale, allFlagKeys)
_verifyPrefs(recent)
_verifyPrefs(stale)
totalPrefs = _mergePrefs(recent.prefsStorage, stale.prefsStorage)
} else {
totalFlags = recent.flagStorage
totalPrefs = recent.prefsStorage
}
totalFlags = _resetFlags(totalFlags)
recent.flagStorage = { ...flagsTemplate, ...totalFlags }
recent.prefsStorage = { ...defaultState.prefsStorage, ...totalPrefs }
this.dirty = dirty || needUpload
this.cache = recent
// set local timestamp to smaller one if we don't have any changes
if (stale && recent && !this.dirty) {
this.cache._timestamp = Math.min(stale._timestamp, recent._timestamp)
}
this.flagStorage = this.cache.flagStorage
this.prefsStorage = this.cache.prefsStorage
},
2026-01-06 16:22:52 +02:00
pushServerSideStorage({ force = false } = {}) {
2025-03-23 20:03:25 +02:00
const needPush = this.dirty || force
2022-08-04 01:56:52 +03:00
if (!needPush) return
2025-03-23 20:03:25 +02:00
this.updateCache({ username: window.vuex.state.users.currentUser.fqn })
const params = { pleroma_settings_store: { 'pleroma-fe': this.cache } }
window.vuex.state.api.backendInteractor
2025-08-10 17:42:37 +03:00
.updateProfileJSON({ params })
2022-08-11 18:06:28 +03:00
.then((user) => {
2025-03-23 20:03:25 +02:00
this.setServerSideStorage(user)
this.dirty = false
2022-08-11 18:06:28 +03:00
})
2026-01-06 16:22:52 +02:00
},
},
2025-03-23 20:03:25 +02:00
})