added ability to granularly modify objects in simple storage

This commit is contained in:
Henry Jameson 2025-03-20 19:32:52 +02:00
parent 76c1ab13ec
commit 5e880ed54f
2 changed files with 143 additions and 6 deletions

View file

@ -3,6 +3,7 @@ import {
isEqual,
cloneDeep,
set,
unset,
get,
clamp,
flatten,
@ -35,7 +36,8 @@ export const defaultState = {
_journal: [],
simple: {
dontShowUpdateNotifs: false,
collapseNav: false
collapseNav: false,
filters: {}
},
collections: {
pinnedStatusActions: ['reply', 'retweet', 'favorite', 'emoji'],
@ -78,11 +80,16 @@ const _verifyPrefs = (state) => {
simple: {},
collections: {}
}
// Simple
Object.entries(defaultState.prefsStorage.simple).forEach(([k, v]) => {
if (typeof v === 'number' || typeof v === 'boolean') return
if (typeof v === 'object' && v != null) return
console.warn(`Preference simple.${k} as invalid type, reinitializing`)
set(state.prefsStorage.simple, k, defaultState.prefsStorage.simple[k])
})
// Collections
Object.entries(defaultState.prefsStorage.collections).forEach(([k, v]) => {
if (Array.isArray(v)) return
console.warn(`Preference collections.${k} as invalid type, reinitializing`)
@ -224,8 +231,27 @@ export const _mergePrefs = (recent, stale) => {
}
switch (operation) {
case 'set':
if (path.startsWith('collections') || path.startsWith('objectCollections')) {
console.error('Illegal operation "set" on a collection')
return
}
if (path.split(/\./g).length <= 1) {
console.error(`Calling set on depth <= 1 (path: ${path}) is not allowed`)
return
}
set(resultOutput, path, args[0])
break
case 'unset':
if (path.startsWith('collections') || path.startsWith('objectCollections')) {
console.error('Illegal operation "unset" on a collection')
return
}
if (path.split(/\./g).length <= 2) {
console.error(`Calling unset on depth <= 2 (path: ${path}) is not allowed`)
return
}
unset(resultOutput, path)
break
case 'addToCollection':
set(resultOutput, path, Array.from(new Set(get(resultOutput, path)).add(args[0])))
break
@ -380,7 +406,15 @@ export const mutations = {
},
setPreference (state, { path, value }) {
if (path.startsWith('_')) {
console.error(`tried to edit internal (starts with _) field '${path}', ignoring.`)
console.error(`Tried to edit internal (starts with _) field '${path}', ignoring.`)
return
}
if (path.startsWith('collections') || path.startsWith('objectCollections')) {
console.error(`Invalid operation 'set' for collection field '${path}', ignoring.`)
return
}
if (path.split(/\./g).length <= 1) {
console.error(`Calling set on depth <= 1 (path: ${path}) is not allowed`)
return
}
set(state.prefsStorage, path, value)
@ -390,14 +424,46 @@ export const mutations = {
]
state.dirty = true
},
unsetPreference (state, { path, value }) {
if (path.startsWith('_')) {
console.error(`Tried to edit internal (starts with _) field '${path}', ignoring.`)
return
}
if (path.startsWith('collections') || path.startsWith('objectCollections')) {
console.error(`Invalid operation 'unset' for collection field '${path}', ignoring.`)
return
}
if (path.split(/\./g).length <= 2) {
console.error(`Calling unset on depth <= 2 (path: ${path}) is not allowed`)
return
}
unset(state.prefsStorage, path, value)
state.prefsStorage._journal = [
...state.prefsStorage._journal,
{ operation: 'unset', path, args: [], timestamp: Date.now() }
]
state.dirty = true
},
addCollectionPreference (state, { path, value }) {
if (path.startsWith('_')) {
console.error(`tried to edit internal (starts with _) field '${path}', ignoring.`)
return
}
const collection = new Set(get(state.prefsStorage, path))
collection.add(value)
set(state.prefsStorage, path, [...collection])
if (path.startsWith('collections')) {
const collection = new Set(get(state.prefsStorage, path))
collection.add(value)
set(state.prefsStorage, path, [...collection])
} else if (path.startsWith('objectCollections')) {
const { _key } = value
if (!_key && typeof _key !== 'string') {
console.error('Object for storage is missing _key field! ignoring')
return
}
const collection = new Set(get(state.prefsStorage, path + '.index'))
collection.add(_key)
set(state.prefsStorage, path + '.index', [...collection])
set(state.prefsStorage, path + '.data.' + _key, value)
}
state.prefsStorage._journal = [
...state.prefsStorage._journal,
{ operation: 'addToCollection', path, args: [value], timestamp: Date.now() }

View file

@ -107,7 +107,7 @@ describe('The serverSideStorage module', () => {
})
})
describe('setPreference', () => {
const { setPreference, updateCache, addCollectionPreference, removeCollectionPreference } = mutations
const { setPreference, unsetPreference, updateCache, addCollectionPreference, removeCollectionPreference } = mutations
it('should set preference and update journal log accordingly', () => {
const state = cloneDeep(defaultState)
@ -160,6 +160,25 @@ describe('The serverSideStorage module', () => {
expect(state.prefsStorage.collections.testing).to.eql([2])
expect(state.prefsStorage._journal.length).to.eql(2)
})
it('should remove depth = 3 set/unset entries from journal', () => {
const state = cloneDeep(defaultState)
setPreference(state, { path: 'simple.object.foo', value: 1 })
unsetPreference(state, { path: 'simple.object.foo' })
updateCache(state, { username: 'test' })
expect(state.prefsStorage.simple.object).to.not.have.property('foo')
expect(state.prefsStorage._journal.length).to.eql(1)
})
it('should not allow unsetting depth <= 2', () => {
const state = cloneDeep(defaultState)
setPreference(state, { path: 'simple.object.foo', value: 1 })
unsetPreference(state, { path: 'simple.object' })
unsetPreference(state, { path: 'simple' })
updateCache(state, { username: 'test' })
expect(state.prefsStorage.simple.object).to.have.property('foo')
expect(state.prefsStorage._journal.length).to.eql(1)
})
})
})
@ -315,6 +334,58 @@ describe('The serverSideStorage module', () => {
]
})
})
it('should work with objects', () => {
expect(
_mergePrefs(
// RECENT
{
simple: { lv2: { lv3: 'foo' } },
_journal: [
{ path: 'simple.lv2.lv3', operation: 'set', args: ['foo'], timestamp: 2 }
]
},
// STALE
{
simple: { lv2: { lv3: 'bar' } },
_journal: [
{ path: 'simple.lv2.lv3', operation: 'set', args: ['bar'], timestamp: 4 }
]
}
)
).to.eql({
simple: { lv2: { lv3: 'bar' } },
_journal: [
{ path: 'simple.lv2.lv3', operation: 'set', args: ['bar'], timestamp: 4 }
]
})
})
it('should work with unset', () => {
expect(
_mergePrefs(
// RECENT
{
simple: { lv2: { lv3: 'foo' } },
_journal: [
{ path: 'simple.lv2.lv3', operation: 'set', args: ['foo'], timestamp: 2 }
]
},
// STALE
{
simple: { lv2: {} },
_journal: [
{ path: 'simple.lv2.lv3', operation: 'unset', args: [], timestamp: 4 }
]
}
)
).to.eql({
simple: { lv2: {} },
_journal: [
{ path: 'simple.lv2.lv3', operation: 'unset', args: [], timestamp: 4 }
]
})
})
})
describe('_resetFlags', () => {