aboutsummaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/api.js3
-rw-r--r--src/modules/config.js1
-rw-r--r--src/modules/lists.js100
-rw-r--r--src/modules/serverSideStorage.js223
-rw-r--r--src/modules/users.js17
5 files changed, 298 insertions, 46 deletions
diff --git a/src/modules/api.js b/src/modules/api.js
index 22740590..0acc03f1 100644
--- a/src/modules/api.js
+++ b/src/modules/api.js
@@ -15,6 +15,9 @@ const api = {
mastoUserSocketStatus: null,
followRequests: []
},
+ getters: {
+ followRequestCount: state => state.followRequests.length
+ },
mutations: {
setBackendInteractor (state, backendInteractor) {
state.backendInteractor = backendInteractor
diff --git a/src/modules/config.js b/src/modules/config.js
index be30dee3..eeaac917 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -89,7 +89,6 @@ export const defaultState = {
contentColumnWidth: '45rem',
notifsColumnWidth: '25rem',
navbarColumnStretch: false,
- listsNavigation: false,
greentext: undefined, // instance default
useAtIcon: undefined, // instance default
mentionLinkDisplay: undefined, // instance default
diff --git a/src/modules/lists.js b/src/modules/lists.js
index 84c15759..22fed800 100644
--- a/src/modules/lists.js
+++ b/src/modules/lists.js
@@ -9,27 +9,43 @@ export const mutations = {
setLists (state, value) {
state.allLists = value
},
- setList (state, { id, title }) {
- if (!state.allListsObject[id]) {
- state.allListsObject[id] = {}
+ setList (state, { listId, title }) {
+ if (!state.allListsObject[listId]) {
+ state.allListsObject[listId] = { accountIds: [] }
}
- state.allListsObject[id].title = title
+ state.allListsObject[listId].title = title
- if (!find(state.allLists, { id })) {
- state.allLists.push({ id, title })
+ const entry = find(state.allLists, { id: listId })
+ if (!entry) {
+ state.allLists.push({ id: listId, title })
} else {
- find(state.allLists, { id }).title = title
+ entry.title = title
}
},
- setListAccounts (state, { id, accountIds }) {
- if (!state.allListsObject[id]) {
- state.allListsObject[id] = {}
+ setListAccounts (state, { listId, accountIds }) {
+ if (!state.allListsObject[listId]) {
+ state.allListsObject[listId] = { accountIds: [] }
}
- state.allListsObject[id].accountIds = accountIds
+ state.allListsObject[listId].accountIds = accountIds
},
- deleteList (state, { id }) {
- delete state.allListsObject[id]
- remove(state.allLists, list => list.id === id)
+ addListAccount (state, { listId, accountId }) {
+ if (!state.allListsObject[listId]) {
+ state.allListsObject[listId] = { accountIds: [] }
+ }
+ state.allListsObject[listId].accountIds.push(accountId)
+ },
+ removeListAccount (state, { listId, accountId }) {
+ if (!state.allListsObject[listId]) {
+ state.allListsObject[listId] = { accountIds: [] }
+ }
+ const { accountIds } = state.allListsObject[listId]
+ const set = new Set(accountIds)
+ set.delete(accountId)
+ state.allListsObject[listId].accountIds = [...set]
+ },
+ deleteList (state, { listId }) {
+ delete state.allListsObject[listId]
+ remove(state.allLists, list => list.id === listId)
}
}
@@ -40,37 +56,57 @@ const actions = {
createList ({ rootState, commit }, { title }) {
return rootState.api.backendInteractor.createList({ title })
.then((list) => {
- commit('setList', { id: list.id, title })
+ commit('setList', { listId: list.id, title })
return list
})
},
- fetchList ({ rootState, commit }, { id }) {
- return rootState.api.backendInteractor.getList({ id })
- .then((list) => commit('setList', { id: list.id, title: list.title }))
+ fetchList ({ rootState, commit }, { listId }) {
+ return rootState.api.backendInteractor.getList({ listId })
+ .then((list) => commit('setList', { listId: list.id, title: list.title }))
},
- fetchListAccounts ({ rootState, commit }, { id }) {
- return rootState.api.backendInteractor.getListAccounts({ id })
- .then((accountIds) => commit('setListAccounts', { id, accountIds }))
+ fetchListAccounts ({ rootState, commit }, { listId }) {
+ return rootState.api.backendInteractor.getListAccounts({ listId })
+ .then((accountIds) => commit('setListAccounts', { listId, accountIds }))
},
- setList ({ rootState, commit }, { id, title }) {
- rootState.api.backendInteractor.updateList({ id, title })
- commit('setList', { id, title })
+ setList ({ rootState, commit }, { listId, title }) {
+ rootState.api.backendInteractor.updateList({ listId, title })
+ commit('setList', { listId, title })
},
- setListAccounts ({ rootState, commit }, { id, accountIds }) {
- const saved = rootState.lists.allListsObject[id].accountIds || []
+ setListAccounts ({ rootState, commit }, { listId, accountIds }) {
+ const saved = rootState.lists.allListsObject[listId].accountIds || []
const added = accountIds.filter(id => !saved.includes(id))
const removed = saved.filter(id => !accountIds.includes(id))
- commit('setListAccounts', { id, accountIds })
+ commit('setListAccounts', { listId, accountIds })
if (added.length > 0) {
- rootState.api.backendInteractor.addAccountsToList({ id, accountIds: added })
+ rootState.api.backendInteractor.addAccountsToList({ listId, accountIds: added })
}
if (removed.length > 0) {
- rootState.api.backendInteractor.removeAccountsFromList({ id, accountIds: removed })
+ rootState.api.backendInteractor.removeAccountsFromList({ listId, accountIds: removed })
}
},
- deleteList ({ rootState, commit }, { id }) {
- rootState.api.backendInteractor.deleteList({ id })
- commit('deleteList', { id })
+ addListAccount ({ rootState, commit }, { listId, accountId }) {
+ return rootState
+ .api
+ .backendInteractor
+ .addAccountsToList({ listId, accountIds: [accountId] })
+ .then((result) => {
+ commit('addListAccount', { listId, accountId })
+ return result
+ })
+ },
+ removeListAccount ({ rootState, commit }, { listId, accountId }) {
+ return rootState
+ .api
+ .backendInteractor
+ .removeAccountsFromList({ listId, accountIds: [accountId] })
+ .then((result) => {
+ commit('removeListAccount', { listId, accountId })
+ return result
+ })
+ },
+ deleteList ({ rootState, commit }, { listId }) {
+ rootState.api.backendInteractor.deleteList({ listId })
+ commit('deleteList', { listId })
}
}
diff --git a/src/modules/serverSideStorage.js b/src/modules/serverSideStorage.js
index e516a6e6..56164be7 100644
--- a/src/modules/serverSideStorage.js
+++ b/src/modules/serverSideStorage.js
@@ -1,5 +1,5 @@
import { toRaw } from 'vue'
-import { isEqual, cloneDeep } from 'lodash'
+import { isEqual, cloneDeep, set, get, clamp, flatten, groupBy, findLastIndex, takeRight } from 'lodash'
import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js'
export const VERSION = 1
@@ -14,14 +14,21 @@ export const defaultState = {
// storage of flags - stuff that can only be set and incremented
flagStorage: {
updateCounter: 0, // Counter for most recent update notification seen
- // TODO move to prefsStorage when that becomes a thing since only way
- // this can be reset is by complete reset of all flags
- dontShowUpdateNotifs: 0, // if user chose to not show update notifications ever again
reset: 0 // special flag that can be used to force-reset all flags, debug purposes only
// special reset codes:
// 1000: trim keys to those known by currently running FE
// 1001: same as above + reset everything to 0
},
+ prefsStorage: {
+ _journal: [],
+ simple: {
+ dontShowUpdateNotifs: false,
+ collapseNav: false
+ },
+ collections: {
+ pinnedNavItems: ['home', 'dms', 'chats']
+ }
+ },
// raw data
raw: null,
// local cache
@@ -33,14 +40,43 @@ export const newUserFlags = {
updateCounter: CURRENT_UPDATE_COUNTER // new users don't need to see update notification
}
-const _wrapData = (data) => ({
+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
+}
+
+const _wrapData = (data, userName) => ({
...data,
+ _user: userName,
_timestamp: Date.now(),
_version: VERSION
})
const _checkValidity = (data) => data._timestamp > 0 && data._version > 0
+const _verifyPrefs = (state) => {
+ state.prefsStorage = state.prefsStorage || {
+ simple: {},
+ collections: {}
+ }
+ Object.entries(defaultState.prefsStorage.simple).forEach(([k, v]) => {
+ if (typeof v === 'number' || typeof v === 'boolean') return
+ console.warn(`Preference simple.${k} as invalid type, reinitializing`)
+ set(state.prefsStorage.simple, k, defaultState.prefsStorage.simple[k])
+ })
+ Object.entries(defaultState.prefsStorage.collections).forEach(([k, v]) => {
+ if (Array.isArray(v)) return
+ console.warn(`Preference collections.${k} as invalid type, reinitializing`)
+ set(state.prefsStorage.collections, k, defaultState.prefsStorage.collections[k])
+ })
+}
+
export const _getRecentData = (cache, live) => {
const result = { recent: null, stale: null, needUpload: false }
const cacheValid = _checkValidity(cache || {})
@@ -85,6 +121,8 @@ export const _getAllFlags = (recent, stale) => {
}
export const _mergeFlags = (recent, stale, allFlagKeys) => {
+ if (!stale.flagStorage) return recent.flagStorage
+ if (!recent.flagStorage) return stale.flagStorage
return Object.fromEntries(allFlagKeys.map(flag => {
const recentFlag = recent.flagStorage[flag]
const staleFlag = stale.flagStorage[flag]
@@ -93,6 +131,88 @@ export const _mergeFlags = (recent, stale, allFlagKeys) => {
}))
}
+const _mergeJournal = (...journals) => {
+ // Ignore invalid journal entries
+ const allJournals = flatten(
+ journals.map(j => Array.isArray(j) ? j : [])
+ ).filter(entry =>
+ Object.prototype.hasOwnProperty.call(entry, 'path') &&
+ Object.prototype.hasOwnProperty.call(entry, 'operation') &&
+ Object.prototype.hasOwnProperty.call(entry, 'args') &&
+ Object.prototype.hasOwnProperty.call(entry, 'timestamp')
+ )
+ const grouped = groupBy(allJournals, 'path')
+ const trimmedGrouped = Object.entries(grouped).map(([path, journal]) => {
+ // side effect
+ journal.sort((a, b) => a.timestamp > b.timestamp ? 1 : -1)
+
+ if (path.startsWith('collections')) {
+ const lastRemoveIndex = findLastIndex(journal, ({ operation }) => operation === 'removeFromCollection')
+ // everything before last remove is unimportant
+ if (lastRemoveIndex > 0) {
+ return journal.slice(lastRemoveIndex)
+ } else {
+ // everything else doesn't need trimming
+ return journal
+ }
+ } else if (path.startsWith('simple')) {
+ // Only the last record is important
+ return takeRight(journal)
+ } else {
+ return journal
+ }
+ })
+ return flatten(trimmedGrouped)
+ .sort((a, b) => a.timestamp > b.timestamp ? 1 : -1)
+}
+
+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 = _mergeJournal(staleJournal, recentJournal)
+ totalJournal.forEach(({ path, timestamp, operation, command, 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
+ case 'addToCollection':
+ set(resultOutput, path, Array.from(new Set(get(resultOutput, path)).add(args[0])))
+ break
+ case 'removeFromCollection': {
+ const newSet = new Set(get(resultOutput, path))
+ newSet.delete(args[0])
+ set(resultOutput, path, Array.from(newSet))
+ break
+ }
+ case 'reorderCollection': {
+ const [value, movement] = args
+ set(resultOutput, path, _moveItemInArray(get(resultOutput, path), value, movement))
+ 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) => {
let result = { ...totalFlags }
const allFlagKeys = Object.keys(totalFlags)
@@ -149,10 +269,17 @@ export const _doMigrations = (cache) => {
}
export const mutations = {
+ clearServerSideStorage (state, userData) {
+ state = { ...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)
@@ -165,7 +292,8 @@ export const mutations = {
if (recent === null) {
console.debug(`Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`)
recent = _wrapData({
- flagStorage: { ...flagsTemplate }
+ flagStorage: { ...flagsTemplate },
+ prefsStorage: { ...defaultState.prefsStorage }
})
}
@@ -180,17 +308,23 @@ export const mutations = {
const allFlagKeys = _getAllFlags(recent, stale)
let totalFlags
+ let totalPrefs
if (dirty) {
// Merge the flags
- console.debug('Merging 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 = totalFlags
+ recent.flagStorage = { ...flagsTemplate, ...totalFlags }
+ recent.prefsStorage = { ...defaultState.prefsStorage, ...totalPrefs }
state.dirty = dirty || needsUpload
state.cache = recent
@@ -199,10 +333,72 @@ export const mutations = {
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('_')) {
+ console.error(`tried to edit internal (starts with _) field '${path}', ignoring.`)
+ return
+ }
+ set(state.prefsStorage, path, value)
+ state.prefsStorage._journal = [
+ ...state.prefsStorage._journal,
+ { operation: 'set', path, args: [value], 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])
+ state.prefsStorage._journal = [
+ ...state.prefsStorage._journal,
+ { operation: 'addToCollection', path, args: [value], timestamp: Date.now() }
+ ]
+ state.dirty = true
+ },
+ removeCollectionPreference (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.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('_')) {
+ console.error(`tried to edit internal (starts with _) field '${path}', ignoring.`)
+ return
+ }
+ 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)
}
}
@@ -214,15 +410,16 @@ const serverSideStorage = {
actions: {
pushServerSideStorage ({ state, rootState, commit }, { force = false } = {}) {
const needPush = state.dirty || force
+ console.log(needPush)
if (!needPush) return
- state.cache = _wrapData({
- flagStorage: toRaw(state.flagStorage)
- })
+ commit('updateCache', { username: rootState.users.currentUser.fqn })
const params = { pleroma_settings_store: { 'pleroma-fe': state.cache } }
rootState.api.backendInteractor
.updateProfile({ params })
- .then((user) => commit('setServerSideStorage', user))
- state.dirty = false
+ .then((user) => {
+ commit('setServerSideStorage', user)
+ state.dirty = false
+ })
}
}
}
diff --git a/src/modules/users.js b/src/modules/users.js
index be0bc997..de28766a 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -171,6 +171,9 @@ export const mutations = {
state.relationships[relationship.id] = relationship
})
},
+ updateUserInLists (state, { id, inLists }) {
+ state.usersObject[id].inLists = inLists
+ },
saveBlockIds (state, blockIds) {
state.currentUser.blockIds = blockIds
},
@@ -298,6 +301,12 @@ const users = {
.then((relationships) => store.commit('updateUserRelationship', relationships))
}
},
+ fetchUserInLists (store, id) {
+ if (store.state.currentUser) {
+ store.rootState.api.backendInteractor.fetchUserInLists({ id })
+ .then((inLists) => store.commit('updateUserInLists', { id, inLists }))
+ }
+ },
fetchBlocks (store) {
return store.rootState.api.backendInteractor.fetchBlocks()
.then((blocks) => {
@@ -509,6 +518,7 @@ const users = {
store.dispatch('stopFetchingTimeline', 'friends')
store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken()))
store.dispatch('stopFetchingNotifications')
+ store.dispatch('stopFetchingLists')
store.dispatch('stopFetchingFollowRequests')
store.commit('clearNotifications')
store.commit('resetStatuses')
@@ -516,6 +526,7 @@ const users = {
store.dispatch('setLastTimeline', 'public-timeline')
store.dispatch('setLayoutWidth', windowWidth())
store.dispatch('setLayoutHeight', windowHeight())
+ store.commit('clearServerSideStorage')
})
},
loginUser (store, accessToken) {
@@ -562,6 +573,12 @@ const users = {
store.dispatch('startFetchingChats')
}
+ store.dispatch('startFetchingLists')
+
+ if (user.locked) {
+ store.dispatch('startFetchingFollowRequests')
+ }
+
if (store.getters.mergedConfig.useStreamingApi) {
store.dispatch('fetchTimeline', 'friends', { since: null })
store.dispatch('fetchNotifications', { since: null })