Merge branch 'setttingssync' into shigusegubu-themes3
This commit is contained in:
commit
a2eb4fd202
5 changed files with 124 additions and 32 deletions
|
|
@ -3,8 +3,8 @@ import { mapState } from 'pinia'
|
||||||
import Popover from '../popover/popover.vue'
|
import Popover from '../popover/popover.vue'
|
||||||
|
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
|
||||||
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
||||||
|
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons'
|
import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,8 @@ const FilteringTab = {
|
||||||
computed: {
|
computed: {
|
||||||
...SharedComputedObject(),
|
...SharedComputedObject(),
|
||||||
...mapState(useSyncConfigStore, {
|
...mapState(useSyncConfigStore, {
|
||||||
muteFilters: (store) => Object.entries(store.prefsStorage.simple.muteFilters),
|
muteFilters: (store) =>
|
||||||
|
Object.entries(store.prefsStorage.simple.muteFilters),
|
||||||
muteFiltersObject: (store) => store.prefsStorage.simple.muteFilters,
|
muteFiltersObject: (store) => store.prefsStorage.simple.muteFilters,
|
||||||
}),
|
}),
|
||||||
...mapState(useInstanceCapabilitiesStore, ['blockExpiration']),
|
...mapState(useInstanceCapabilitiesStore, ['blockExpiration']),
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import {
|
||||||
groupBy,
|
groupBy,
|
||||||
isEqual,
|
isEqual,
|
||||||
set,
|
set,
|
||||||
|
take,
|
||||||
takeRight,
|
takeRight,
|
||||||
uniqWith,
|
uniqWith,
|
||||||
unset,
|
unset,
|
||||||
|
|
@ -219,7 +220,7 @@ export const _mergeFlags = (recent, stale, allFlagKeys) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const _mergeJournal = (...journals) => {
|
export const _mergeJournal = (...journals) => {
|
||||||
// Ignore invalid journal entries
|
// Ignore invalid journal entries
|
||||||
const allJournals = flatten(
|
const allJournals = flatten(
|
||||||
journals.map((j) => (Array.isArray(j) ? j : [])),
|
journals.map((j) => (Array.isArray(j) ? j : [])),
|
||||||
|
|
@ -267,9 +268,11 @@ const _mergeJournal = (...journals) => {
|
||||||
return journal
|
return journal
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return flatten(trimmedGrouped).sort((a, b) =>
|
|
||||||
|
const flat = flatten(trimmedGrouped).sort((a, b) =>
|
||||||
a.timestamp > b.timestamp ? 1 : -1,
|
a.timestamp > b.timestamp ? 1 : -1,
|
||||||
)
|
)
|
||||||
|
return take(flat, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const _mergePrefs = (recent, stale) => {
|
export const _mergePrefs = (recent, stale) => {
|
||||||
|
|
@ -621,12 +624,26 @@ export const useSyncConfigStore = defineStore('sync_config', {
|
||||||
const flagsTemplate = userNew ? newUserFlags : defaultState.flagStorage
|
const flagsTemplate = userNew ? newUserFlags : defaultState.flagStorage
|
||||||
let dirty = false
|
let dirty = false
|
||||||
|
|
||||||
|
if (recent === null) {
|
||||||
|
console.debug(
|
||||||
|
`Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`,
|
||||||
|
)
|
||||||
|
recent = _wrapData({
|
||||||
|
flagStorage: { ...flagsTemplate },
|
||||||
|
prefsStorage: { ...defaultState.prefsStorage },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
recent = recent && (await _doMigrations(recent, this.setPreference))
|
||||||
|
stale = stale && (await _doMigrations(stale, this.setPreference))
|
||||||
|
|
||||||
|
// Various migrations
|
||||||
console.debug('Migrating from old config')
|
console.debug('Migrating from old config')
|
||||||
const vuexState = (await storage.getItem('vuex-lz')) ?? {}
|
const vuexState = (await storage.getItem('vuex-lz')) ?? {}
|
||||||
vuexState.config = vuexState.config ?? {}
|
vuexState.config = vuexState.config ?? {}
|
||||||
|
|
||||||
const migratedEntries = new Set(vuexState.config._syncMigration ?? [])
|
const migratedEntries = new Set(vuexState.config._syncMigration ?? [])
|
||||||
console.debug(`Already migrated Values: ${[...migratedEntries].join()}`)
|
console.debug(`Already migrated Values: ${[...migratedEntries].join() || '[none]'}`)
|
||||||
|
|
||||||
Object.entries(oldDefaultConfigSync).forEach(([key, value]) => {
|
Object.entries(oldDefaultConfigSync).forEach(([key, value]) => {
|
||||||
const oldValue = vuexState.config[key]
|
const oldValue = vuexState.config[key]
|
||||||
|
|
@ -670,19 +687,6 @@ export const useSyncConfigStore = defineStore('sync_config', {
|
||||||
vuexState.config._syncMigration = [...migratedEntries]
|
vuexState.config._syncMigration = [...migratedEntries]
|
||||||
storage.setItem('vuex-lz', vuexState)
|
storage.setItem('vuex-lz', vuexState)
|
||||||
|
|
||||||
if (recent === null) {
|
|
||||||
console.debug(
|
|
||||||
`Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`,
|
|
||||||
)
|
|
||||||
recent = _wrapData({
|
|
||||||
flagStorage: { ...flagsTemplate },
|
|
||||||
prefsStorage: { ...defaultState.prefsStorage },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
recent = recent && (await _doMigrations(recent, this.setPreference))
|
|
||||||
stale = stale && (await _doMigrations(stale, this.setPreference))
|
|
||||||
|
|
||||||
if (!needUpload && recent && stale) {
|
if (!needUpload && recent && stale) {
|
||||||
console.debug('Checking if data needs merging...')
|
console.debug('Checking if data needs merging...')
|
||||||
// discarding timestamps and versions
|
// discarding timestamps and versions
|
||||||
|
|
@ -722,6 +726,7 @@ export const useSyncConfigStore = defineStore('sync_config', {
|
||||||
this.flagStorage = this.cache.flagStorage
|
this.flagStorage = this.cache.flagStorage
|
||||||
this.prefsStorage = this.cache.prefsStorage
|
this.prefsStorage = this.cache.prefsStorage
|
||||||
this.pushSyncConfig()
|
this.pushSyncConfig()
|
||||||
|
console.log('C', this.cache)
|
||||||
},
|
},
|
||||||
pushSyncConfig({ force = false } = {}) {
|
pushSyncConfig({ force = false } = {}) {
|
||||||
const needPush = this.dirty || force
|
const needPush = this.dirty || force
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ import 'virtual:pleroma-fe/service_worker_env'
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import { storage } from 'src/lib/storage.js'
|
import { storage } from 'src/lib/storage.js'
|
||||||
|
import { instanceDefaultConfig } from 'src/modules/default_config_state.js'
|
||||||
import { parseNotification } from 'src/services/entity_normalizer/entity_normalizer.service.js'
|
import { parseNotification } from 'src/services/entity_normalizer/entity_normalizer.service.js'
|
||||||
import { prepareNotificationObject } from 'src/services/notification_utils/notification_utils.js'
|
import { prepareNotificationObject } from 'src/services/notification_utils/notification_utils.js'
|
||||||
import { cacheKey, emojiCacheKey, shouldCache } from 'src/services/sw/sw.js'
|
import { cacheKey, emojiCacheKey, shouldCache } from 'src/services/sw/sw.js'
|
||||||
import { instanceDefaultConfig } from 'src/modules/default_config_state.js'
|
|
||||||
|
|
||||||
// Collects all messages for service workers
|
// Collects all messages for service workers
|
||||||
// Needed because service workers cannot use dynamic imports
|
// Needed because service workers cannot use dynamic imports
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import {
|
||||||
useSyncConfigStore,
|
useSyncConfigStore,
|
||||||
VERSION,
|
VERSION,
|
||||||
} from 'src/stores/sync_config.js'
|
} from 'src/stores/sync_config.js'
|
||||||
|
import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js'
|
||||||
|
|
||||||
describe('The SyncConfig store', () => {
|
describe('The SyncConfig store', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
@ -28,40 +29,52 @@ describe('The SyncConfig store', () => {
|
||||||
storage: {},
|
storage: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should initialize storage if none present', () => {
|
it('should initialize storage if none present', async () => {
|
||||||
const store = useSyncConfigStore()
|
const store = useSyncConfigStore()
|
||||||
store.initSyncConfig({ ...user })
|
// PushSyncConfig is very simple but uses vuex to push data
|
||||||
|
store.pushSyncConfig = () => {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
await store.initSyncConfig({ ...user })
|
||||||
expect(store.cache._version).to.eql(VERSION)
|
expect(store.cache._version).to.eql(VERSION)
|
||||||
expect(store.cache._timestamp).to.be.a('number')
|
expect(store.cache._timestamp).to.be.a('number')
|
||||||
expect(store.cache.flagStorage).to.eql(defaultState.flagStorage)
|
expect(store.cache.flagStorage).to.eql(defaultState.flagStorage)
|
||||||
expect(store.cache.prefsStorage).to.eql(defaultState.prefsStorage)
|
expect(store.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', async () => {
|
||||||
const store = useSyncConfigStore()
|
const store = useSyncConfigStore()
|
||||||
store.initSyncConfig({ ...user, created_at: new Date() })
|
// PushSyncConfig is very simple but uses vuex to push data
|
||||||
|
store.pushSyncConfig = () => {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
await store.initSyncConfig({ ...user, created_at: new Date() })
|
||||||
expect(store.cache._version).to.eql(VERSION)
|
expect(store.cache._version).to.eql(VERSION)
|
||||||
expect(store.cache._timestamp).to.be.a('number')
|
expect(store.cache._timestamp).to.be.a('number')
|
||||||
expect(store.cache.flagStorage).to.eql(newUserFlags)
|
expect(store.cache.flagStorage).to.eql(newUserFlags)
|
||||||
expect(store.cache.prefsStorage).to.eql(defaultState.prefsStorage)
|
expect(store.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', async () => {
|
||||||
const store = useSyncConfigStore()
|
const store = useSyncConfigStore()
|
||||||
|
// PushSyncConfig is very simple but uses vuex to push data
|
||||||
|
store.pushSyncConfig = () => {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
store.cache = {
|
store.cache = {
|
||||||
_timestamp: Date.now(),
|
_timestamp: Date.now(),
|
||||||
_version: VERSION,
|
_version: VERSION,
|
||||||
...cloneDeep(defaultState),
|
...cloneDeep(defaultState),
|
||||||
}
|
}
|
||||||
|
|
||||||
store.initSyncConfig({
|
await store.initSyncConfig({
|
||||||
...user,
|
...user,
|
||||||
storage: {
|
storage: {
|
||||||
_timestamp: 123,
|
_timestamp: 123,
|
||||||
_version: VERSION,
|
_version: VERSION,
|
||||||
flagStorage: {
|
flagStorage: {
|
||||||
...defaultState.flagStorage,
|
...defaultState.flagStorage,
|
||||||
updateCounter: 1,
|
updateCounter: CURRENT_UPDATE_COUNTER,
|
||||||
},
|
},
|
||||||
prefsStorage: {
|
prefsStorage: {
|
||||||
...defaultState.prefsStorage,
|
...defaultState.prefsStorage,
|
||||||
|
|
@ -69,17 +82,62 @@ describe('The SyncConfig store', () => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(store.cache.flagStorage).to.eql({
|
expect(store.flagStorage).to.eql({
|
||||||
...defaultState.flagStorage,
|
...defaultState.flagStorage,
|
||||||
updateCounter: 1,
|
updateCounter: CURRENT_UPDATE_COUNTER,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should reset local timestamp to remote if contents are the same', () => {
|
it('should trim journal to 500 entries', async () => {
|
||||||
|
const store = useSyncConfigStore()
|
||||||
|
// PushSyncConfig is very simple but uses vuex to push data
|
||||||
|
store.pushSyncConfig = () => {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
store.cache = {
|
||||||
|
_timestamp: Date.now(),
|
||||||
|
_version: VERSION,
|
||||||
|
...cloneDeep(defaultState),
|
||||||
|
}
|
||||||
|
const largeJournal = []
|
||||||
|
for (let value = 0; value < 1000; value++) {
|
||||||
|
largeJournal.push({
|
||||||
|
path: 'simple.testing' + value,
|
||||||
|
operation: 'set',
|
||||||
|
args: [value],
|
||||||
|
// should have A timestamp, we don't really care what it is
|
||||||
|
timestamp: 123456,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await store.initSyncConfig({
|
||||||
|
...user,
|
||||||
|
storage: {
|
||||||
|
_timestamp: 123,
|
||||||
|
_version: VERSION,
|
||||||
|
flagStorage: {
|
||||||
|
...defaultState.flagStorage,
|
||||||
|
updateCounter: CURRENT_UPDATE_COUNTER,
|
||||||
|
},
|
||||||
|
prefsStorage: {
|
||||||
|
...defaultState.prefsStorage,
|
||||||
|
_journal: largeJournal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(store.prefsStorage._journal.length).to.eql(500)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reset local timestamp to remote if contents are the same', async () => {
|
||||||
const store = useSyncConfigStore()
|
const store = useSyncConfigStore()
|
||||||
store.cache = null
|
store.cache = null
|
||||||
|
// PushSyncConfig is very simple but uses vuex to push data
|
||||||
|
store.pushSyncConfig = () => {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
|
||||||
store.initSyncConfig({
|
await store.initSyncConfig({
|
||||||
...user,
|
...user,
|
||||||
storage: {
|
storage: {
|
||||||
_timestamp: 123,
|
_timestamp: 123,
|
||||||
|
|
@ -95,9 +153,13 @@ describe('The SyncConfig store', () => {
|
||||||
expect(store.cache.flagStorage.updateCounter).to.eql(999)
|
expect(store.cache.flagStorage.updateCounter).to.eql(999)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should use remote version if local missing', () => {
|
it('should use remote version if local missing', async () => {
|
||||||
const store = useSyncConfigStore()
|
const store = useSyncConfigStore()
|
||||||
store.initSyncConfig(store, user)
|
// PushSyncConfig is very simple but uses vuex to push data
|
||||||
|
store.pushSyncConfig = () => {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
await store.initSyncConfig(store, user)
|
||||||
expect(store.cache._version).to.eql(VERSION)
|
expect(store.cache._version).to.eql(VERSION)
|
||||||
expect(store.cache._timestamp).to.be.a('number')
|
expect(store.cache._timestamp).to.be.a('number')
|
||||||
expect(store.cache.flagStorage).to.eql(defaultState.flagStorage)
|
expect(store.cache.flagStorage).to.eql(defaultState.flagStorage)
|
||||||
|
|
@ -106,6 +168,10 @@ describe('The SyncConfig store', () => {
|
||||||
describe('setPreference', () => {
|
describe('setPreference', () => {
|
||||||
it('should set preference and update journal log accordingly', () => {
|
it('should set preference and update journal log accordingly', () => {
|
||||||
const store = useSyncConfigStore()
|
const store = useSyncConfigStore()
|
||||||
|
// PushSyncConfig is very simple but uses vuex to push data
|
||||||
|
store.pushSyncConfig = () => {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
store.setPreference({ path: 'simple.testing', value: 1 })
|
store.setPreference({ path: 'simple.testing', value: 1 })
|
||||||
expect(store.prefsStorage.simple.testing).to.eql(1)
|
expect(store.prefsStorage.simple.testing).to.eql(1)
|
||||||
expect(store.prefsStorage._journal.length).to.eql(1)
|
expect(store.prefsStorage._journal.length).to.eql(1)
|
||||||
|
|
@ -120,6 +186,10 @@ describe('The SyncConfig store', () => {
|
||||||
|
|
||||||
it('should keep journal to a minimum', () => {
|
it('should keep journal to a minimum', () => {
|
||||||
const store = useSyncConfigStore()
|
const store = useSyncConfigStore()
|
||||||
|
// PushSyncConfig is very simple but uses vuex to push data
|
||||||
|
store.pushSyncConfig = () => {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
store.setPreference({ path: 'simple.testing', value: 1 })
|
store.setPreference({ path: 'simple.testing', value: 1 })
|
||||||
store.setPreference({ path: 'simple.testing', value: 2 })
|
store.setPreference({ path: 'simple.testing', value: 2 })
|
||||||
store.addCollectionPreference({ path: 'collections.testing', value: 2 })
|
store.addCollectionPreference({ path: 'collections.testing', value: 2 })
|
||||||
|
|
@ -149,6 +219,10 @@ describe('The SyncConfig store', () => {
|
||||||
|
|
||||||
it('should remove duplicate entries from journal', () => {
|
it('should remove duplicate entries from journal', () => {
|
||||||
const store = useSyncConfigStore()
|
const store = useSyncConfigStore()
|
||||||
|
// PushSyncConfig is very simple but uses vuex to push data
|
||||||
|
store.pushSyncConfig = () => {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
store.setPreference({ path: 'simple.testing', value: 1 })
|
store.setPreference({ path: 'simple.testing', value: 1 })
|
||||||
store.setPreference({ path: 'simple.testing', value: 1 })
|
store.setPreference({ path: 'simple.testing', value: 1 })
|
||||||
store.addCollectionPreference({ path: 'collections.testing', value: 2 })
|
store.addCollectionPreference({ path: 'collections.testing', value: 2 })
|
||||||
|
|
@ -161,6 +235,10 @@ describe('The SyncConfig store', () => {
|
||||||
|
|
||||||
it('should remove depth = 3 set/unset entries from journal', () => {
|
it('should remove depth = 3 set/unset entries from journal', () => {
|
||||||
const store = useSyncConfigStore()
|
const store = useSyncConfigStore()
|
||||||
|
// PushSyncConfig is very simple but uses vuex to push data
|
||||||
|
store.pushSyncConfig = () => {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
store.setPreference({ path: 'simple.object.foo', value: 1 })
|
store.setPreference({ path: 'simple.object.foo', value: 1 })
|
||||||
store.unsetPreference({ path: 'simple.object.foo' })
|
store.unsetPreference({ path: 'simple.object.foo' })
|
||||||
store.updateCache(store, { username: 'test' })
|
store.updateCache(store, { username: 'test' })
|
||||||
|
|
@ -170,6 +248,10 @@ describe('The SyncConfig store', () => {
|
||||||
|
|
||||||
it('should not allow unsetting depth <= 2', () => {
|
it('should not allow unsetting depth <= 2', () => {
|
||||||
const store = useSyncConfigStore()
|
const store = useSyncConfigStore()
|
||||||
|
// PushSyncConfig is very simple but uses vuex to push data
|
||||||
|
store.pushSyncConfig = () => {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
store.setPreference({ path: 'simple.object.foo', value: 1 })
|
store.setPreference({ path: 'simple.object.foo', value: 1 })
|
||||||
expect(() => store.unsetPreference({ path: 'simple' })).to.throw()
|
expect(() => store.unsetPreference({ path: 'simple' })).to.throw()
|
||||||
expect(() =>
|
expect(() =>
|
||||||
|
|
@ -179,6 +261,10 @@ describe('The SyncConfig store', () => {
|
||||||
|
|
||||||
it('should not allow (un)setting depth > 3', () => {
|
it('should not allow (un)setting depth > 3', () => {
|
||||||
const store = useSyncConfigStore()
|
const store = useSyncConfigStore()
|
||||||
|
// PushSyncConfig is very simple but uses vuex to push data
|
||||||
|
store.pushSyncConfig = () => {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
store.setPreference({ path: 'simple.object', value: {} })
|
store.setPreference({ path: 'simple.object', value: {} })
|
||||||
expect(() =>
|
expect(() =>
|
||||||
store.setPreference({ path: 'simple.object.lv3', value: 1 }),
|
store.setPreference({ path: 'simple.object.lv3', value: 1 }),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue