initial prefs storage work
This commit is contained in:
parent
750696643f
commit
aa41cedd93
@ -1,5 +1,5 @@
|
|||||||
import { toRaw } from 'vue'
|
import { toRaw } from 'vue'
|
||||||
import { isEqual, cloneDeep } from 'lodash'
|
import { isEqual, uniqBy, cloneDeep, set } from 'lodash'
|
||||||
import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js'
|
import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js'
|
||||||
|
|
||||||
export const VERSION = 1
|
export const VERSION = 1
|
||||||
@ -22,6 +22,10 @@ export const defaultState = {
|
|||||||
// 1000: trim keys to those known by currently running FE
|
// 1000: trim keys to those known by currently running FE
|
||||||
// 1001: same as above + reset everything to 0
|
// 1001: same as above + reset everything to 0
|
||||||
},
|
},
|
||||||
|
prefsStorage: {
|
||||||
|
_journal: [],
|
||||||
|
simple: {}
|
||||||
|
},
|
||||||
// raw data
|
// raw data
|
||||||
raw: null,
|
raw: null,
|
||||||
// local cache
|
// local cache
|
||||||
@ -93,6 +97,42 @@ export const _mergeFlags = (recent, stale, allFlagKeys) => {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const _mergePrefs = (recent, stale, allFlagKeys) => {
|
||||||
|
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 = uniqBy(
|
||||||
|
[...recentJournal, ...staleJournal].sort((a, b) => a.timestamp > b.timestamp ? -1 : 1),
|
||||||
|
'path'
|
||||||
|
).reverse()
|
||||||
|
totalJournal.forEach(({ path, timestamp, operation, args }) => {
|
||||||
|
if (path.startsWith('_')) {
|
||||||
|
console.error(`journal contains entry to edit internal (starts with _) field '${path}', something is incorrect here, ignoring.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch (operation) {
|
||||||
|
case 'set':
|
||||||
|
set(resultOutput, path, args[0])
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
console.error(`Unknown journal operation: '${operation}', did we forget to run reverse migrations beforehand?`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return { ...resultOutput, _journal: totalJournal }
|
||||||
|
}
|
||||||
|
|
||||||
export const _resetFlags = (totalFlags, knownKeys = defaultState.flagStorage) => {
|
export const _resetFlags = (totalFlags, knownKeys = defaultState.flagStorage) => {
|
||||||
let result = { ...totalFlags }
|
let result = { ...totalFlags }
|
||||||
const allFlagKeys = Object.keys(totalFlags)
|
const allFlagKeys = Object.keys(totalFlags)
|
||||||
@ -165,7 +205,8 @@ export const mutations = {
|
|||||||
if (recent === null) {
|
if (recent === null) {
|
||||||
console.debug(`Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`)
|
console.debug(`Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`)
|
||||||
recent = _wrapData({
|
recent = _wrapData({
|
||||||
flagStorage: { ...flagsTemplate }
|
flagStorage: { ...flagsTemplate },
|
||||||
|
prefsStorage: { ...defaultState.prefsStorage }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,17 +221,21 @@ export const mutations = {
|
|||||||
|
|
||||||
const allFlagKeys = _getAllFlags(recent, stale)
|
const allFlagKeys = _getAllFlags(recent, stale)
|
||||||
let totalFlags
|
let totalFlags
|
||||||
|
let totalPrefs
|
||||||
if (dirty) {
|
if (dirty) {
|
||||||
// Merge the flags
|
// Merge the flags
|
||||||
console.debug('Merging the flags...')
|
console.debug('Merging the data...')
|
||||||
totalFlags = _mergeFlags(recent, stale, allFlagKeys)
|
totalFlags = _mergeFlags(recent, stale, allFlagKeys)
|
||||||
|
totalPrefs = _mergePrefs(recent.prefsStorage, stale.prefsStorage)
|
||||||
} else {
|
} else {
|
||||||
totalFlags = recent.flagStorage
|
totalFlags = recent.flagStorage
|
||||||
|
totalPrefs = recent.prefsStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
totalFlags = _resetFlags(totalFlags)
|
totalFlags = _resetFlags(totalFlags)
|
||||||
|
|
||||||
recent.flagStorage = totalFlags
|
recent.flagStorage = totalFlags
|
||||||
|
recent.prefsStorage = totalPrefs
|
||||||
|
|
||||||
state.dirty = dirty || needsUpload
|
state.dirty = dirty || needsUpload
|
||||||
state.cache = recent
|
state.cache = recent
|
||||||
@ -216,7 +261,8 @@ const serverSideStorage = {
|
|||||||
const needPush = state.dirty || force
|
const needPush = state.dirty || force
|
||||||
if (!needPush) return
|
if (!needPush) return
|
||||||
state.cache = _wrapData({
|
state.cache = _wrapData({
|
||||||
flagStorage: toRaw(state.flagStorage)
|
flagStorage: toRaw(state.flagStorage),
|
||||||
|
prefsStorage: toRaw(state.flagStorage)
|
||||||
})
|
})
|
||||||
const params = { pleroma_settings_store: { 'pleroma-fe': state.cache } }
|
const params = { pleroma_settings_store: { 'pleroma-fe': state.cache } }
|
||||||
rootState.api.backendInteractor
|
rootState.api.backendInteractor
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
_getRecentData,
|
_getRecentData,
|
||||||
_getAllFlags,
|
_getAllFlags,
|
||||||
_mergeFlags,
|
_mergeFlags,
|
||||||
|
_mergePrefs,
|
||||||
_resetFlags,
|
_resetFlags,
|
||||||
mutations,
|
mutations,
|
||||||
defaultState,
|
defaultState,
|
||||||
@ -28,6 +29,7 @@ describe('The serverSideStorage module', () => {
|
|||||||
expect(state.cache._version).to.eql(VERSION)
|
expect(state.cache._version).to.eql(VERSION)
|
||||||
expect(state.cache._timestamp).to.be.a('number')
|
expect(state.cache._timestamp).to.be.a('number')
|
||||||
expect(state.cache.flagStorage).to.eql(defaultState.flagStorage)
|
expect(state.cache.flagStorage).to.eql(defaultState.flagStorage)
|
||||||
|
expect(state.cache.prefsStorage).to.eql(defaultState.prefsStorage)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should initialize storage with proper flags for new users if none present', () => {
|
it('should initialize storage with proper flags for new users if none present', () => {
|
||||||
@ -36,6 +38,7 @@ describe('The serverSideStorage module', () => {
|
|||||||
expect(state.cache._version).to.eql(VERSION)
|
expect(state.cache._version).to.eql(VERSION)
|
||||||
expect(state.cache._timestamp).to.be.a('number')
|
expect(state.cache._timestamp).to.be.a('number')
|
||||||
expect(state.cache.flagStorage).to.eql(newUserFlags)
|
expect(state.cache.flagStorage).to.eql(newUserFlags)
|
||||||
|
expect(state.cache.prefsStorage).to.eql(defaultState.prefsStorage)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should merge flags even if remote timestamp is older', () => {
|
it('should merge flags even if remote timestamp is older', () => {
|
||||||
@ -57,6 +60,9 @@ describe('The serverSideStorage module', () => {
|
|||||||
flagStorage: {
|
flagStorage: {
|
||||||
...defaultState.flagStorage,
|
...defaultState.flagStorage,
|
||||||
updateCounter: 1
|
updateCounter: 1
|
||||||
|
},
|
||||||
|
prefsStorage: {
|
||||||
|
...defaultState.flagStorage,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,6 +163,94 @@ describe('The serverSideStorage module', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('_mergePrefs', () => {
|
||||||
|
it('should prefer recent and apply journal to it', () => {
|
||||||
|
expect(
|
||||||
|
_mergePrefs(
|
||||||
|
// RECENT
|
||||||
|
{
|
||||||
|
simple: { a: 1, b: 0, c: true },
|
||||||
|
_journal: [
|
||||||
|
{ path: 'simple.b', operation: 'set', args: [0], timestamp: 2 },
|
||||||
|
{ path: 'simple.c', operation: 'set', args: [true], timestamp: 4 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// STALE
|
||||||
|
{
|
||||||
|
simple: { a: 1, b: 1, c: false },
|
||||||
|
_journal: [
|
||||||
|
{ path: 'simple.a', operation: 'set', args: [1], timestamp: 1 },
|
||||||
|
{ path: 'simple.b', operation: 'set', args: [1], timestamp: 3 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).to.eql({
|
||||||
|
simple: { a: 1, b: 1, c: true },
|
||||||
|
_journal: [
|
||||||
|
{ path: 'simple.a', operation: 'set', args: [1], timestamp: 1 },
|
||||||
|
{ path: 'simple.b', operation: 'set', args: [1], timestamp: 3 },
|
||||||
|
{ path: 'simple.c', operation: 'set', args: [true], timestamp: 4 }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should allow setting falsy values', () => {
|
||||||
|
expect(
|
||||||
|
_mergePrefs(
|
||||||
|
// RECENT
|
||||||
|
{
|
||||||
|
simple: { a: 1, b: 0, c: false },
|
||||||
|
_journal: [
|
||||||
|
{ path: 'simple.b', operation: 'set', args: [0], timestamp: 2 },
|
||||||
|
{ path: 'simple.c', operation: 'set', args: [false], timestamp: 4 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// STALE
|
||||||
|
{
|
||||||
|
simple: { a: 0, b: 0, c: true },
|
||||||
|
_journal: [
|
||||||
|
{ path: 'simple.a', operation: 'set', args: [0], timestamp: 1 },
|
||||||
|
{ path: 'simple.b', operation: 'set', args: [0], timestamp: 3 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).to.eql({
|
||||||
|
simple: { a: 0, b: 0, c: false },
|
||||||
|
_journal: [
|
||||||
|
{ path: 'simple.a', operation: 'set', args: [0], timestamp: 1 },
|
||||||
|
{ path: 'simple.b', operation: 'set', args: [0], timestamp: 3 },
|
||||||
|
{ path: 'simple.c', operation: 'set', args: [false], timestamp: 4 }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should work with strings', () => {
|
||||||
|
expect(
|
||||||
|
_mergePrefs(
|
||||||
|
// RECENT
|
||||||
|
{
|
||||||
|
simple: { a: 'foo' },
|
||||||
|
_journal: [
|
||||||
|
{ path: 'simple.a', operation: 'set', args: ['foo'], timestamp: 2 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// STALE
|
||||||
|
{
|
||||||
|
simple: { a: 'bar' },
|
||||||
|
_journal: [
|
||||||
|
{ path: 'simple.a', operation: 'set', args: ['bar'], timestamp: 4 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).to.eql({
|
||||||
|
simple: { a: 'bar' },
|
||||||
|
_journal: [
|
||||||
|
{ path: 'simple.a', operation: 'set', args: ['bar'], timestamp: 4 }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('_resetFlags', () => {
|
describe('_resetFlags', () => {
|
||||||
it('should reset all known flags to 0 when reset flag is set to > 0 and < 9000', () => {
|
it('should reset all known flags to 0 when reset flag is set to > 0 and < 9000', () => {
|
||||||
const totalFlags = { a: 0, b: 3, reset: 1 }
|
const totalFlags = { a: 0, b: 3, reset: 1 }
|
||||||
|
Loading…
Reference in New Issue
Block a user