migrate to pinia
This commit is contained in:
parent
eaf7efdcff
commit
f347897b29
10 changed files with 330 additions and 317 deletions
|
@ -1,14 +1,19 @@
|
|||
import SideDrawer from '../side_drawer/side_drawer.vue'
|
||||
import Notifications from '../notifications/notifications.vue'
|
||||
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
|
||||
import GestureService from '../../services/gesture_service/gesture_service'
|
||||
import NavigationPins from 'src/components/navigation/navigation_pins.vue'
|
||||
|
||||
import {
|
||||
unseenNotificationsFromStore,
|
||||
countExtraNotifications
|
||||
} from '../../services/notification_utils/notification_utils'
|
||||
import GestureService from '../../services/gesture_service/gesture_service'
|
||||
import NavigationPins from 'src/components/navigation/navigation_pins.vue'
|
||||
|
||||
import { mapGetters } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faTimes,
|
||||
|
@ -18,7 +23,6 @@ import {
|
|||
faMinus,
|
||||
faCheckDouble
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements'
|
||||
|
||||
library.add(
|
||||
faTimes,
|
||||
|
@ -71,10 +75,9 @@ const MobileNav = {
|
|||
return this.$route.name === 'chat'
|
||||
},
|
||||
...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']),
|
||||
...mapGetters(['unreadChatCount']),
|
||||
chatsPinned () {
|
||||
return new Set(this.$store.state.serverSideStorage.prefsStorage.collections.pinnedNavItems).has('chats')
|
||||
},
|
||||
...mapState(useServerSideStorageStore, {
|
||||
pinnedItems: store => new Set(store.prefsStorage.collections.pinnedNavItems).has('chats')
|
||||
}),
|
||||
shouldConfirmLogout () {
|
||||
return this.$store.getters.mergedConfig.modalOnLogout
|
||||
},
|
||||
|
|
|
@ -7,7 +7,9 @@ import { filterNavigation } from 'src/components/navigation/filter.js'
|
|||
import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
|
||||
import NavigationPins from 'src/components/navigation/navigation_pins.vue'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
@ -76,19 +78,19 @@ const NavPanel = {
|
|||
this.editMode = !this.editMode
|
||||
},
|
||||
toggleCollapse () {
|
||||
this.$store.commit('setPreference', { path: 'simple.collapseNav', value: !this.collapsed })
|
||||
this.$store.dispatch('pushServerSideStorage')
|
||||
useServerSideStorageStore().setPreference({ path: 'simple.collapseNav', value: !this.collapsed })
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
},
|
||||
isPinned (item) {
|
||||
return this.pinnedItems.has(item)
|
||||
},
|
||||
togglePin (item) {
|
||||
if (this.isPinned(item)) {
|
||||
this.$store.commit('removeCollectionPreference', { path: 'collections.pinnedNavItems', value: item })
|
||||
useServerSideStorageStore().removeCollectionPreference({ path: 'collections.pinnedNavItems', value: item })
|
||||
} else {
|
||||
this.$store.commit('addCollectionPreference', { path: 'collections.pinnedNavItems', value: item })
|
||||
useServerSideStorageStore().addCollectionPreference({ path: 'collections.pinnedNavItems', value: item })
|
||||
}
|
||||
this.$store.dispatch('pushServerSideStorage')
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -96,14 +98,16 @@ const NavPanel = {
|
|||
unreadAnnouncementCount: 'unreadAnnouncementCount',
|
||||
supportsAnnouncements: store => store.supportsAnnouncements
|
||||
}),
|
||||
...mapPiniaState(useServerSideStorageStore, {
|
||||
collapsed: store => store.prefsStorage.simple.collapseNav,
|
||||
pinnedItems: store => new Set(store.prefsStorage.collections.pinnedNavItems)
|
||||
}),
|
||||
...mapState({
|
||||
currentUser: state => state.users.currentUser,
|
||||
followRequestCount: state => state.api.followRequests.length,
|
||||
privateMode: state => state.instance.private,
|
||||
federating: state => state.instance.federating,
|
||||
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
|
||||
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems),
|
||||
collapsed: state => state.serverSideStorage.prefsStorage.simple.collapseNav,
|
||||
bookmarkFolders: state => state.instance.pleromaBookmarkFoldersAvailable
|
||||
}),
|
||||
timelinesItems () {
|
||||
|
|
|
@ -3,8 +3,10 @@ import { routeTo } from 'src/components/navigation/navigation.js'
|
|||
import OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faThumbtack } from '@fortawesome/free-solid-svg-icons'
|
||||
import { mapStores } from 'pinia'
|
||||
import { mapStores, mapState as mapPiniaState } from 'pinia'
|
||||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
|
||||
library.add(faThumbtack)
|
||||
|
||||
|
@ -19,11 +21,11 @@ const NavigationEntry = {
|
|||
},
|
||||
togglePin (value) {
|
||||
if (this.isPinned(value)) {
|
||||
this.$store.commit('removeCollectionPreference', { path: 'collections.pinnedNavItems', value })
|
||||
useServerSideStorageStore().removeCollectionPreference({ path: 'collections.pinnedNavItems', value })
|
||||
} else {
|
||||
this.$store.commit('addCollectionPreference', { path: 'collections.pinnedNavItems', value })
|
||||
useServerSideStorageStore().addCollectionPreference({ path: 'collections.pinnedNavItems', value })
|
||||
}
|
||||
this.$store.dispatch('pushServerSideStorage')
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -35,9 +37,11 @@ const NavigationEntry = {
|
|||
},
|
||||
...mapStores(useAnnouncementsStore),
|
||||
...mapState({
|
||||
currentUser: state => state.users.currentUser,
|
||||
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems)
|
||||
})
|
||||
currentUser: state => state.users.currentUser
|
||||
}),
|
||||
...mapPiniaState(useServerSideStorageStore, {
|
||||
pinnedItems: store => new Set(store.prefsStorage.collections.pinnedNavItems)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
import { useListsStore } from 'src/stores/lists'
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements'
|
||||
import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
|
||||
library.add(
|
||||
faUsers,
|
||||
|
@ -54,15 +55,17 @@ const NavPanel = {
|
|||
supportsAnnouncements: store => store.supportsAnnouncements
|
||||
}),
|
||||
...mapPiniaState(useBookmarkFoldersStore, {
|
||||
bookmarks: getBookmarkFolderEntries
|
||||
bookmarks: getBookmarkFolderEntries,
|
||||
}),
|
||||
...mapPiniaState(useServerSideStorageStore, {
|
||||
pinnedItems: store => new Set(store.prefsStorage.collections.pinnedNavItems)
|
||||
}),
|
||||
...mapState({
|
||||
currentUser: state => state.users.currentUser,
|
||||
followRequestCount: state => state.api.followRequests.length,
|
||||
privateMode: state => state.instance.private,
|
||||
federating: state => state.instance.federating,
|
||||
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
|
||||
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems)
|
||||
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable
|
||||
}),
|
||||
pinnedList () {
|
||||
if (!this.currentUser) {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { mapState } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
|
||||
import ActionButtonContainer from './action_button_container.vue'
|
||||
import Popover from 'src/components/popover/popover.vue'
|
||||
import genRandomSeed from 'src/services/random_seed/random_seed.service.js'
|
||||
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
|
||||
import { BUTTONS } from './buttons_definitions.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
|
@ -36,8 +38,8 @@ const StatusActionButtons = {
|
|||
ActionButtonContainer
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedStatusActions)
|
||||
...mapState(useServerSideStorageStore, {
|
||||
pinnedItems: store => new Set(store.prefsStorage.collections.pinnedStatusActions)
|
||||
}),
|
||||
buttons () {
|
||||
return BUTTONS.filter(x => x.if ? x.if(this.funcArg) : true)
|
||||
|
@ -101,12 +103,12 @@ const StatusActionButtons = {
|
|||
return this.pinnedItems.has(button.name)
|
||||
},
|
||||
unpin (button) {
|
||||
this.$store.commit('removeCollectionPreference', { path: 'collections.pinnedStatusActions', value: button.name })
|
||||
this.$store.dispatch('pushServerSideStorage')
|
||||
useServerSideStorageStore().removeCollectionPreference({ path: 'collections.pinnedStatusActions', value: button.name })
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
},
|
||||
pin (button) {
|
||||
this.$store.commit('addCollectionPreference', { path: 'collections.pinnedStatusActions', value: button.name })
|
||||
this.$store.dispatch('pushServerSideStorage')
|
||||
useServerSideStorageStore().addCollectionPreference({ path: 'collections.pinnedStatusActions', value: button.name })
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
},
|
||||
getComponent (button) {
|
||||
if (!this.$store.state.users.currentUser && button.anonLink) {
|
||||
|
|
|
@ -3,6 +3,8 @@ import { library } from '@fortawesome/fontawesome-svg-core'
|
|||
import pleromaTanMask from 'src/assets/pleromatan_apology_mask.png'
|
||||
import pleromaTanFoxMask from 'src/assets/pleromatan_apology_fox_mask.png'
|
||||
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
|
||||
import {
|
||||
faTimes
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
@ -36,8 +38,8 @@ const UpdateNotification = {
|
|||
shouldShow () {
|
||||
return !this.$store.state.instance.disableUpdateNotification &&
|
||||
this.$store.state.users.currentUser &&
|
||||
this.$store.state.serverSideStorage.flagStorage.updateCounter < CURRENT_UPDATE_COUNTER &&
|
||||
!this.$store.state.serverSideStorage.prefsStorage.simple.dontShowUpdateNotifs
|
||||
useServerSideStorageStore().flagStorage.updateCounter < CURRENT_UPDATE_COUNTER &&
|
||||
!useServerSideStorageStore().prefsStorage.simple.dontShowUpdateNotifs
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -46,13 +48,13 @@ const UpdateNotification = {
|
|||
},
|
||||
neverShowAgain () {
|
||||
this.toggleShow()
|
||||
this.$store.commit('setFlag', { flag: 'updateCounter', value: CURRENT_UPDATE_COUNTER })
|
||||
this.$store.commit('setPreference', { path: 'simple.dontShowUpdateNotifs', value: true })
|
||||
this.$store.dispatch('pushServerSideStorage')
|
||||
useServerSideStorageStore().setFlag({ flag: 'updateCounter', value: CURRENT_UPDATE_COUNTER })
|
||||
useServerSideStorageStore().setPreference({ path: 'simple.dontShowUpdateNotifs', value: true })
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
},
|
||||
dismiss () {
|
||||
this.$store.commit('setFlag', { flag: 'updateCounter', value: CURRENT_UPDATE_COUNTER })
|
||||
this.$store.dispatch('pushServerSideStorage')
|
||||
useServerSideStorageStore().setFlag({ flag: 'updateCounter', value: CURRENT_UPDATE_COUNTER })
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
|
|
@ -5,7 +5,6 @@ import users from './users.js'
|
|||
import api from './api.js'
|
||||
import config from './config.js'
|
||||
import profileConfig from './profileConfig.js'
|
||||
import serverSideStorage from './serverSideStorage.js'
|
||||
import adminSettings from './adminSettings.js'
|
||||
import authFlow from './auth_flow.js'
|
||||
import oauthTokens from './oauth_tokens.js'
|
||||
|
@ -20,7 +19,6 @@ export default {
|
|||
api,
|
||||
config,
|
||||
profileConfig,
|
||||
serverSideStorage,
|
||||
adminSettings,
|
||||
authFlow,
|
||||
oauthTokens,
|
||||
|
|
|
@ -4,8 +4,10 @@ import apiService from '../services/api/api.service.js'
|
|||
import oauthApi from '../services/new_api/oauth.js'
|
||||
import { compact, map, each, mergeWith, last, concat, uniq, isArray } from 'lodash'
|
||||
import { registerPushNotifications, unregisterPushNotifications } from '../services/sw/sw.js'
|
||||
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
|
||||
// TODO: Unify with mergeOrAdd in statuses.js
|
||||
export const mergeOrAdd = (arr, obj, item) => {
|
||||
|
@ -605,7 +607,8 @@ const users = {
|
|||
user.muteIds = []
|
||||
user.domainMutes = []
|
||||
commit('setCurrentUser', user)
|
||||
commit('setServerSideStorage', user)
|
||||
|
||||
useServerSideStorageStore().setServerSideStorage(user)
|
||||
commit('addNewUsers', [user])
|
||||
|
||||
dispatch('fetchEmoji')
|
||||
|
@ -615,7 +618,7 @@ const users = {
|
|||
|
||||
// Set our new backend interactor
|
||||
commit('setBackendInteractor', backendInteractorService(accessToken))
|
||||
dispatch('pushServerSideStorage')
|
||||
useServerSideStorageStore().pushServerSideStorage()
|
||||
|
||||
if (user.token) {
|
||||
dispatch('setWsToken', user.token)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { toRaw } from 'vue'
|
||||
import {
|
||||
isEqual,
|
||||
|
@ -323,200 +324,194 @@ export const _doMigrations = (cache) => {
|
|||
return cache
|
||||
}
|
||||
|
||||
export const mutations = {
|
||||
clearServerSideStorage (state) {
|
||||
const blankState = { ...cloneDeep(defaultState) }
|
||||
Object.keys(state).forEach(k => {
|
||||
state[k] = blankState[k]
|
||||
})
|
||||
export const useServerSideStorageStore = defineStore('serverSideStorage', {
|
||||
state() {
|
||||
return cloneDeep(defaultState)
|
||||
},
|
||||
setServerSideStorage (state, userData) {
|
||||
const live = userData.storage
|
||||
state.raw = live
|
||||
let cache = state.cache
|
||||
if (cache && cache._user !== userData.fqn) {
|
||||
console.warn('Cache belongs to another user! reinitializing local cache!')
|
||||
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) {
|
||||
console.debug(`Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`)
|
||||
recent = _wrapData({
|
||||
flagStorage: { ...flagsTemplate },
|
||||
prefsStorage: { ...defaultState.prefsStorage }
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
console.debug(`Data ${dirty ? 'needs' : 'doesn\'t need'} merging`)
|
||||
}
|
||||
|
||||
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 }
|
||||
|
||||
state.dirty = dirty || needUpload
|
||||
state.cache = recent
|
||||
// set local timestamp to smaller one if we don't have any changes
|
||||
if (stale && recent && !state.dirty) {
|
||||
state.cache._timestamp = Math.min(stale._timestamp, recent._timestamp)
|
||||
}
|
||||
state.flagStorage = state.cache.flagStorage
|
||||
state.prefsStorage = state.cache.prefsStorage
|
||||
},
|
||||
setFlag (state, { flag, value }) {
|
||||
state.flagStorage[flag] = value
|
||||
state.dirty = true
|
||||
},
|
||||
setPreference (state, { path, value }) {
|
||||
if (path.startsWith('_')) {
|
||||
throw new Error(`Tried to edit internal (starts with _) field '${path}', ignoring.`)
|
||||
}
|
||||
if (path.startsWith('collections') || path.startsWith('objectCollections')) {
|
||||
throw new Error(`Invalid operation 'set' for collection field '${path}', ignoring.`)
|
||||
}
|
||||
if (path.split(/\./g).length <= 1) {
|
||||
throw new Error(`Calling set on depth <= 1 (path: ${path}) is not allowed`)
|
||||
}
|
||||
if (path.split(/\./g).length > 3) {
|
||||
throw new Error(`Calling set on depth > 3 (path: ${path}) is not allowed`)
|
||||
}
|
||||
set(state.prefsStorage, path, value)
|
||||
state.prefsStorage._journal = [
|
||||
...state.prefsStorage._journal,
|
||||
{ operation: 'set', path, args: [value], timestamp: Date.now() }
|
||||
]
|
||||
state.dirty = true
|
||||
},
|
||||
unsetPreference (state, { path, value }) {
|
||||
if (path.startsWith('_')) {
|
||||
throw new Error(`Tried to edit internal (starts with _) field '${path}', ignoring.`)
|
||||
}
|
||||
if (path.startsWith('collections') || path.startsWith('objectCollections')) {
|
||||
throw new Error(`Invalid operation 'unset' for collection field '${path}', ignoring.`)
|
||||
}
|
||||
if (path.split(/\./g).length <= 2) {
|
||||
throw new Error(`Calling unset on depth <= 2 (path: ${path}) is not allowed`)
|
||||
}
|
||||
if (path.split(/\./g).length > 3) {
|
||||
throw new Error(`Calling unset on depth > 3 (path: ${path}) is not allowed`)
|
||||
}
|
||||
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('_')) {
|
||||
throw new Error(`tried to edit internal (starts with _) field '${path}'`)
|
||||
}
|
||||
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') {
|
||||
throw new Error('Object for storage is missing _key field!')
|
||||
}
|
||||
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() }
|
||||
]
|
||||
state.dirty = true
|
||||
},
|
||||
removeCollectionPreference (state, { path, value }) {
|
||||
if (path.startsWith('_')) {
|
||||
throw new Error(`tried to edit internal (starts with _) field '${path}', ignoring.`)
|
||||
}
|
||||
const collection = new Set(get(state.prefsStorage, path))
|
||||
collection.delete(value)
|
||||
set(state.prefsStorage, path, [...collection])
|
||||
state.prefsStorage._journal = [
|
||||
...state.prefsStorage._journal,
|
||||
{ operation: 'removeFromCollection', path, args: [value], timestamp: Date.now() }
|
||||
]
|
||||
state.dirty = true
|
||||
},
|
||||
reorderCollectionPreference (state, { path, value, movement }) {
|
||||
if (path.startsWith('_')) {
|
||||
throw new Error(`tried to edit internal (starts with _) field '${path}', ignoring.`)
|
||||
}
|
||||
const collection = get(state.prefsStorage, path)
|
||||
const newCollection = _moveItemInArray(collection, value, movement)
|
||||
set(state.prefsStorage, path, newCollection)
|
||||
state.prefsStorage._journal = [
|
||||
...state.prefsStorage._journal,
|
||||
{ operation: 'arrangeCollection', path, args: [value], timestamp: Date.now() }
|
||||
]
|
||||
state.dirty = true
|
||||
},
|
||||
updateCache (state, { username }) {
|
||||
state.prefsStorage._journal = _mergeJournal(state.prefsStorage._journal)
|
||||
state.cache = _wrapData({
|
||||
flagStorage: toRaw(state.flagStorage),
|
||||
prefsStorage: toRaw(state.prefsStorage)
|
||||
}, username)
|
||||
}
|
||||
}
|
||||
|
||||
const serverSideStorage = {
|
||||
state: {
|
||||
...cloneDeep(defaultState)
|
||||
},
|
||||
mutations,
|
||||
actions: {
|
||||
pushServerSideStorage ({ state, rootState, commit }, { force = false } = {}) {
|
||||
const needPush = state.dirty || force
|
||||
clearServerSideStorage () {
|
||||
const blankState = { ...cloneDeep(defaultState) }
|
||||
Object.keys(this).forEach(k => {
|
||||
this[k] = blankState[k]
|
||||
})
|
||||
},
|
||||
setServerSideStorage (userData) {
|
||||
const live = userData.storage
|
||||
this.raw = live
|
||||
let cache = this.cache
|
||||
if (cache && cache._user !== userData.fqn) {
|
||||
console.warn('Cache belongs to another user! reinitializing local cache!')
|
||||
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) {
|
||||
console.debug(`Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`)
|
||||
recent = _wrapData({
|
||||
flagStorage: { ...flagsTemplate },
|
||||
prefsStorage: { ...defaultState.prefsStorage }
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
console.debug(`Data ${dirty ? 'needs' : 'doesn\'t need'} merging`)
|
||||
}
|
||||
|
||||
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
|
||||
},
|
||||
setFlag ({ flag, value }) {
|
||||
this.flagStorage[flag] = value
|
||||
this.dirty = true
|
||||
},
|
||||
setPreference ({ path, value }) {
|
||||
if (path.startsWith('_')) {
|
||||
throw new Error(`Tried to edit internal (starts with _) field '${path}', ignoring.`)
|
||||
}
|
||||
if (path.startsWith('collections') || path.startsWith('objectCollections')) {
|
||||
throw new Error(`Invalid operation 'set' for collection field '${path}', ignoring.`)
|
||||
}
|
||||
if (path.split(/\./g).length <= 1) {
|
||||
throw new Error(`Calling set on depth <= 1 (path: ${path}) is not allowed`)
|
||||
}
|
||||
if (path.split(/\./g).length > 3) {
|
||||
throw new Error(`Calling set on depth > 3 (path: ${path}) is not allowed`)
|
||||
}
|
||||
set(this.prefsStorage, path, value)
|
||||
this.prefsStorage._journal = [
|
||||
...this.prefsStorage._journal,
|
||||
{ operation: 'set', path, args: [value], timestamp: Date.now() }
|
||||
]
|
||||
this.dirty = true
|
||||
},
|
||||
unsetPreference ({ path, value }) {
|
||||
if (path.startsWith('_')) {
|
||||
throw new Error(`Tried to edit internal (starts with _) field '${path}', ignoring.`)
|
||||
}
|
||||
if (path.startsWith('collections') || path.startsWith('objectCollections')) {
|
||||
throw new Error(`Invalid operation 'unset' for collection field '${path}', ignoring.`)
|
||||
}
|
||||
if (path.split(/\./g).length <= 2) {
|
||||
throw new Error(`Calling unset on depth <= 2 (path: ${path}) is not allowed`)
|
||||
}
|
||||
if (path.split(/\./g).length > 3) {
|
||||
throw new Error(`Calling unset on depth > 3 (path: ${path}) is not allowed`)
|
||||
}
|
||||
unset(this.prefsStorage, path, value)
|
||||
this.prefsStorage._journal = [
|
||||
...this.prefsStorage._journal,
|
||||
{ operation: 'unset', path, args: [], timestamp: Date.now() }
|
||||
]
|
||||
this.dirty = true
|
||||
},
|
||||
addCollectionPreference ({ path, value }) {
|
||||
if (path.startsWith('_')) {
|
||||
throw new Error(`tried to edit internal (starts with _) field '${path}'`)
|
||||
}
|
||||
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,
|
||||
{ operation: 'addToCollection', path, args: [value], timestamp: Date.now() }
|
||||
]
|
||||
this.dirty = true
|
||||
},
|
||||
removeCollectionPreference ({ path, value }) {
|
||||
if (path.startsWith('_')) {
|
||||
throw new Error(`tried to edit internal (starts with _) field '${path}', ignoring.`)
|
||||
}
|
||||
const collection = new Set(get(this.prefsStorage, path))
|
||||
collection.delete(value)
|
||||
set(this.prefsStorage, path, [...collection])
|
||||
this.prefsStorage._journal = [
|
||||
...this.prefsStorage._journal,
|
||||
{ operation: 'removeFromCollection', path, args: [value], timestamp: Date.now() }
|
||||
]
|
||||
this.dirty = true
|
||||
},
|
||||
reorderCollectionPreference ({ path, value, movement }) {
|
||||
if (path.startsWith('_')) {
|
||||
throw new Error(`tried to edit internal (starts with _) field '${path}', ignoring.`)
|
||||
}
|
||||
const collection = get(this.prefsStorage, path)
|
||||
const newCollection = _moveItemInArray(collection, value, movement)
|
||||
set(this.prefsStorage, path, newCollection)
|
||||
this.prefsStorage._journal = [
|
||||
...this.prefsStorage._journal,
|
||||
{ operation: 'arrangeCollection', path, args: [value], timestamp: Date.now() }
|
||||
]
|
||||
this.dirty = true
|
||||
},
|
||||
updateCache ({ username }) {
|
||||
this.prefsStorage._journal = _mergeJournal(this.prefsStorage._journal)
|
||||
this.cache = _wrapData({
|
||||
flagStorage: toRaw(this.flagStorage),
|
||||
prefsStorage: toRaw(this.prefsStorage)
|
||||
}, username)
|
||||
},
|
||||
pushServerSideStorage ({ force = false } = {}) {
|
||||
const needPush = this.dirty || force
|
||||
if (!needPush) return
|
||||
commit('updateCache', { username: rootState.users.currentUser.fqn })
|
||||
const params = { pleroma_settings_store: { 'pleroma-fe': state.cache } }
|
||||
rootState.api.backendInteractor
|
||||
this.updateCache({ username: window.vuex.state.users.currentUser.fqn })
|
||||
const params = { pleroma_settings_store: { 'pleroma-fe': this.cache } }
|
||||
window.vuex.state.api.backendInteractor
|
||||
.updateProfile({ params })
|
||||
.then((user) => {
|
||||
commit('setServerSideStorage', user)
|
||||
state.dirty = false
|
||||
this.setServerSideStorage(user)
|
||||
this.dirty = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default serverSideStorage
|
||||
})
|
|
@ -1,4 +1,5 @@
|
|||
import { cloneDeep } from 'lodash'
|
||||
import { setActivePinia, createPinia } from 'pinia'
|
||||
|
||||
import {
|
||||
VERSION,
|
||||
|
@ -10,49 +11,50 @@ import {
|
|||
_mergeFlags,
|
||||
_mergePrefs,
|
||||
_resetFlags,
|
||||
mutations,
|
||||
defaultState,
|
||||
newUserFlags
|
||||
} from 'src/modules/serverSideStorage.js'
|
||||
newUserFlags,
|
||||
useServerSideStorageStore,
|
||||
} from 'src/stores/serverSideStorage.js'
|
||||
|
||||
describe('The serverSideStorage module', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
})
|
||||
|
||||
describe('mutations', () => {
|
||||
describe('setServerSideStorage', () => {
|
||||
const { setServerSideStorage } = mutations
|
||||
const user = {
|
||||
created_at: new Date('1999-02-09'),
|
||||
storage: {}
|
||||
}
|
||||
|
||||
it('should initialize storage if none present', () => {
|
||||
const state = cloneDeep(defaultState)
|
||||
setServerSideStorage(state, user)
|
||||
expect(state.cache._version).to.eql(VERSION)
|
||||
expect(state.cache._timestamp).to.be.a('number')
|
||||
expect(state.cache.flagStorage).to.eql(defaultState.flagStorage)
|
||||
expect(state.cache.prefsStorage).to.eql(defaultState.prefsStorage)
|
||||
const store = useServerSideStorageStore()
|
||||
store.setServerSideStorage(store, user)
|
||||
expect(store.cache._version).to.eql(VERSION)
|
||||
expect(store.cache._timestamp).to.be.a('number')
|
||||
expect(store.cache.flagStorage).to.eql(defaultState.flagStorage)
|
||||
expect(store.cache.prefsStorage).to.eql(defaultState.prefsStorage)
|
||||
})
|
||||
|
||||
it('should initialize storage with proper flags for new users if none present', () => {
|
||||
const state = cloneDeep(defaultState)
|
||||
setServerSideStorage(state, { ...user, created_at: new Date() })
|
||||
expect(state.cache._version).to.eql(VERSION)
|
||||
expect(state.cache._timestamp).to.be.a('number')
|
||||
expect(state.cache.flagStorage).to.eql(newUserFlags)
|
||||
expect(state.cache.prefsStorage).to.eql(defaultState.prefsStorage)
|
||||
const store = useServerSideStorageStore()
|
||||
store.setServerSideStorage({ ...user, created_at: new Date() })
|
||||
expect(store.cache._version).to.eql(VERSION)
|
||||
expect(store.cache._timestamp).to.be.a('number')
|
||||
expect(store.cache.flagStorage).to.eql(newUserFlags)
|
||||
expect(store.cache.prefsStorage).to.eql(defaultState.prefsStorage)
|
||||
})
|
||||
|
||||
it('should merge flags even if remote timestamp is older', () => {
|
||||
const state = {
|
||||
...cloneDeep(defaultState),
|
||||
cache: {
|
||||
_timestamp: Date.now(),
|
||||
_version: VERSION,
|
||||
...cloneDeep(defaultState)
|
||||
}
|
||||
const store = useServerSideStorageStore()
|
||||
store.cache = {
|
||||
_timestamp: Date.now(),
|
||||
_version: VERSION,
|
||||
...cloneDeep(defaultState)
|
||||
}
|
||||
setServerSideStorage(
|
||||
state,
|
||||
|
||||
store.setServerSideStorage(
|
||||
{
|
||||
...user,
|
||||
storage: {
|
||||
|
@ -68,19 +70,18 @@ describe('The serverSideStorage module', () => {
|
|||
}
|
||||
}
|
||||
)
|
||||
expect(state.cache.flagStorage).to.eql({
|
||||
|
||||
expect(store.cache.flagStorage).to.eql({
|
||||
...defaultState.flagStorage,
|
||||
updateCounter: 1
|
||||
})
|
||||
})
|
||||
|
||||
it('should reset local timestamp to remote if contents are the same', () => {
|
||||
const state = {
|
||||
...cloneDeep(defaultState),
|
||||
cache: null
|
||||
}
|
||||
setServerSideStorage(
|
||||
state,
|
||||
const store = useServerSideStorageStore()
|
||||
store.cache = null
|
||||
|
||||
store.setServerSideStorage(
|
||||
{
|
||||
...user,
|
||||
storage: {
|
||||
|
@ -93,97 +94,95 @@ describe('The serverSideStorage module', () => {
|
|||
}
|
||||
}
|
||||
)
|
||||
expect(state.cache._timestamp).to.eql(123)
|
||||
expect(state.flagStorage.updateCounter).to.eql(999)
|
||||
expect(state.cache.flagStorage.updateCounter).to.eql(999)
|
||||
expect(store.cache._timestamp).to.eql(123)
|
||||
expect(store.flagStorage.updateCounter).to.eql(999)
|
||||
expect(store.cache.flagStorage.updateCounter).to.eql(999)
|
||||
})
|
||||
|
||||
it('should remote version if local missing', () => {
|
||||
const state = cloneDeep(defaultState)
|
||||
setServerSideStorage(state, user)
|
||||
expect(state.cache._version).to.eql(VERSION)
|
||||
expect(state.cache._timestamp).to.be.a('number')
|
||||
expect(state.cache.flagStorage).to.eql(defaultState.flagStorage)
|
||||
const store = useServerSideStorageStore()
|
||||
store.setServerSideStorage(store, user)
|
||||
expect(store.cache._version).to.eql(VERSION)
|
||||
expect(store.cache._timestamp).to.be.a('number')
|
||||
expect(store.cache.flagStorage).to.eql(defaultState.flagStorage)
|
||||
})
|
||||
})
|
||||
describe('setPreference', () => {
|
||||
const { setPreference, unsetPreference, updateCache, addCollectionPreference, removeCollectionPreference } = mutations
|
||||
|
||||
it('should set preference and update journal log accordingly', () => {
|
||||
const state = cloneDeep(defaultState)
|
||||
setPreference(state, { path: 'simple.testing', value: 1 })
|
||||
expect(state.prefsStorage.simple.testing).to.eql(1)
|
||||
expect(state.prefsStorage._journal.length).to.eql(1)
|
||||
expect(state.prefsStorage._journal[0]).to.eql({
|
||||
const store = useServerSideStorageStore()
|
||||
store.setPreference({ path: 'simple.testing', value: 1 })
|
||||
expect(store.prefsStorage.simple.testing).to.eql(1)
|
||||
expect(store.prefsStorage._journal.length).to.eql(1)
|
||||
expect(store.prefsStorage._journal[0]).to.eql({
|
||||
path: 'simple.testing',
|
||||
operation: 'set',
|
||||
args: [1],
|
||||
// should have A timestamp, we don't really care what it is
|
||||
timestamp: state.prefsStorage._journal[0].timestamp
|
||||
timestamp: store.prefsStorage._journal[0].timestamp
|
||||
})
|
||||
})
|
||||
|
||||
it('should keep journal to a minimum', () => {
|
||||
const state = cloneDeep(defaultState)
|
||||
setPreference(state, { path: 'simple.testing', value: 1 })
|
||||
setPreference(state, { path: 'simple.testing', value: 2 })
|
||||
addCollectionPreference(state, { path: 'collections.testing', value: 2 })
|
||||
removeCollectionPreference(state, { path: 'collections.testing', value: 2 })
|
||||
updateCache(state, { username: 'test' })
|
||||
expect(state.prefsStorage.simple.testing).to.eql(2)
|
||||
expect(state.prefsStorage.collections.testing).to.eql([])
|
||||
expect(state.prefsStorage._journal.length).to.eql(2)
|
||||
expect(state.prefsStorage._journal[0]).to.eql({
|
||||
const store = useServerSideStorageStore()
|
||||
store.setPreference({ path: 'simple.testing', value: 1 })
|
||||
store.setPreference({ path: 'simple.testing', value: 2 })
|
||||
store.addCollectionPreference({ path: 'collections.testing', value: 2 })
|
||||
store.removeCollectionPreference({ path: 'collections.testing', value: 2 })
|
||||
store.updateCache({ username: 'test' })
|
||||
expect(store.prefsStorage.simple.testing).to.eql(2)
|
||||
expect(store.prefsStorage.collections.testing).to.eql([])
|
||||
expect(store.prefsStorage._journal.length).to.eql(2)
|
||||
expect(store.prefsStorage._journal[0]).to.eql({
|
||||
path: 'simple.testing',
|
||||
operation: 'set',
|
||||
args: [2],
|
||||
// should have A timestamp, we don't really care what it is
|
||||
timestamp: state.prefsStorage._journal[0].timestamp
|
||||
timestamp: store.prefsStorage._journal[0].timestamp
|
||||
})
|
||||
expect(state.prefsStorage._journal[1]).to.eql({
|
||||
expect(store.prefsStorage._journal[1]).to.eql({
|
||||
path: 'collections.testing',
|
||||
operation: 'removeFromCollection',
|
||||
args: [2],
|
||||
// should have A timestamp, we don't really care what it is
|
||||
timestamp: state.prefsStorage._journal[1].timestamp
|
||||
timestamp: store.prefsStorage._journal[1].timestamp
|
||||
})
|
||||
})
|
||||
|
||||
it('should remove duplicate entries from journal', () => {
|
||||
const state = cloneDeep(defaultState)
|
||||
setPreference(state, { path: 'simple.testing', value: 1 })
|
||||
setPreference(state, { path: 'simple.testing', value: 1 })
|
||||
addCollectionPreference(state, { path: 'collections.testing', value: 2 })
|
||||
addCollectionPreference(state, { path: 'collections.testing', value: 2 })
|
||||
updateCache(state, { username: 'test' })
|
||||
expect(state.prefsStorage.simple.testing).to.eql(1)
|
||||
expect(state.prefsStorage.collections.testing).to.eql([2])
|
||||
expect(state.prefsStorage._journal.length).to.eql(2)
|
||||
const store = useServerSideStorageStore()
|
||||
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 })
|
||||
store.updateCache({ username: 'test' })
|
||||
expect(store.prefsStorage.simple.testing).to.eql(1)
|
||||
expect(store.prefsStorage.collections.testing).to.eql([2])
|
||||
expect(store.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)
|
||||
const store = useServerSideStorageStore()
|
||||
store.setPreference({ path: 'simple.object.foo', value: 1 })
|
||||
store.unsetPreference({ path: 'simple.object.foo' })
|
||||
store.updateCache(store, { username: 'test' })
|
||||
expect(store.prefsStorage.simple.object).to.not.have.property('foo')
|
||||
expect(store.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 })
|
||||
expect(() => unsetPreference(state, { path: 'simple' })).to.throw()
|
||||
expect(() => unsetPreference(state, { path: 'simple.object' })).to.throw()
|
||||
const store = useServerSideStorageStore()
|
||||
store.setPreference({ path: 'simple.object.foo', value: 1 })
|
||||
expect(() => store.unsetPreference({ path: 'simple' })).to.throw()
|
||||
expect(() => store.unsetPreference({ path: 'simple.object' })).to.throw()
|
||||
})
|
||||
|
||||
it('should not allow (un)setting depth > 3', () => {
|
||||
const state = cloneDeep(defaultState)
|
||||
setPreference(state, { path: 'simple.object', value: {} })
|
||||
expect(() => setPreference(state, { path: 'simple.object.lv3', value: 1 })).to.not.throw()
|
||||
expect(() => setPreference(state, { path: 'simple.object.lv3.lv4', value: 1})).to.throw()
|
||||
expect(() => unsetPreference(state, { path: 'simple.object.lv3', value: 1 })).to.not.throw()
|
||||
expect(() => unsetPreference(state, { path: 'simple.object.lv3.lv4', value: 1})).to.throw()
|
||||
const store = useServerSideStorageStore()
|
||||
store.setPreference({ path: 'simple.object', value: {} })
|
||||
expect(() => store.setPreference({ path: 'simple.object.lv3', value: 1 })).to.not.throw()
|
||||
expect(() => store.setPreference({ path: 'simple.object.lv3.lv4', value: 1})).to.throw()
|
||||
expect(() => store.unsetPreference({ path: 'simple.object.lv3', value: 1 })).to.not.throw()
|
||||
expect(() => store.unsetPreference({ path: 'simple.object.lv3.lv4', value: 1})).to.throw()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Add table
Reference in a new issue