Merge remote-tracking branch 'origin/develop' into migrate/vuex-to-pinia
This commit is contained in:
commit
58e18d48df
489 changed files with 31167 additions and 9871 deletions
229
src/modules/adminSettings.js
Normal file
229
src/modules/adminSettings.js
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
import { set, get, cloneDeep, differenceWith, isEqual, flatten } from 'lodash'
|
||||
|
||||
export const defaultState = {
|
||||
frontends: [],
|
||||
loaded: false,
|
||||
needsReboot: null,
|
||||
config: null,
|
||||
modifiedPaths: null,
|
||||
descriptions: null,
|
||||
draft: null,
|
||||
dbConfigEnabled: null
|
||||
}
|
||||
|
||||
export const newUserFlags = {
|
||||
...defaultState.flagStorage
|
||||
}
|
||||
|
||||
const adminSettingsStorage = {
|
||||
state: {
|
||||
...cloneDeep(defaultState)
|
||||
},
|
||||
mutations: {
|
||||
setInstanceAdminNoDbConfig (state) {
|
||||
state.loaded = false
|
||||
state.dbConfigEnabled = false
|
||||
},
|
||||
setAvailableFrontends (state, { frontends }) {
|
||||
state.frontends = frontends.map(f => {
|
||||
f.installedRefs = f.installed_refs
|
||||
if (f.name === 'pleroma-fe') {
|
||||
f.refs = ['master', 'develop']
|
||||
} else {
|
||||
f.refs = [f.ref]
|
||||
}
|
||||
return f
|
||||
})
|
||||
},
|
||||
updateAdminSettings (state, { config, modifiedPaths }) {
|
||||
state.loaded = true
|
||||
state.dbConfigEnabled = true
|
||||
state.config = config
|
||||
state.modifiedPaths = modifiedPaths
|
||||
},
|
||||
updateAdminDescriptions (state, { descriptions }) {
|
||||
state.descriptions = descriptions
|
||||
},
|
||||
updateAdminDraft (state, { path, value }) {
|
||||
const [group, key, subkey] = path
|
||||
const parent = [group, key, subkey]
|
||||
|
||||
set(state.draft, path, value)
|
||||
|
||||
// force-updating grouped draft to trigger refresh of group settings
|
||||
if (path.length > parent.length) {
|
||||
set(state.draft, parent, cloneDeep(get(state.draft, parent)))
|
||||
}
|
||||
},
|
||||
resetAdminDraft (state) {
|
||||
state.draft = cloneDeep(state.config)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
loadFrontendsStuff ({ state, rootState, dispatch, commit }) {
|
||||
rootState.api.backendInteractor.fetchAvailableFrontends()
|
||||
.then(frontends => commit('setAvailableFrontends', { frontends }))
|
||||
},
|
||||
loadAdminStuff ({ state, rootState, dispatch, commit }) {
|
||||
rootState.api.backendInteractor.fetchInstanceDBConfig()
|
||||
.then(backendDbConfig => {
|
||||
if (backendDbConfig.error) {
|
||||
if (backendDbConfig.error.status === 400) {
|
||||
backendDbConfig.error.json().then(errorJson => {
|
||||
if (/configurable_from_database/.test(errorJson.error)) {
|
||||
commit('setInstanceAdminNoDbConfig')
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
dispatch('setInstanceAdminSettings', { backendDbConfig })
|
||||
}
|
||||
})
|
||||
if (state.descriptions === null) {
|
||||
rootState.api.backendInteractor.fetchInstanceConfigDescriptions()
|
||||
.then(backendDescriptions => dispatch('setInstanceAdminDescriptions', { backendDescriptions }))
|
||||
}
|
||||
},
|
||||
setInstanceAdminSettings ({ state, commit, dispatch }, { backendDbConfig }) {
|
||||
const config = state.config || {}
|
||||
const modifiedPaths = new Set()
|
||||
backendDbConfig.configs.forEach(c => {
|
||||
const path = [c.group, c.key]
|
||||
if (c.db) {
|
||||
// Path elements can contain dot, therefore we use ' -> ' as a separator instead
|
||||
// Using strings for modified paths for easier searching
|
||||
c.db.forEach(x => modifiedPaths.add([...path, x].join(' -> ')))
|
||||
}
|
||||
const convert = (value) => {
|
||||
if (Array.isArray(value) && value.length > 0 && value[0].tuple) {
|
||||
return value.reduce((acc, c) => {
|
||||
return { ...acc, [c.tuple[0]]: convert(c.tuple[1]) }
|
||||
}, {})
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
set(config, path, convert(c.value))
|
||||
})
|
||||
commit('updateAdminSettings', { config, modifiedPaths })
|
||||
commit('resetAdminDraft')
|
||||
},
|
||||
setInstanceAdminDescriptions ({ state, commit, dispatch }, { backendDescriptions }) {
|
||||
const convert = ({ children, description, label, key = '<ROOT>', group, suggestions }, path, acc) => {
|
||||
const newPath = group ? [group, key] : [key]
|
||||
const obj = { description, label, suggestions }
|
||||
if (Array.isArray(children)) {
|
||||
children.forEach(c => {
|
||||
convert(c, newPath, obj)
|
||||
})
|
||||
}
|
||||
set(acc, newPath, obj)
|
||||
}
|
||||
|
||||
const descriptions = {}
|
||||
backendDescriptions.forEach(d => convert(d, '', descriptions))
|
||||
commit('updateAdminDescriptions', { descriptions })
|
||||
},
|
||||
|
||||
// This action takes draft state, diffs it with live config state and then pushes
|
||||
// only differences between the two. Difference detection only work up to subkey (third) level.
|
||||
pushAdminDraft ({ rootState, state, commit, dispatch }) {
|
||||
// TODO cleanup paths in modifiedPaths
|
||||
const convert = (value) => {
|
||||
if (typeof value !== 'object') {
|
||||
return value
|
||||
} else if (Array.isArray(value)) {
|
||||
return value.map(convert)
|
||||
} else {
|
||||
return Object.entries(value).map(([k, v]) => ({ tuple: [k, v] }))
|
||||
}
|
||||
}
|
||||
|
||||
// Getting all group-keys used in config
|
||||
const allGroupKeys = flatten(
|
||||
Object
|
||||
.entries(state.config)
|
||||
.map(
|
||||
([group, lv1data]) => Object
|
||||
.keys(lv1data)
|
||||
.map((key) => ({ group, key }))
|
||||
)
|
||||
)
|
||||
|
||||
// Only using group-keys where there are changes detected
|
||||
const changedGroupKeys = allGroupKeys.filter(({ group, key }) => {
|
||||
return !isEqual(state.config[group][key], state.draft[group][key])
|
||||
})
|
||||
|
||||
// Here we take all changed group-keys and get all changed subkeys
|
||||
const changed = changedGroupKeys.map(({ group, key }) => {
|
||||
const config = state.config[group][key]
|
||||
const draft = state.draft[group][key]
|
||||
|
||||
// We convert group-key value into entries arrays
|
||||
const eConfig = Object.entries(config)
|
||||
const eDraft = Object.entries(draft)
|
||||
|
||||
// Then those entries array we diff so only changed subkey entries remain
|
||||
// We use the diffed array to reconstruct the object and then shove it into convert()
|
||||
return ({ group, key, value: convert(Object.fromEntries(differenceWith(eDraft, eConfig, isEqual))) })
|
||||
})
|
||||
|
||||
rootState.api.backendInteractor.pushInstanceDBConfig({
|
||||
payload: {
|
||||
configs: changed
|
||||
}
|
||||
})
|
||||
.then(() => rootState.api.backendInteractor.fetchInstanceDBConfig())
|
||||
.then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig }))
|
||||
},
|
||||
pushAdminSetting ({ rootState, state, commit, dispatch }, { path, value }) {
|
||||
const [group, key, ...rest] = Array.isArray(path) ? path : path.split(/\./g)
|
||||
const clone = {} // not actually cloning the entire thing to avoid excessive writes
|
||||
set(clone, rest, value)
|
||||
|
||||
// TODO cleanup paths in modifiedPaths
|
||||
const convert = (value) => {
|
||||
if (typeof value !== 'object') {
|
||||
return value
|
||||
} else if (Array.isArray(value)) {
|
||||
return value.map(convert)
|
||||
} else {
|
||||
return Object.entries(value).map(([k, v]) => ({ tuple: [k, v] }))
|
||||
}
|
||||
}
|
||||
|
||||
rootState.api.backendInteractor.pushInstanceDBConfig({
|
||||
payload: {
|
||||
configs: [{
|
||||
group,
|
||||
key,
|
||||
value: convert(clone)
|
||||
}]
|
||||
}
|
||||
})
|
||||
.then(() => rootState.api.backendInteractor.fetchInstanceDBConfig())
|
||||
.then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig }))
|
||||
},
|
||||
resetAdminSetting ({ rootState, state, commit, dispatch }, { path }) {
|
||||
const [group, key, subkey] = path.split(/\./g)
|
||||
|
||||
state.modifiedPaths.delete(path)
|
||||
|
||||
return rootState.api.backendInteractor.pushInstanceDBConfig({
|
||||
payload: {
|
||||
configs: [{
|
||||
group,
|
||||
key,
|
||||
delete: true,
|
||||
subkeys: [subkey]
|
||||
}]
|
||||
}
|
||||
})
|
||||
.then(() => rootState.api.backendInteractor.fetchInstanceDBConfig())
|
||||
.then(backendDbConfig => dispatch('setInstanceAdminSettings', { backendDbConfig }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default adminSettingsStorage
|
||||
|
|
@ -89,6 +89,9 @@ const api = {
|
|||
const { state, commit, dispatch, rootState } = store
|
||||
const timelineData = rootState.statuses.timelines.friends
|
||||
state.mastoUserSocket = state.backendInteractor.startUserSocket({ store })
|
||||
state.mastoUserSocket.addEventListener('pleroma:authenticated', () => {
|
||||
state.mastoUserSocket.subscribe('user')
|
||||
})
|
||||
state.mastoUserSocket.addEventListener(
|
||||
'message',
|
||||
({ detail: message }) => {
|
||||
|
|
@ -204,12 +207,14 @@ const api = {
|
|||
timeline = 'friends',
|
||||
tag = false,
|
||||
userId = false,
|
||||
listId = false
|
||||
listId = false,
|
||||
statusId = false,
|
||||
bookmarkFolderId = false
|
||||
}) {
|
||||
if (store.state.fetchers[timeline]) return
|
||||
|
||||
const fetcher = store.state.backendInteractor.startFetchingTimeline({
|
||||
timeline, store, userId, listId, tag
|
||||
timeline, store, userId, listId, statusId, bookmarkFolderId, tag
|
||||
})
|
||||
store.commit('addFetcher', { fetcherName: timeline, fetcher })
|
||||
},
|
||||
|
|
@ -273,6 +278,18 @@ const api = {
|
|||
store.commit('removeFetcher', { fetcherName: 'lists', fetcher })
|
||||
},
|
||||
|
||||
// Bookmark folders
|
||||
startFetchingBookmarkFolders (store) {
|
||||
if (store.state.fetchers.bookmarkFolders) return
|
||||
const fetcher = store.state.backendInteractor.startFetchingBookmarkFolders({ store })
|
||||
store.commit('addFetcher', { fetcherName: 'bookmarkFolders', fetcher })
|
||||
},
|
||||
stopFetchingBookmarkFolders (store) {
|
||||
const fetcher = store.state.fetchers.bookmarkFolders
|
||||
if (!fetcher) return
|
||||
store.commit('removeFetcher', { fetcherName: 'bookmarkFolders', fetcher })
|
||||
},
|
||||
|
||||
// Pleroma websocket
|
||||
setWsToken (store, token) {
|
||||
store.commit('setWsToken', token)
|
||||
|
|
|
|||
66
src/modules/bookmark_folders.js
Normal file
66
src/modules/bookmark_folders.js
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import { remove, find } from 'lodash'
|
||||
|
||||
export const defaultState = {
|
||||
allFolders: []
|
||||
}
|
||||
|
||||
export const mutations = {
|
||||
setBookmarkFolders (state, value) {
|
||||
state.allFolders = value
|
||||
},
|
||||
setBookmarkFolder (state, { id, name, emoji, emoji_url: emojiUrl }) {
|
||||
const entry = find(state.allFolders, { id })
|
||||
if (!entry) {
|
||||
state.allFolders.push({ id, name, emoji, emoji_url: emojiUrl })
|
||||
} else {
|
||||
entry.name = name
|
||||
entry.emoji = emoji
|
||||
entry.emoji_url = emojiUrl
|
||||
}
|
||||
},
|
||||
deleteBookmarkFolder (state, { folderId }) {
|
||||
remove(state.allFolders, folder => folder.id === folderId)
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
setBookmarkFolders ({ commit }, value) {
|
||||
commit('setBookmarkFolders', value)
|
||||
},
|
||||
createBookmarkFolder ({ rootState, commit }, { name, emoji }) {
|
||||
return rootState.api.backendInteractor.createBookmarkFolder({ name, emoji })
|
||||
.then((folder) => {
|
||||
commit('setBookmarkFolder', folder)
|
||||
return folder
|
||||
})
|
||||
},
|
||||
setBookmarkFolder ({ rootState, commit }, { folderId, name, emoji }) {
|
||||
return rootState.api.backendInteractor.updateBookmarkFolder({ folderId, name, emoji })
|
||||
.then((folder) => {
|
||||
commit('setBookmarkFolder', folder)
|
||||
return folder
|
||||
})
|
||||
},
|
||||
deleteBookmarkFolder ({ rootState, commit }, { folderId }) {
|
||||
rootState.api.backendInteractor.deleteBookmarkFolder({ folderId })
|
||||
commit('deleteBookmarkFolder', { folderId })
|
||||
}
|
||||
}
|
||||
|
||||
export const getters = {
|
||||
findBookmarkFolderName: state => id => {
|
||||
const folder = state.allFolders.find(folder => folder.id === id)
|
||||
|
||||
if (!folder) return
|
||||
return folder.name
|
||||
}
|
||||
}
|
||||
|
||||
const bookmarkFolders = {
|
||||
state: defaultState,
|
||||
mutations,
|
||||
actions,
|
||||
getters
|
||||
}
|
||||
|
||||
export default bookmarkFolders
|
||||
|
|
@ -65,6 +65,7 @@ const chats = {
|
|||
const newChatMessageSideEffects = (chat) => {
|
||||
maybeShowChatNotification(store, chat)
|
||||
}
|
||||
commit('addNewUsers', chats.map(k => k.account).filter(k => k))
|
||||
commit('addNewChats', { dispatch, chats, rootGetters, newChatMessageSideEffects })
|
||||
},
|
||||
updateChat ({ commit }, { chat }) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,23 @@
|
|||
import Cookies from 'js-cookie'
|
||||
import { setPreset, applyTheme, applyConfig } from '../services/style_setter/style_setter.js'
|
||||
import { applyConfig } from '../services/style_setter/style_setter.js'
|
||||
import messages from '../i18n/messages'
|
||||
import { set } from 'lodash'
|
||||
import localeService from '../services/locale/locale.service.js'
|
||||
import { useI18nStore } from '../stores/i18n.js'
|
||||
import { useInterfaceStore } from '../stores/interface.js'
|
||||
|
||||
const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage'
|
||||
const APPEARANCE_SETTINGS_KEYS = new Set([
|
||||
'sidebarColumnWidth',
|
||||
'contentColumnWidth',
|
||||
'notifsColumnWidth',
|
||||
'textSize',
|
||||
'navbarSize',
|
||||
'panelHeaderSize',
|
||||
'forcedRoundness',
|
||||
'emojiSize',
|
||||
'emojiReactionsScale'
|
||||
])
|
||||
|
||||
const browserLocale = (window.navigator.language || 'en').split('-')[0]
|
||||
|
||||
|
|
@ -20,15 +32,40 @@ export const multiChoiceProperties = [
|
|||
'conversationDisplay', // tree | linear
|
||||
'conversationOtherRepliesButton', // below | inside
|
||||
'mentionLinkDisplay', // short | full_for_remote | full
|
||||
'userPopoverAvatarAction' // close | zoom | open
|
||||
'userPopoverAvatarAction', // close | zoom | open
|
||||
'unsavedPostAction' // save | discard | confirm
|
||||
]
|
||||
|
||||
export const defaultState = {
|
||||
expertLevel: 0, // used to track which settings to show and hide
|
||||
colors: {},
|
||||
theme: undefined,
|
||||
customTheme: undefined,
|
||||
customThemeSource: undefined,
|
||||
|
||||
// Theme stuff
|
||||
theme: undefined, // Very old theme store, stores preset name, still in use
|
||||
|
||||
// V1
|
||||
colors: {}, // VERY old theme store, just colors of V1, probably not even used anymore
|
||||
|
||||
// V2
|
||||
customTheme: undefined, // "snapshot", previously was used as actual theme store for V2 so it's still used in case of PleromaFE downgrade event.
|
||||
customThemeSource: undefined, // "source", stores original theme data
|
||||
|
||||
// V3
|
||||
style: null,
|
||||
styleCustomData: null,
|
||||
palette: null,
|
||||
paletteCustomData: null,
|
||||
themeDebug: false, // debug mode that uses computed backgrounds instead of real ones to debug contrast functions
|
||||
forceThemeRecompilation: false, // flag that forces recompilation on boot even if cache exists
|
||||
theme3hacks: { // Hacks, user overrides that are independent of theme used
|
||||
underlay: 'none',
|
||||
fonts: {
|
||||
interface: undefined,
|
||||
input: undefined,
|
||||
post: undefined,
|
||||
monospace: undefined
|
||||
}
|
||||
},
|
||||
|
||||
hideISP: false,
|
||||
hideInstanceWallpaper: false,
|
||||
hideShoutbox: false,
|
||||
|
|
@ -37,10 +74,13 @@ export const defaultState = {
|
|||
hideMutedThreads: undefined, // instance default
|
||||
hideWordFilteredPosts: undefined, // instance default
|
||||
muteBotStatuses: undefined, // instance default
|
||||
muteSensitiveStatuses: undefined, // instance default
|
||||
collapseMessageWithSubject: undefined, // instance default
|
||||
padEmoji: true,
|
||||
hideAttachments: false,
|
||||
hideAttachmentsInConv: false,
|
||||
hideScrobbles: false,
|
||||
hideScrobblesAfter: '2d',
|
||||
maxThumbnails: 16,
|
||||
hideNsfw: true,
|
||||
preloadImage: true,
|
||||
|
|
@ -57,6 +97,7 @@ export const defaultState = {
|
|||
notificationVisibility: {
|
||||
follows: true,
|
||||
mentions: true,
|
||||
statuses: true,
|
||||
likes: true,
|
||||
repeats: true,
|
||||
moves: true,
|
||||
|
|
@ -66,7 +107,21 @@ export const defaultState = {
|
|||
chatMention: true,
|
||||
polls: true
|
||||
},
|
||||
notificationNative: {
|
||||
follows: true,
|
||||
mentions: true,
|
||||
statuses: true,
|
||||
likes: false,
|
||||
repeats: false,
|
||||
moves: false,
|
||||
emojiReactions: false,
|
||||
followRequest: true,
|
||||
reports: true,
|
||||
chatMention: true,
|
||||
polls: true
|
||||
},
|
||||
webPushNotifications: false,
|
||||
webPushAlwaysShowNotifications: false,
|
||||
muteWords: [],
|
||||
highlight: {},
|
||||
interfaceLanguage: browserLocale,
|
||||
|
|
@ -84,11 +139,14 @@ export const defaultState = {
|
|||
modalOnUnfollow: undefined, // instance default
|
||||
modalOnBlock: undefined, // instance default
|
||||
modalOnMute: undefined, // instance default
|
||||
modalOnMuteConversation: undefined, // instance default
|
||||
modalOnMuteDomain: undefined, // instance default
|
||||
modalOnDelete: undefined, // instance default
|
||||
modalOnLogout: undefined, // instance default
|
||||
modalOnApproveFollow: undefined, // instance default
|
||||
modalOnDenyFollow: undefined, // instance default
|
||||
modalOnRemoveUserFromFollowers: undefined, // instance default
|
||||
modalMobileCenter: undefined,
|
||||
playVideosInModal: false,
|
||||
useOneClickNsfw: false,
|
||||
useContainFit: true,
|
||||
|
|
@ -99,7 +157,12 @@ export const defaultState = {
|
|||
sidebarColumnWidth: '25rem',
|
||||
contentColumnWidth: '45rem',
|
||||
notifsColumnWidth: '25rem',
|
||||
emojiReactionsScale: 1.0,
|
||||
emojiReactionsScale: undefined,
|
||||
textSize: undefined, // instance default
|
||||
emojiSize: undefined, // instance default
|
||||
navbarSize: undefined, // instance default
|
||||
panelHeaderSize: undefined, // instance default
|
||||
forcedRoundness: undefined, // instance default
|
||||
navbarColumnStretch: false,
|
||||
greentext: undefined, // instance default
|
||||
useAtIcon: undefined, // instance default
|
||||
|
|
@ -118,8 +181,22 @@ export const defaultState = {
|
|||
conversationTreeAdvanced: undefined, // instance default
|
||||
conversationOtherRepliesButton: undefined, // instance default
|
||||
conversationTreeFadeAncestors: undefined, // instance default
|
||||
showExtraNotifications: undefined, // instance default
|
||||
showExtraNotificationsTip: undefined, // instance default
|
||||
showChatsInExtraNotifications: undefined, // instance default
|
||||
showAnnouncementsInExtraNotifications: undefined, // instance default
|
||||
showFollowRequestsInExtraNotifications: undefined, // instance default
|
||||
maxDepthInThread: undefined, // instance default
|
||||
autocompleteSelect: undefined // instance default
|
||||
autocompleteSelect: undefined, // instance default
|
||||
closingDrawerMarksAsSeen: undefined, // instance default
|
||||
unseenAtTop: undefined, // instance default
|
||||
ignoreInactionableSeen: undefined, // instance default
|
||||
unsavedPostAction: undefined, // instance default
|
||||
autoSaveDraft: undefined, // instance default
|
||||
useAbsoluteTimeFormat: undefined, // instance default
|
||||
absoluteTimeFormatMinAge: undefined, // instance default
|
||||
absoluteTime12h: undefined, // instance default
|
||||
imageCompression: true
|
||||
}
|
||||
|
||||
// caching the instance default properties
|
||||
|
|
@ -149,8 +226,12 @@ const config = {
|
|||
}
|
||||
},
|
||||
mutations: {
|
||||
setOptionTemporarily (state, { name, value }) {
|
||||
set(state, name, value)
|
||||
applyConfig(state)
|
||||
},
|
||||
setOption (state, { name, value }) {
|
||||
state[name] = value
|
||||
set(state, name, value)
|
||||
},
|
||||
setHighlight (state, { user, color, type }) {
|
||||
const data = this.state.config.highlight[user]
|
||||
|
|
@ -179,33 +260,86 @@ const config = {
|
|||
setHighlight ({ commit, dispatch }, { user, color, type }) {
|
||||
commit('setHighlight', { user, color, type })
|
||||
},
|
||||
setOptionTemporarily ({ commit, dispatch, state, rootState }, { name, value }) {
|
||||
if (rootState.interface.temporaryChangesTimeoutId !== null) {
|
||||
console.warn('Can\'t track more than one temporary change')
|
||||
return
|
||||
}
|
||||
const oldValue = state[name]
|
||||
|
||||
commit('setOptionTemporarily', { name, value })
|
||||
|
||||
const confirm = () => {
|
||||
dispatch('setOption', { name, value })
|
||||
commit('clearTemporaryChanges')
|
||||
}
|
||||
|
||||
const revert = () => {
|
||||
commit('setOptionTemporarily', { name, value: oldValue })
|
||||
commit('clearTemporaryChanges')
|
||||
}
|
||||
|
||||
commit('setTemporaryChanges', {
|
||||
timeoutId: setTimeout(revert, 10000),
|
||||
confirm,
|
||||
revert
|
||||
})
|
||||
},
|
||||
setThemeV2 ({ commit, dispatch }, { customTheme, customThemeSource }) {
|
||||
commit('setOption', { name: 'theme', value: 'custom' })
|
||||
commit('setOption', { name: 'customTheme', value: customTheme })
|
||||
commit('setOption', { name: 'customThemeSource', value: customThemeSource })
|
||||
dispatch('setTheme', { themeData: customThemeSource, recompile: true })
|
||||
},
|
||||
setOption ({ commit, dispatch, state }, { name, value }) {
|
||||
commit('setOption', { name, value })
|
||||
switch (name) {
|
||||
case 'theme':
|
||||
setPreset(value)
|
||||
break
|
||||
case 'sidebarColumnWidth':
|
||||
case 'contentColumnWidth':
|
||||
case 'notifsColumnWidth':
|
||||
case 'emojiReactionsScale':
|
||||
const exceptions = new Set([
|
||||
'useStreamingApi'
|
||||
])
|
||||
|
||||
if (exceptions.has(name)) {
|
||||
switch (name) {
|
||||
case 'useStreamingApi': {
|
||||
const action = value ? 'enableMastoSockets' : 'disableMastoSockets'
|
||||
|
||||
dispatch(action).then(() => {
|
||||
commit('setOption', { name: 'useStreamingApi', value })
|
||||
}).catch((e) => {
|
||||
console.error('Failed starting MastoAPI Streaming socket', e)
|
||||
dispatch('disableMastoSockets')
|
||||
dispatch('setOption', { name: 'useStreamingApi', value: false })
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
commit('setOption', { name, value })
|
||||
if (APPEARANCE_SETTINGS_KEYS.has(name)) {
|
||||
applyConfig(state)
|
||||
break
|
||||
case 'customTheme':
|
||||
case 'customThemeSource':
|
||||
applyTheme(value)
|
||||
break
|
||||
case 'interfaceLanguage':
|
||||
messages.setLanguage(useI18nStore().i18n, value)
|
||||
dispatch('loadUnicodeEmojiData', value)
|
||||
Cookies.set(
|
||||
BACKEND_LANGUAGE_COOKIE_NAME,
|
||||
localeService.internalToBackendLocaleMulti(value)
|
||||
)
|
||||
break
|
||||
case 'thirdColumnMode':
|
||||
useInterfaceStore().setLayoutWidth(undefined)
|
||||
break
|
||||
}
|
||||
if (name.startsWith('theme3hacks')) {
|
||||
dispatch('applyTheme', { recompile: true })
|
||||
}
|
||||
switch (name) {
|
||||
case 'theme':
|
||||
if (value === 'custom') break
|
||||
dispatch('setTheme', { themeName: value, recompile: true, saveData: true })
|
||||
break
|
||||
case 'themeDebug': {
|
||||
dispatch('setTheme', { recompile: true })
|
||||
break
|
||||
}
|
||||
case 'interfaceLanguage':
|
||||
messages.setLanguage(useI18nStore().i18n, value)
|
||||
dispatch('loadUnicodeEmojiData', value)
|
||||
Cookies.set(
|
||||
BACKEND_LANGUAGE_COOKIE_NAME,
|
||||
localeService.internalToBackendLocaleMulti(value)
|
||||
)
|
||||
break
|
||||
case 'thirdColumnMode':
|
||||
useInterfaceStore().setLayoutWidth(undefined)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
86
src/modules/drafts.js
Normal file
86
src/modules/drafts.js
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import { storage } from 'src/lib/storage.js'
|
||||
|
||||
export const defaultState = {
|
||||
drafts: {}
|
||||
}
|
||||
|
||||
export const mutations = {
|
||||
addOrSaveDraft (state, { draft }) {
|
||||
state.drafts[draft.id] = draft
|
||||
},
|
||||
abandonDraft (state, { id }) {
|
||||
delete state.drafts[id]
|
||||
},
|
||||
loadDrafts (state, data) {
|
||||
state.drafts = data
|
||||
}
|
||||
}
|
||||
|
||||
const storageKey = 'pleroma-fe-drafts'
|
||||
|
||||
/*
|
||||
* Note: we do not use the persist state plugin because
|
||||
* it is not impossible for a user to have two windows at
|
||||
* the same time. The persist state plugin is just overriding
|
||||
* everything with the current state. This isn't good because
|
||||
* if a draft is created in one window and another draft is
|
||||
* created in another, the draft in the first window will just
|
||||
* be overriden.
|
||||
* Here, we can't guarantee 100% atomicity unless one uses
|
||||
* different keys, which will just pollute the whole storage.
|
||||
* It is indeed best to have backend support for this.
|
||||
*/
|
||||
const getStorageData = async () => ((await storage.getItem(storageKey)) || {})
|
||||
|
||||
const saveDraftToStorage = async (draft) => {
|
||||
const currentData = await getStorageData()
|
||||
currentData[draft.id] = JSON.parse(JSON.stringify(draft))
|
||||
await storage.setItem(storageKey, currentData)
|
||||
}
|
||||
|
||||
const deleteDraftFromStorage = async (id) => {
|
||||
const currentData = await getStorageData()
|
||||
delete currentData[id]
|
||||
await storage.setItem(storageKey, currentData)
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
async addOrSaveDraft (store, { draft }) {
|
||||
const id = draft.id || (new Date().getTime()).toString()
|
||||
const draftWithId = { ...draft, id }
|
||||
store.commit('addOrSaveDraft', { draft: draftWithId })
|
||||
await saveDraftToStorage(draftWithId)
|
||||
return id
|
||||
},
|
||||
async abandonDraft (store, { id }) {
|
||||
store.commit('abandonDraft', { id })
|
||||
await deleteDraftFromStorage(id)
|
||||
},
|
||||
async loadDrafts (store) {
|
||||
const currentData = await getStorageData()
|
||||
store.commit('loadDrafts', currentData)
|
||||
}
|
||||
}
|
||||
|
||||
export const getters = {
|
||||
draftsByTypeAndRefId (state) {
|
||||
return (type, refId) => {
|
||||
return Object.values(state.drafts).filter(draft => draft.type === type && draft.refId === refId)
|
||||
}
|
||||
},
|
||||
draftsArray (state) {
|
||||
return Object.values(state.drafts)
|
||||
},
|
||||
draftCount (state) {
|
||||
return Object.values(state.drafts).length
|
||||
}
|
||||
}
|
||||
|
||||
const drafts = {
|
||||
state: defaultState,
|
||||
mutations,
|
||||
getters,
|
||||
actions
|
||||
}
|
||||
|
||||
export default drafts
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
import { getPreset, applyTheme } from '../services/style_setter/style_setter.js'
|
||||
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
|
||||
import apiService from '../services/api/api.service.js'
|
||||
import { instanceDefaultProperties } from './config.js'
|
||||
import { langCodeToCldrName, ensureFinalFallback } from '../i18n/languages.js'
|
||||
|
|
@ -45,7 +43,10 @@ const defaultState = {
|
|||
registrationOpen: true,
|
||||
server: 'http://localhost:4040/',
|
||||
textlimit: 5000,
|
||||
themeData: undefined,
|
||||
themesIndex: undefined,
|
||||
stylesIndex: undefined,
|
||||
palettesIndex: undefined,
|
||||
themeData: undefined, // used for theme editor v2
|
||||
vapidPublicKey: undefined,
|
||||
|
||||
// Stuff from static/config.json
|
||||
|
|
@ -72,15 +73,19 @@ const defaultState = {
|
|||
hideSitename: false,
|
||||
hideUserStats: false,
|
||||
muteBotStatuses: false,
|
||||
muteSensitiveStatuses: false,
|
||||
modalOnRepeat: false,
|
||||
modalOnUnfollow: false,
|
||||
modalOnBlock: true,
|
||||
modalOnMute: false,
|
||||
modalOnMuteConversation: false,
|
||||
modalOnMuteDomain: true,
|
||||
modalOnDelete: true,
|
||||
modalOnLogout: true,
|
||||
modalOnApproveFollow: false,
|
||||
modalOnDenyFollow: false,
|
||||
modalOnRemoveUserFromFollowers: false,
|
||||
modalMobileCenter: false,
|
||||
loginMethod: 'password',
|
||||
logo: '/static/logo.svg',
|
||||
logoMargin: '.2em',
|
||||
|
|
@ -98,14 +103,36 @@ const defaultState = {
|
|||
sidebarRight: false,
|
||||
subjectLineBehavior: 'email',
|
||||
theme: 'pleroma-dark',
|
||||
palette: null,
|
||||
style: null,
|
||||
emojiReactionsScale: 0.5,
|
||||
textSize: '14px',
|
||||
emojiSize: '2.2rem',
|
||||
navbarSize: '3.5rem',
|
||||
panelHeaderSize: '3.2rem',
|
||||
forcedRoundness: -1,
|
||||
fontsOverride: {},
|
||||
virtualScrolling: true,
|
||||
sensitiveByDefault: false,
|
||||
conversationDisplay: 'linear',
|
||||
conversationTreeAdvanced: false,
|
||||
conversationOtherRepliesButton: 'below',
|
||||
conversationTreeFadeAncestors: false,
|
||||
showExtraNotifications: true,
|
||||
showExtraNotificationsTip: true,
|
||||
showChatsInExtraNotifications: true,
|
||||
showAnnouncementsInExtraNotifications: true,
|
||||
showFollowRequestsInExtraNotifications: true,
|
||||
maxDepthInThread: 6,
|
||||
autocompleteSelect: false,
|
||||
closingDrawerMarksAsSeen: true,
|
||||
unseenAtTop: false,
|
||||
ignoreInactionableSeen: false,
|
||||
unsavedPostAction: 'confirm',
|
||||
autoSaveDraft: false,
|
||||
useAbsoluteTimeFormat: false,
|
||||
absoluteTimeFormatMinAge: '0d',
|
||||
absoluteTime12h: '24h',
|
||||
|
||||
// Nasty stuff
|
||||
customEmoji: [],
|
||||
|
|
@ -125,10 +152,13 @@ const defaultState = {
|
|||
shoutAvailable: false,
|
||||
pleromaChatMessagesAvailable: false,
|
||||
pleromaCustomEmojiReactionsAvailable: false,
|
||||
pleromaBookmarkFoldersAvailable: false,
|
||||
gopherAvailable: false,
|
||||
mediaProxyAvailable: false,
|
||||
suggestionsEnabled: false,
|
||||
suggestionsWeb: '',
|
||||
quotingAvailable: false,
|
||||
groupActorAvailable: false,
|
||||
|
||||
// Html stuff
|
||||
instanceSpecificPanelContent: '',
|
||||
|
|
@ -136,6 +166,7 @@ const defaultState = {
|
|||
|
||||
// Version Information
|
||||
backendVersion: '',
|
||||
backendRepository: '',
|
||||
frontendVersion: '',
|
||||
|
||||
pollsAvailable: false,
|
||||
|
|
@ -269,9 +300,6 @@ const instance = {
|
|||
dispatch('initializeSocket')
|
||||
}
|
||||
break
|
||||
case 'theme':
|
||||
dispatch('setTheme', value)
|
||||
break
|
||||
}
|
||||
},
|
||||
async getStaticEmoji ({ commit }) {
|
||||
|
|
@ -288,8 +316,7 @@ const instance = {
|
|||
}, {})
|
||||
commit('setInstanceOption', { name: 'emoji', value: injectRegionalIndicators(emoji) })
|
||||
} catch (e) {
|
||||
console.warn("Can't load static emoji")
|
||||
console.warn(e)
|
||||
console.warn("Can't load static emoji\n", e)
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -356,29 +383,9 @@ const instance = {
|
|||
throw (res)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Can't load custom emojis")
|
||||
console.warn(e)
|
||||
console.warn("Can't load custom emojis\n", e)
|
||||
}
|
||||
},
|
||||
|
||||
setTheme ({ commit, rootState }, themeName) {
|
||||
commit('setInstanceOption', { name: 'theme', value: themeName })
|
||||
getPreset(themeName)
|
||||
.then(themeData => {
|
||||
commit('setInstanceOption', { name: 'themeData', value: themeData })
|
||||
// No need to apply theme if there's user theme already
|
||||
const { customTheme } = rootState.config
|
||||
if (customTheme) return
|
||||
|
||||
// New theme presets don't have 'theme' property, they use 'source'
|
||||
const themeSource = themeData.source
|
||||
if (!themeData.theme || (themeSource && themeSource.themeEngineVersion === CURRENT_VERSION)) {
|
||||
applyTheme(themeSource)
|
||||
} else {
|
||||
applyTheme(themeData.theme)
|
||||
}
|
||||
})
|
||||
},
|
||||
fetchEmoji ({ dispatch, state }) {
|
||||
if (!state.customEmojiFetched) {
|
||||
state.customEmojiFetched = true
|
||||
|
|
@ -397,8 +404,7 @@ const instance = {
|
|||
})
|
||||
commit('setKnownDomains', result)
|
||||
} catch (e) {
|
||||
console.warn("Can't load known domains")
|
||||
console.warn(e)
|
||||
console.warn("Can't load known domains\n", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
169
src/modules/notifications.js
Normal file
169
src/modules/notifications.js
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
import apiService from '../services/api/api.service.js'
|
||||
|
||||
import {
|
||||
isStatusNotification,
|
||||
isValidNotification,
|
||||
maybeShowNotification
|
||||
} from '../services/notification_utils/notification_utils.js'
|
||||
|
||||
import {
|
||||
closeDesktopNotification,
|
||||
closeAllDesktopNotifications
|
||||
} from '../services/desktop_notification_utils/desktop_notification_utils.js'
|
||||
|
||||
const emptyNotifications = () => ({
|
||||
desktopNotificationSilence: true,
|
||||
maxId: 0,
|
||||
minId: Number.POSITIVE_INFINITY,
|
||||
data: [],
|
||||
idStore: {},
|
||||
loading: false
|
||||
})
|
||||
|
||||
export const defaultState = () => ({
|
||||
...emptyNotifications()
|
||||
})
|
||||
|
||||
export const notifications = {
|
||||
state: defaultState(),
|
||||
mutations: {
|
||||
addNewNotifications (state, { notifications }) {
|
||||
notifications.forEach(notification => {
|
||||
state.data.push(notification)
|
||||
state.idStore[notification.id] = notification
|
||||
})
|
||||
},
|
||||
clearNotifications (state) {
|
||||
state = emptyNotifications()
|
||||
},
|
||||
updateNotificationsMinMaxId (state, id) {
|
||||
state.maxId = id > state.maxId ? id : state.maxId
|
||||
state.minId = id < state.minId ? id : state.minId
|
||||
},
|
||||
setNotificationsLoading (state, { value }) {
|
||||
state.loading = value
|
||||
},
|
||||
setNotificationsSilence (state, { value }) {
|
||||
state.desktopNotificationSilence = value
|
||||
},
|
||||
markNotificationsAsSeen (state) {
|
||||
state.data.forEach((notification) => {
|
||||
notification.seen = true
|
||||
})
|
||||
},
|
||||
markSingleNotificationAsSeen (state, { id }) {
|
||||
const notification = state.idStore[id]
|
||||
if (notification) notification.seen = true
|
||||
},
|
||||
dismissNotification (state, { id }) {
|
||||
state.data = state.data.filter(n => n.id !== id)
|
||||
delete state.idStore[id]
|
||||
},
|
||||
updateNotification (state, { id, updater }) {
|
||||
const notification = state.idStore[id]
|
||||
notification && updater(notification)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
addNewNotifications (store, { notifications, older }) {
|
||||
const { commit, dispatch, state, rootState } = store
|
||||
const validNotifications = notifications.filter((notification) => {
|
||||
// If invalid notification, update ids but don't add it to store
|
||||
if (!isValidNotification(notification)) {
|
||||
console.error('Invalid notification:', notification)
|
||||
commit('updateNotificationsMinMaxId', notification.id)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
const statusNotifications = validNotifications.filter(notification => isStatusNotification(notification.type) && notification.status)
|
||||
|
||||
// Synchronous commit to add all the statuses
|
||||
commit('addNewStatuses', { statuses: statusNotifications.map(notification => notification.status) })
|
||||
|
||||
// Update references to statuses in notifications to ones in the store
|
||||
statusNotifications.forEach(notification => {
|
||||
const id = notification.status.id
|
||||
const referenceStatus = rootState.statuses.allStatusesObject[id]
|
||||
|
||||
if (referenceStatus) {
|
||||
notification.status = referenceStatus
|
||||
}
|
||||
})
|
||||
|
||||
validNotifications.forEach(notification => {
|
||||
if (notification.type === 'pleroma:report') {
|
||||
dispatch('addReport', notification.report)
|
||||
}
|
||||
|
||||
if (notification.type === 'pleroma:emoji_reaction') {
|
||||
dispatch('fetchEmojiReactionsBy', notification.status.id)
|
||||
}
|
||||
|
||||
// Only add a new notification if we don't have one for the same action
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (!state.idStore.hasOwnProperty(notification.id)) {
|
||||
commit('updateNotificationsMinMaxId', notification.id)
|
||||
commit('addNewNotifications', { notifications: [notification] })
|
||||
|
||||
maybeShowNotification(store, notification)
|
||||
} else if (notification.seen) {
|
||||
state.idStore[notification.id].seen = true
|
||||
}
|
||||
})
|
||||
},
|
||||
notificationClicked ({ state, dispatch }, { id }) {
|
||||
const notification = state.idStore[id]
|
||||
const { type, seen } = notification
|
||||
|
||||
if (!seen) {
|
||||
switch (type) {
|
||||
case 'mention':
|
||||
case 'pleroma:report':
|
||||
case 'follow_request':
|
||||
break
|
||||
default:
|
||||
dispatch('markSingleNotificationAsSeen', { id })
|
||||
}
|
||||
}
|
||||
},
|
||||
setNotificationsLoading ({ rootState, commit }, { value }) {
|
||||
commit('setNotificationsLoading', { value })
|
||||
},
|
||||
setNotificationsSilence ({ rootState, commit }, { value }) {
|
||||
commit('setNotificationsSilence', { value })
|
||||
},
|
||||
markNotificationsAsSeen ({ rootState, state, commit }) {
|
||||
commit('markNotificationsAsSeen')
|
||||
apiService.markNotificationsAsSeen({
|
||||
id: state.maxId,
|
||||
credentials: rootState.users.currentUser.credentials
|
||||
}).then(() => {
|
||||
closeAllDesktopNotifications(rootState)
|
||||
})
|
||||
},
|
||||
markSingleNotificationAsSeen ({ rootState, commit }, { id }) {
|
||||
commit('markSingleNotificationAsSeen', { id })
|
||||
apiService.markNotificationsAsSeen({
|
||||
single: true,
|
||||
id,
|
||||
credentials: rootState.users.currentUser.credentials
|
||||
}).then(() => {
|
||||
closeDesktopNotification(rootState, { id })
|
||||
})
|
||||
},
|
||||
dismissNotificationLocal ({ rootState, commit }, { id }) {
|
||||
commit('dismissNotification', { id })
|
||||
},
|
||||
dismissNotification ({ rootState, commit }, { id }) {
|
||||
commit('dismissNotification', { id })
|
||||
rootState.api.backendInteractor.dismissNotification({ id })
|
||||
},
|
||||
updateNotification ({ rootState, commit }, { id, updater }) {
|
||||
commit('updateNotification', { id, updater })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default notifications
|
||||
|
|
@ -22,9 +22,9 @@ const notificationsApi = ({ rootState, commit }, { path, value, oldValue }) => {
|
|||
.updateNotificationSettings({ settings })
|
||||
.then(result => {
|
||||
if (result.status === 'success') {
|
||||
commit('confirmServerSideOption', { name, value })
|
||||
commit('confirmProfileOption', { name, value })
|
||||
} else {
|
||||
commit('confirmServerSideOption', { name, value: oldValue })
|
||||
commit('confirmProfileOption', { name, value: oldValue })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -94,16 +94,16 @@ export const settingsMap = {
|
|||
|
||||
export const defaultState = Object.fromEntries(Object.keys(settingsMap).map(key => [key, null]))
|
||||
|
||||
const serverSideConfig = {
|
||||
const profileConfig = {
|
||||
state: { ...defaultState },
|
||||
mutations: {
|
||||
confirmServerSideOption (state, { name, value }) {
|
||||
confirmProfileOption (state, { name, value }) {
|
||||
set(state, name, value)
|
||||
},
|
||||
wipeServerSideOption (state, { name }) {
|
||||
wipeProfileOption (state, { name }) {
|
||||
set(state, name, null)
|
||||
},
|
||||
wipeAllServerSideOptions (state) {
|
||||
wipeAllProfileOptions (state) {
|
||||
Object.keys(settingsMap).forEach(key => {
|
||||
set(state, key, null)
|
||||
})
|
||||
|
|
@ -118,23 +118,23 @@ const serverSideConfig = {
|
|||
}
|
||||
},
|
||||
actions: {
|
||||
setServerSideOption ({ rootState, state, commit, dispatch }, { name, value }) {
|
||||
setProfileOption ({ rootState, state, commit, dispatch }, { name, value }) {
|
||||
const oldValue = get(state, name)
|
||||
const map = settingsMap[name]
|
||||
if (!map) throw new Error('Invalid server-side setting')
|
||||
const { set: path = map, api = defaultApi } = map
|
||||
commit('wipeServerSideOption', { name })
|
||||
commit('wipeProfileOption', { name })
|
||||
|
||||
api({ rootState, commit }, { path, value, oldValue })
|
||||
.catch((e) => {
|
||||
console.warn('Error setting server-side option:', e)
|
||||
commit('confirmServerSideOption', { name, value: oldValue })
|
||||
commit('confirmProfileOption', { name, value: oldValue })
|
||||
})
|
||||
},
|
||||
logout ({ commit }) {
|
||||
commit('wipeAllServerSideOptions')
|
||||
commit('wipeAllProfileOptions')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default serverSideConfig
|
||||
export default profileConfig
|
||||
|
|
@ -1,5 +1,17 @@
|
|||
import { toRaw } from 'vue'
|
||||
import { isEqual, cloneDeep, set, get, clamp, flatten, groupBy, findLastIndex, takeRight, uniqWith } from 'lodash'
|
||||
import {
|
||||
isEqual,
|
||||
cloneDeep,
|
||||
set,
|
||||
get,
|
||||
clamp,
|
||||
flatten,
|
||||
groupBy,
|
||||
findLastIndex,
|
||||
takeRight,
|
||||
uniqWith,
|
||||
merge as _merge
|
||||
} from 'lodash'
|
||||
import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js'
|
||||
|
||||
export const VERSION = 1
|
||||
|
|
@ -26,6 +38,7 @@ export const defaultState = {
|
|||
collapseNav: false
|
||||
},
|
||||
collections: {
|
||||
pinnedStatusActions: ['reply', 'retweet', 'favorite', 'emoji'],
|
||||
pinnedNavItems: ['home', 'dms', 'chats']
|
||||
}
|
||||
},
|
||||
|
|
@ -77,7 +90,7 @@ const _verifyPrefs = (state) => {
|
|||
})
|
||||
}
|
||||
|
||||
export const _getRecentData = (cache, live) => {
|
||||
export const _getRecentData = (cache, live, isTest) => {
|
||||
const result = { recent: null, stale: null, needUpload: false }
|
||||
const cacheValid = _checkValidity(cache || {})
|
||||
const liveValid = _checkValidity(live || {})
|
||||
|
|
@ -110,6 +123,17 @@ export const _getRecentData = (cache, live) => {
|
|||
console.debug('Both sources are invalid, start from scratch')
|
||||
result.needUpload = true
|
||||
}
|
||||
|
||||
const merge = (a, b) => ({
|
||||
_version: a._version ?? b._version,
|
||||
_timestamp: a._timestamp ?? b._timestamp,
|
||||
needUpload: b.needUpload ?? a.needUpload,
|
||||
prefsStorage: _merge(a.prefsStorage, b.prefsStorage),
|
||||
flagStorage: _merge(a.flagStorage, b.flagStorage)
|
||||
})
|
||||
result.recent = isTest ? result.recent : (result.recent && merge(defaultState, result.recent))
|
||||
result.stale = isTest ? result.stale : (result.stale && merge(defaultState, result.stale))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
@ -281,18 +305,18 @@ export const mutations = {
|
|||
clearServerSideStorage (state, userData) {
|
||||
state = { ...cloneDeep(defaultState) }
|
||||
},
|
||||
setServerSideStorage (state, userData) {
|
||||
setServerSideStorage (state, userData, test) {
|
||||
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!')
|
||||
console.warn('Cache belongs to another user! reinitializing local cache!')
|
||||
cache = null
|
||||
}
|
||||
|
||||
cache = _doMigrations(cache)
|
||||
|
||||
let { recent, stale, needsUpload } = _getRecentData(cache, live)
|
||||
let { recent, stale, needUpload } = _getRecentData(cache, live)
|
||||
|
||||
const userNew = userData.created_at > NEW_USER_DATE
|
||||
const flagsTemplate = userNew ? newUserFlags : defaultState.flagStorage
|
||||
|
|
@ -306,7 +330,7 @@ export const mutations = {
|
|||
})
|
||||
}
|
||||
|
||||
if (!needsUpload && recent && stale) {
|
||||
if (!needUpload && recent && stale) {
|
||||
console.debug('Checking if data needs merging...')
|
||||
// discarding timestamps and versions
|
||||
const { _timestamp: _0, _version: _1, ...recentData } = recent
|
||||
|
|
@ -335,7 +359,7 @@ export const mutations = {
|
|||
recent.flagStorage = { ...flagsTemplate, ...totalFlags }
|
||||
recent.prefsStorage = { ...defaultState.prefsStorage, ...totalPrefs }
|
||||
|
||||
state.dirty = dirty || needsUpload
|
||||
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) {
|
||||
|
|
@ -419,7 +443,6 @@ const serverSideStorage = {
|
|||
actions: {
|
||||
pushServerSideStorage ({ state, rootState, commit }, { force = false } = {}) {
|
||||
const needPush = state.dirty || force
|
||||
console.log(needPush)
|
||||
if (!needPush) return
|
||||
commit('updateCache', { username: rootState.users.currentUser.fqn })
|
||||
const params = { pleroma_settings_store: { 'pleroma-fe': state.cache } }
|
||||
|
|
|
|||
|
|
@ -12,11 +12,6 @@ import {
|
|||
isArray,
|
||||
omitBy
|
||||
} from 'lodash'
|
||||
import {
|
||||
isStatusNotification,
|
||||
isValidNotification,
|
||||
maybeShowNotification
|
||||
} from '../services/notification_utils/notification_utils.js'
|
||||
import apiService from '../services/api/api.service.js'
|
||||
import { useReportsStore } from '../stores/reports.js'
|
||||
|
||||
|
|
@ -37,21 +32,12 @@ const emptyTl = (userId = 0) => ({
|
|||
flushMarker: 0
|
||||
})
|
||||
|
||||
const emptyNotifications = () => ({
|
||||
desktopNotificationSilence: true,
|
||||
maxId: 0,
|
||||
minId: Number.POSITIVE_INFINITY,
|
||||
data: [],
|
||||
idStore: {},
|
||||
loading: false
|
||||
})
|
||||
|
||||
export const defaultState = () => ({
|
||||
allStatuses: [],
|
||||
scrobblesNextFetch: {},
|
||||
allStatusesObject: {},
|
||||
conversationsObject: {},
|
||||
maxId: 0,
|
||||
notifications: emptyNotifications(),
|
||||
favorites: new Set(),
|
||||
timelines: {
|
||||
mentions: emptyTl(),
|
||||
|
|
@ -121,8 +107,24 @@ const sortTimeline = (timeline) => {
|
|||
return timeline
|
||||
}
|
||||
|
||||
const getLatestScrobble = (state, user) => {
|
||||
if (state.scrobblesNextFetch[user.id] && state.scrobblesNextFetch[user.id] > Date.now()) {
|
||||
return
|
||||
}
|
||||
|
||||
state.scrobblesNextFetch[user.id] = Date.now() + 24 * 60 * 60 * 1000
|
||||
apiService.fetchScrobbles({ accountId: user.id }).then((scrobbles) => {
|
||||
if (scrobbles.length > 0) {
|
||||
user.latestScrobble = scrobbles[0]
|
||||
|
||||
state.scrobblesNextFetch[user.id] = Date.now() + 60 * 1000
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Add status to the global storages (arrays and objects maintaining statuses) except timelines
|
||||
const addStatusToGlobalStorage = (state, data) => {
|
||||
getLatestScrobble(state, data.user)
|
||||
const result = mergeOrAdd(state.allStatuses, state.allStatusesObject, data)
|
||||
if (result.new) {
|
||||
// Add to conversation
|
||||
|
|
@ -138,22 +140,6 @@ const addStatusToGlobalStorage = (state, data) => {
|
|||
return result
|
||||
}
|
||||
|
||||
// Remove status from the global storages (arrays and objects maintaining statuses) except timelines
|
||||
const removeStatusFromGlobalStorage = (state, status) => {
|
||||
remove(state.allStatuses, { id: status.id })
|
||||
|
||||
// TODO: Need to remove from allStatusesObject?
|
||||
|
||||
// Remove possible notification
|
||||
remove(state.notifications.data, ({ action: { id } }) => id === status.id)
|
||||
|
||||
// Remove from conversation
|
||||
const conversationId = status.statusnet_conversation_id
|
||||
if (state.conversationsObject[conversationId]) {
|
||||
remove(state.conversationsObject[conversationId], { id: status.id })
|
||||
}
|
||||
}
|
||||
|
||||
const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {}, noIdUpdate = false, userId, pagination = {} }) => {
|
||||
// Sanity check
|
||||
if (!isArray(statuses)) {
|
||||
|
|
@ -230,6 +216,10 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
|
|||
timelineObject.newStatusCount += 1
|
||||
}
|
||||
|
||||
if (status.quote) {
|
||||
addStatus(status.quote, /* showImmediately = */ false, /* addToTimeline = */ false)
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
|
|
@ -283,26 +273,12 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
|
|||
favoriteStatus(favorite)
|
||||
}
|
||||
},
|
||||
deletion: (deletion) => {
|
||||
const uri = deletion.uri
|
||||
const status = find(allStatuses, { uri })
|
||||
if (!status) {
|
||||
return
|
||||
}
|
||||
|
||||
removeStatusFromGlobalStorage(state, status)
|
||||
|
||||
if (timeline) {
|
||||
remove(timelineObject.statuses, { uri })
|
||||
remove(timelineObject.visibleStatuses, { uri })
|
||||
}
|
||||
},
|
||||
follow: (follow) => {
|
||||
// NOOP, it is known status but we don't do anything about it for now
|
||||
},
|
||||
default: (unknown) => {
|
||||
console.log('unknown status type')
|
||||
console.log(unknown)
|
||||
console.warn('unknown status type')
|
||||
console.warn(unknown)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -318,52 +294,6 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
|
|||
}
|
||||
}
|
||||
|
||||
const updateNotificationsMinMaxId = (state, notification) => {
|
||||
state.notifications.maxId = notification.id > state.notifications.maxId
|
||||
? notification.id
|
||||
: state.notifications.maxId
|
||||
state.notifications.minId = notification.id < state.notifications.minId
|
||||
? notification.id
|
||||
: state.notifications.minId
|
||||
}
|
||||
|
||||
const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes, rootGetters, newNotificationSideEffects }) => {
|
||||
each(notifications, (notification) => {
|
||||
// If invalid notification, update ids but don't add it to store
|
||||
if (!isValidNotification(notification)) {
|
||||
console.error('Invalid notification:', notification)
|
||||
updateNotificationsMinMaxId(state, notification)
|
||||
return
|
||||
}
|
||||
|
||||
if (isStatusNotification(notification.type)) {
|
||||
notification.action = addStatusToGlobalStorage(state, notification.action).item
|
||||
notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item
|
||||
}
|
||||
|
||||
if (notification.type === 'pleroma:report') {
|
||||
useReportsStore().addReport(notification.report)
|
||||
}
|
||||
|
||||
if (notification.type === 'pleroma:emoji_reaction') {
|
||||
dispatch('fetchEmojiReactionsBy', notification.status.id)
|
||||
}
|
||||
|
||||
// Only add a new notification if we don't have one for the same action
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (!state.notifications.idStore.hasOwnProperty(notification.id)) {
|
||||
updateNotificationsMinMaxId(state, notification)
|
||||
|
||||
state.notifications.data.push(notification)
|
||||
state.notifications.idStore[notification.id] = notification
|
||||
|
||||
newNotificationSideEffects(notification)
|
||||
} else if (notification.seen) {
|
||||
state.notifications.idStore[notification.id].seen = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const removeStatus = (state, { timeline, userId }) => {
|
||||
const timelineObject = state.timelines[timeline]
|
||||
if (userId) {
|
||||
|
|
@ -376,7 +306,6 @@ const removeStatus = (state, { timeline, userId }) => {
|
|||
|
||||
export const mutations = {
|
||||
addNewStatuses,
|
||||
addNewNotifications,
|
||||
removeStatus,
|
||||
showNewStatuses (state, { timeline }) {
|
||||
const oldTimeline = (state.timelines[timeline])
|
||||
|
|
@ -398,9 +327,6 @@ export const mutations = {
|
|||
const userId = excludeUserId ? state.timelines[timeline].userId : undefined
|
||||
state.timelines[timeline] = emptyTl(userId)
|
||||
},
|
||||
clearNotifications (state) {
|
||||
state.notifications = emptyNotifications()
|
||||
},
|
||||
setFavorited (state, { status, value }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
|
||||
|
|
@ -460,10 +386,12 @@ export const mutations = {
|
|||
setBookmarked (state, { status, value }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
newStatus.bookmarked = value
|
||||
newStatus.bookmark_folder_id = status.bookmark_folder_id
|
||||
},
|
||||
setBookmarkedConfirm (state, { status }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
newStatus.bookmarked = status.bookmarked
|
||||
if (status.pleroma) newStatus.bookmark_folder_id = status.pleroma.bookmark_folder
|
||||
},
|
||||
setDeleted (state, { status }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
|
|
@ -483,31 +411,6 @@ export const mutations = {
|
|||
const newStatus = state.allStatusesObject[id]
|
||||
newStatus.nsfw = nsfw
|
||||
},
|
||||
setNotificationsLoading (state, { value }) {
|
||||
state.notifications.loading = value
|
||||
},
|
||||
setNotificationsSilence (state, { value }) {
|
||||
state.notifications.desktopNotificationSilence = value
|
||||
},
|
||||
markNotificationsAsSeen (state) {
|
||||
each(state.notifications.data, (notification) => {
|
||||
notification.seen = true
|
||||
})
|
||||
},
|
||||
markSingleNotificationAsSeen (state, { id }) {
|
||||
const notification = find(state.notifications.data, n => n.id === id)
|
||||
if (notification) notification.seen = true
|
||||
},
|
||||
dismissNotification (state, { id }) {
|
||||
state.notifications.data = state.notifications.data.filter(n => n.id !== id)
|
||||
},
|
||||
dismissNotifications (state, { finder }) {
|
||||
state.notifications.data = state.notifications.data.filter(n => finder)
|
||||
},
|
||||
updateNotification (state, { id, updater }) {
|
||||
const notification = find(state.notifications.data, n => n.id === id)
|
||||
notification && updater(notification)
|
||||
},
|
||||
queueFlush (state, { timeline, id }) {
|
||||
state.timelines[timeline].flushMarker = id
|
||||
},
|
||||
|
|
@ -589,23 +492,9 @@ export const mutations = {
|
|||
const statuses = {
|
||||
state: defaultState(),
|
||||
actions: {
|
||||
addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId, pagination }) {
|
||||
addNewStatuses ({ rootState, commit, dispatch, state }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId, pagination }) {
|
||||
commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId, pagination })
|
||||
},
|
||||
addNewNotifications (store, { notifications, older }) {
|
||||
const { commit, dispatch, rootGetters } = store
|
||||
|
||||
const newNotificationSideEffects = (notification) => {
|
||||
maybeShowNotification(store, notification)
|
||||
}
|
||||
commit('addNewNotifications', { dispatch, notifications, older, rootGetters, newNotificationSideEffects })
|
||||
},
|
||||
setNotificationsLoading ({ rootState, commit }, { value }) {
|
||||
commit('setNotificationsLoading', { value })
|
||||
},
|
||||
setNotificationsSilence ({ rootState, commit }, { value }) {
|
||||
commit('setNotificationsSilence', { value })
|
||||
},
|
||||
fetchStatus ({ rootState, dispatch }, id) {
|
||||
return rootState.api.backendInteractor.fetchStatus({ id })
|
||||
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
|
||||
|
|
@ -616,9 +505,19 @@ const statuses = {
|
|||
fetchStatusHistory ({ rootState, dispatch }, status) {
|
||||
return apiService.fetchStatusHistory({ status })
|
||||
},
|
||||
deleteStatus ({ rootState, commit }, status) {
|
||||
commit('setDeleted', { status })
|
||||
deleteStatus ({ rootState, commit, dispatch }, status) {
|
||||
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
|
||||
.then((_) => {
|
||||
commit('setDeleted', { status })
|
||||
})
|
||||
.catch((e) => {
|
||||
dispatch('pushGlobalNotice', {
|
||||
level: 'error',
|
||||
messageKey: 'status.delete_error',
|
||||
messageArgs: [e.message],
|
||||
timeout: 5000
|
||||
})
|
||||
})
|
||||
},
|
||||
deleteStatusById ({ rootState, commit }, id) {
|
||||
const status = rootState.statuses.allStatusesObject[id]
|
||||
|
|
@ -651,11 +550,11 @@ const statuses = {
|
|||
rootState.api.backendInteractor.unpinOwnStatus({ id: statusId })
|
||||
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
|
||||
},
|
||||
muteConversation ({ rootState, commit }, statusId) {
|
||||
muteConversation ({ rootState, commit }, { id: statusId }) {
|
||||
return rootState.api.backendInteractor.muteConversation({ id: statusId })
|
||||
.then((status) => commit('setMutedStatus', status))
|
||||
},
|
||||
unmuteConversation ({ rootState, commit }, statusId) {
|
||||
unmuteConversation ({ rootState, commit }, { id: statusId }) {
|
||||
return rootState.api.backendInteractor.unmuteConversation({ id: statusId })
|
||||
.then((status) => commit('setMutedStatus', status))
|
||||
},
|
||||
|
|
@ -673,7 +572,7 @@ const statuses = {
|
|||
},
|
||||
bookmark ({ rootState, commit }, status) {
|
||||
commit('setBookmarked', { status, value: true })
|
||||
rootState.api.backendInteractor.bookmarkStatus({ id: status.id })
|
||||
rootState.api.backendInteractor.bookmarkStatus({ id: status.id, folder_id: status.bookmark_folder_id })
|
||||
.then(status => {
|
||||
commit('setBookmarkedConfirm', { status })
|
||||
})
|
||||
|
|
@ -691,31 +590,6 @@ const statuses = {
|
|||
queueFlushAll ({ rootState, commit }) {
|
||||
commit('queueFlushAll')
|
||||
},
|
||||
markNotificationsAsSeen ({ rootState, commit }) {
|
||||
commit('markNotificationsAsSeen')
|
||||
apiService.markNotificationsAsSeen({
|
||||
id: rootState.statuses.notifications.maxId,
|
||||
credentials: rootState.users.currentUser.credentials
|
||||
})
|
||||
},
|
||||
markSingleNotificationAsSeen ({ rootState, commit }, { id }) {
|
||||
commit('markSingleNotificationAsSeen', { id })
|
||||
apiService.markNotificationsAsSeen({
|
||||
single: true,
|
||||
id,
|
||||
credentials: rootState.users.currentUser.credentials
|
||||
})
|
||||
},
|
||||
dismissNotificationLocal ({ rootState, commit }, { id }) {
|
||||
commit('dismissNotification', { id })
|
||||
},
|
||||
dismissNotification ({ rootState, commit }, { id }) {
|
||||
commit('dismissNotification', { id })
|
||||
rootState.api.backendInteractor.dismissNotification({ id })
|
||||
},
|
||||
updateNotification ({ rootState, commit }, { id, updater }) {
|
||||
commit('updateNotification', { id, updater })
|
||||
},
|
||||
fetchFavsAndRepeats ({ rootState, commit }, id) {
|
||||
Promise.all([
|
||||
rootState.api.backendInteractor.fetchFavoritedByUsers({ id }),
|
||||
|
|
@ -748,7 +622,7 @@ const statuses = {
|
|||
)
|
||||
},
|
||||
fetchEmojiReactionsBy ({ rootState, commit }, id) {
|
||||
rootState.api.backendInteractor.fetchEmojiReactions({ id }).then(
|
||||
return rootState.api.backendInteractor.fetchEmojiReactions({ id }).then(
|
||||
emojiReactions => {
|
||||
commit('addEmojiReactionsBy', { id, emojiReactions, currentUser: rootState.users.currentUser })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import backendInteractorService from '../services/backend_interactor_service/bac
|
|||
import { windowWidth, windowHeight } from '../services/window_utils/window_utils'
|
||||
import oauthApi from '../services/new_api/oauth.js'
|
||||
import { compact, map, each, mergeWith, last, concat, uniq, isArray } from 'lodash'
|
||||
import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js'
|
||||
import { registerPushNotifications, unregisterPushNotifications } from '../services/sw/sw.js'
|
||||
import { useInterfaceStore } from '../stores/interface.js'
|
||||
|
||||
// TODO: Unify with mergeOrAdd in statuses.js
|
||||
|
|
@ -196,9 +196,15 @@ export const mutations = {
|
|||
state.currentUser.blockIds.push(blockId)
|
||||
}
|
||||
},
|
||||
setBlockIdsMaxId (state, blockIdsMaxId) {
|
||||
state.currentUser.blockIdsMaxId = blockIdsMaxId
|
||||
},
|
||||
saveMuteIds (state, muteIds) {
|
||||
state.currentUser.muteIds = muteIds
|
||||
},
|
||||
setMuteIdsMaxId (state, muteIdsMaxId) {
|
||||
state.currentUser.muteIdsMaxId = muteIdsMaxId
|
||||
},
|
||||
addMuteId (state, muteId) {
|
||||
if (state.currentUser.muteIds.indexOf(muteId) === -1) {
|
||||
state.currentUser.muteIds.push(muteId)
|
||||
|
|
@ -245,6 +251,7 @@ export const mutations = {
|
|||
signUpPending (state) {
|
||||
state.signUpPending = true
|
||||
state.signUpErrors = []
|
||||
state.signUpNotice = {}
|
||||
},
|
||||
signUpSuccess (state) {
|
||||
state.signUpPending = false
|
||||
|
|
@ -252,6 +259,12 @@ export const mutations = {
|
|||
signUpFailure (state, errors) {
|
||||
state.signUpPending = false
|
||||
state.signUpErrors = errors
|
||||
state.signUpNotice = {}
|
||||
},
|
||||
signUpNotice (state, notice) {
|
||||
state.signUpPending = false
|
||||
state.signUpErrors = []
|
||||
state.signUpNotice = notice
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,6 +295,7 @@ export const defaultState = {
|
|||
usersByNameObject: {},
|
||||
signUpPending: false,
|
||||
signUpErrors: [],
|
||||
signUpNotice: {},
|
||||
relationships: {}
|
||||
}
|
||||
|
||||
|
|
@ -321,10 +335,20 @@ const users = {
|
|||
.then((inLists) => store.commit('updateUserInLists', { id, inLists }))
|
||||
}
|
||||
},
|
||||
fetchBlocks (store) {
|
||||
return store.rootState.api.backendInteractor.fetchBlocks()
|
||||
fetchBlocks (store, args) {
|
||||
const { reset } = args || {}
|
||||
|
||||
const maxId = store.state.currentUser.blockIdsMaxId
|
||||
return store.rootState.api.backendInteractor.fetchBlocks({ maxId })
|
||||
.then((blocks) => {
|
||||
store.commit('saveBlockIds', map(blocks, 'id'))
|
||||
if (reset) {
|
||||
store.commit('saveBlockIds', map(blocks, 'id'))
|
||||
} else {
|
||||
map(blocks, 'id').map(id => store.commit('addBlockId', id))
|
||||
}
|
||||
if (blocks.length) {
|
||||
store.commit('setBlockIdsMaxId', last(blocks).id)
|
||||
}
|
||||
store.commit('addNewUsers', blocks)
|
||||
return blocks
|
||||
})
|
||||
|
|
@ -347,10 +371,20 @@ const users = {
|
|||
editUserNote (store, args) {
|
||||
return editUserNote(store, args)
|
||||
},
|
||||
fetchMutes (store) {
|
||||
return store.rootState.api.backendInteractor.fetchMutes()
|
||||
fetchMutes (store, args) {
|
||||
const { reset } = args || {}
|
||||
|
||||
const maxId = store.state.currentUser.muteIdsMaxId
|
||||
return store.rootState.api.backendInteractor.fetchMutes({ maxId })
|
||||
.then((mutes) => {
|
||||
store.commit('saveMuteIds', map(mutes, 'id'))
|
||||
if (reset) {
|
||||
store.commit('saveMuteIds', map(mutes, 'id'))
|
||||
} else {
|
||||
map(mutes, 'id').map(id => store.commit('addMuteId', id))
|
||||
}
|
||||
if (mutes.length) {
|
||||
store.commit('setMuteIdsMaxId', last(mutes).id)
|
||||
}
|
||||
store.commit('addNewUsers', mutes)
|
||||
return mutes
|
||||
})
|
||||
|
|
@ -419,11 +453,11 @@ const users = {
|
|||
commit('clearFollowers', userId)
|
||||
},
|
||||
subscribeUser ({ rootState, commit }, id) {
|
||||
return rootState.api.backendInteractor.subscribeUser({ id })
|
||||
return rootState.api.backendInteractor.followUser({ id, notify: true })
|
||||
.then((relationship) => commit('updateUserRelationship', [relationship]))
|
||||
},
|
||||
unsubscribeUser ({ rootState, commit }, id) {
|
||||
return rootState.api.backendInteractor.unsubscribeUser({ id })
|
||||
return rootState.api.backendInteractor.followUser({ id, notify: false })
|
||||
.then((relationship) => commit('updateUserRelationship', [relationship]))
|
||||
},
|
||||
toggleActivationStatus ({ rootState, commit }, { user }) {
|
||||
|
|
@ -473,7 +507,7 @@ const users = {
|
|||
store.commit('addNewUsers', users)
|
||||
store.commit('addNewUsers', targetUsers)
|
||||
|
||||
const notificationsObject = store.rootState.statuses.notifications.idStore
|
||||
const notificationsObject = store.rootState.notifications.idStore
|
||||
const relevantNotifications = Object.entries(notificationsObject)
|
||||
.filter(([k, val]) => notificationIds.includes(k))
|
||||
.map(([k, val]) => val)
|
||||
|
|
@ -499,9 +533,16 @@ const users = {
|
|||
const data = await rootState.api.backendInteractor.register(
|
||||
{ params: { ...userInfo } }
|
||||
)
|
||||
store.commit('signUpSuccess')
|
||||
store.commit('setToken', data.access_token)
|
||||
store.dispatch('loginUser', data.access_token)
|
||||
|
||||
if (data.access_token) {
|
||||
store.commit('signUpSuccess')
|
||||
store.commit('setToken', data.access_token)
|
||||
store.dispatch('loginUser', data.access_token)
|
||||
return 'ok'
|
||||
} else { // Request succeeded, but user cannot login yet.
|
||||
store.commit('signUpNotice', data)
|
||||
return 'request_sent'
|
||||
}
|
||||
} catch (e) {
|
||||
const errors = e.message
|
||||
store.commit('signUpFailure', errors)
|
||||
|
|
@ -539,6 +580,7 @@ const users = {
|
|||
store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken()))
|
||||
store.dispatch('stopFetchingNotifications')
|
||||
store.dispatch('stopFetchingLists')
|
||||
store.dispatch('stopFetchingBookmarkFolders')
|
||||
store.dispatch('stopFetchingFollowRequests')
|
||||
store.commit('clearNotifications')
|
||||
store.commit('resetStatuses')
|
||||
|
|
@ -552,6 +594,7 @@ const users = {
|
|||
loginUser (store, accessToken) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const commit = store.commit
|
||||
const dispatch = store.dispatch
|
||||
commit('beginLogin')
|
||||
store.rootState.api.backendInteractor.verifyCredentials(accessToken)
|
||||
.then((data) => {
|
||||
|
|
@ -566,54 +609,55 @@ const users = {
|
|||
commit('setServerSideStorage', user)
|
||||
commit('addNewUsers', [user])
|
||||
|
||||
store.dispatch('fetchEmoji')
|
||||
dispatch('fetchEmoji')
|
||||
|
||||
getNotificationPermission()
|
||||
.then(permission => useInterfaceStore().setNotificationPermission(permission))
|
||||
|
||||
// Set our new backend interactor
|
||||
commit('setBackendInteractor', backendInteractorService(accessToken))
|
||||
store.dispatch('pushServerSideStorage')
|
||||
dispatch('pushServerSideStorage')
|
||||
|
||||
if (user.token) {
|
||||
store.dispatch('setWsToken', user.token)
|
||||
dispatch('setWsToken', user.token)
|
||||
|
||||
// Initialize the shout socket.
|
||||
store.dispatch('initializeSocket')
|
||||
dispatch('initializeSocket')
|
||||
}
|
||||
|
||||
const startPolling = () => {
|
||||
// Start getting fresh posts.
|
||||
store.dispatch('startFetchingTimeline', { timeline: 'friends' })
|
||||
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
||||
|
||||
// Start fetching notifications
|
||||
store.dispatch('startFetchingNotifications')
|
||||
dispatch('startFetchingNotifications')
|
||||
|
||||
// Start fetching chats
|
||||
store.dispatch('startFetchingChats')
|
||||
dispatch('startFetchingChats')
|
||||
}
|
||||
|
||||
store.dispatch('startFetchingLists')
|
||||
dispatch('startFetchingLists')
|
||||
dispatch('startFetchingBookmarkFolders')
|
||||
|
||||
if (user.locked) {
|
||||
store.dispatch('startFetchingFollowRequests')
|
||||
dispatch('startFetchingFollowRequests')
|
||||
}
|
||||
|
||||
if (store.getters.mergedConfig.useStreamingApi) {
|
||||
store.dispatch('fetchTimeline', { timeline: 'friends', since: null })
|
||||
store.dispatch('fetchNotifications', { since: null })
|
||||
store.dispatch('enableMastoSockets', true).catch((error) => {
|
||||
dispatch('fetchTimeline', { timeline: 'friends', since: null })
|
||||
dispatch('fetchNotifications', { since: null })
|
||||
dispatch('enableMastoSockets', true).catch((error) => {
|
||||
console.error('Failed initializing MastoAPI Streaming socket', error)
|
||||
}).then(() => {
|
||||
store.dispatch('fetchChats', { latest: true })
|
||||
setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000)
|
||||
dispatch('fetchChats', { latest: true })
|
||||
setTimeout(() => dispatch('setNotificationsSilence', false), 10000)
|
||||
})
|
||||
} else {
|
||||
startPolling()
|
||||
}
|
||||
|
||||
// Get user mutes
|
||||
store.dispatch('fetchMutes')
|
||||
dispatch('fetchMutes')
|
||||
|
||||
useInterfaceStore().setLayoutWidth(windowWidth())
|
||||
useInterfaceStore().setLayoutHeight(windowHeight())
|
||||
|
|
@ -625,6 +669,12 @@ const users = {
|
|||
const response = data.error
|
||||
// Authentication failed
|
||||
commit('endLogin')
|
||||
|
||||
// remove authentication token on client/authentication errors
|
||||
if ([400, 401, 403, 422].includes(response.status)) {
|
||||
commit('clearToken')
|
||||
}
|
||||
|
||||
if (response.status === 401) {
|
||||
reject(new Error('Wrong username or password'))
|
||||
} else {
|
||||
|
|
@ -635,7 +685,7 @@ const users = {
|
|||
resolve()
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
console.error(error)
|
||||
commit('endLogin')
|
||||
reject(new Error('Failed to connect to server, try again'))
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue