aboutsummaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/api.js106
-rw-r--r--src/modules/chats.js6
-rw-r--r--src/modules/config.js55
-rw-r--r--src/modules/instance.js19
-rw-r--r--src/modules/media_viewer.js9
-rw-r--r--src/modules/shout.js (renamed from src/modules/chat.js)6
-rw-r--r--src/modules/statuses.js29
-rw-r--r--src/modules/users.js14
8 files changed, 190 insertions, 54 deletions
diff --git a/src/modules/api.js b/src/modules/api.js
index 08485a30..54f94356 100644
--- a/src/modules/api.js
+++ b/src/modules/api.js
@@ -3,8 +3,11 @@ import { WSConnectionStatus } from '../services/api/api.service.js'
import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
import { Socket } from 'phoenix'
+const retryTimeout = (multiplier) => 1000 * multiplier
+
const api = {
state: {
+ retryMultiplier: 1,
backendInteractor: backendInteractorService(),
fetchers: {},
socket: null,
@@ -34,18 +37,43 @@ const api = {
},
setMastoUserSocketStatus (state, value) {
state.mastoUserSocketStatus = value
+ },
+ incrementRetryMultiplier (state) {
+ state.retryMultiplier = Math.max(++state.retryMultiplier, 3)
+ },
+ resetRetryMultiplier (state) {
+ state.retryMultiplier = 1
}
},
actions: {
- // Global MastoAPI socket control, in future should disable ALL sockets/(re)start relevant sockets
- enableMastoSockets (store) {
- const { state, dispatch } = store
- if (state.mastoUserSocket) return
+ /**
+ * Global MastoAPI socket control, in future should disable ALL sockets/(re)start relevant sockets
+ *
+ * @param {Boolean} [initial] - whether this enabling happened at boot time or not
+ */
+ enableMastoSockets (store, initial) {
+ const { state, dispatch, commit } = store
+ // Do not initialize unless nonexistent or closed
+ if (
+ state.mastoUserSocket &&
+ ![
+ WebSocket.CLOSED,
+ WebSocket.CLOSING
+ ].includes(state.mastoUserSocket.getState())
+ ) {
+ return
+ }
+ if (initial) {
+ commit('setMastoUserSocketStatus', WSConnectionStatus.STARTING_INITIAL)
+ } else {
+ commit('setMastoUserSocketStatus', WSConnectionStatus.STARTING)
+ }
return dispatch('startMastoUserSocket')
},
disableMastoSockets (store) {
- const { state, dispatch } = store
+ const { state, dispatch, commit } = store
if (!state.mastoUserSocket) return
+ commit('setMastoUserSocketStatus', WSConnectionStatus.DISABLED)
return dispatch('stopMastoUserSocket')
},
@@ -91,11 +119,29 @@ const api = {
}
)
state.mastoUserSocket.addEventListener('open', () => {
+ // Do not show notification when we just opened up the page
+ if (state.mastoUserSocketStatus !== WSConnectionStatus.STARTING_INITIAL) {
+ dispatch('pushGlobalNotice', {
+ level: 'success',
+ messageKey: 'timeline.socket_reconnected',
+ timeout: 5000
+ })
+ }
+ // Stop polling if we were errored or disabled
+ if (new Set([
+ WSConnectionStatus.ERROR,
+ WSConnectionStatus.DISABLED
+ ]).has(state.mastoUserSocketStatus)) {
+ dispatch('stopFetchingTimeline', { timeline: 'friends' })
+ dispatch('stopFetchingNotifications')
+ dispatch('stopFetchingChats')
+ }
+ commit('resetRetryMultiplier')
commit('setMastoUserSocketStatus', WSConnectionStatus.JOINED)
})
state.mastoUserSocket.addEventListener('error', ({ detail: error }) => {
console.error('Error in MastoAPI websocket:', error)
- commit('setMastoUserSocketStatus', WSConnectionStatus.ERROR)
+ // TODO is this needed?
dispatch('clearOpenedChats')
})
state.mastoUserSocket.addEventListener('close', ({ detail: closeEvent }) => {
@@ -106,14 +152,26 @@ const api = {
const { code } = closeEvent
if (ignoreCodes.has(code)) {
console.debug(`Not restarting socket becasue of closure code ${code} is in ignore list`)
+ commit('setMastoUserSocketStatus', WSConnectionStatus.CLOSED)
} else {
console.warn(`MastoAPI websocket disconnected, restarting. CloseEvent code: ${code}`)
- dispatch('startFetchingTimeline', { timeline: 'friends' })
- dispatch('startFetchingNotifications')
- dispatch('startFetchingChats')
- dispatch('restartMastoUserSocket')
+ setTimeout(() => {
+ dispatch('startMastoUserSocket')
+ }, retryTimeout(state.retryMultiplier))
+ commit('incrementRetryMultiplier')
+ if (state.mastoUserSocketStatus !== WSConnectionStatus.ERROR) {
+ dispatch('startFetchingTimeline', { timeline: 'friends' })
+ dispatch('startFetchingNotifications')
+ dispatch('startFetchingChats')
+ dispatch('pushGlobalNotice', {
+ level: 'error',
+ messageKey: 'timeline.socket_broke',
+ messageArgs: [code],
+ timeout: 5000
+ })
+ }
+ commit('setMastoUserSocketStatus', WSConnectionStatus.ERROR)
}
- commit('setMastoUserSocketStatus', WSConnectionStatus.CLOSED)
dispatch('clearOpenedChats')
})
resolve()
@@ -122,15 +180,6 @@ const api = {
}
})
},
- restartMastoUserSocket ({ dispatch }) {
- // This basically starts MastoAPI user socket and stops conventional
- // fetchers when connection reestablished
- return dispatch('startMastoUserSocket').then(() => {
- dispatch('stopFetchingTimeline', { timeline: 'friends' })
- dispatch('stopFetchingNotifications')
- dispatch('stopFetchingChats')
- })
- },
stopMastoUserSocket ({ state, dispatch }) {
dispatch('startFetchingTimeline', { timeline: 'friends' })
dispatch('startFetchingNotifications')
@@ -156,6 +205,13 @@ const api = {
if (!fetcher) return
store.commit('removeFetcher', { fetcherName: timeline, fetcher })
},
+ fetchTimeline (store, timeline, { ...rest }) {
+ store.state.backendInteractor.fetchTimeline({
+ store,
+ timeline,
+ ...rest
+ })
+ },
// Notifications
startFetchingNotifications (store) {
@@ -168,6 +224,12 @@ const api = {
if (!fetcher) return
store.commit('removeFetcher', { fetcherName: 'notifications', fetcher })
},
+ fetchNotifications (store, { ...rest }) {
+ store.state.backendInteractor.fetchNotifications({
+ store,
+ ...rest
+ })
+ },
// Follow requests
startFetchingFollowRequests (store) {
@@ -193,12 +255,12 @@ const api = {
initializeSocket ({ dispatch, commit, state, rootState }) {
// Set up websocket connection
const token = state.wsToken
- if (rootState.instance.chatAvailable && typeof token !== 'undefined' && state.socket === null) {
+ if (rootState.instance.shoutAvailable && typeof token !== 'undefined' && state.socket === null) {
const socket = new Socket('/socket', { params: { token } })
socket.connect()
commit('setSocket', socket)
- dispatch('initializeChat', socket)
+ dispatch('initializeShout', socket)
}
},
disconnectFromSocket ({ commit, state }) {
diff --git a/src/modules/chats.js b/src/modules/chats.js
index 0a373d88..69d683bd 100644
--- a/src/modules/chats.js
+++ b/src/modules/chats.js
@@ -115,6 +115,9 @@ const chats = {
},
handleMessageError ({ commit }, value) {
commit('handleMessageError', { commit, ...value })
+ },
+ cullOlderMessages ({ commit }, chatId) {
+ commit('cullOlderMessages', chatId)
}
},
mutations: {
@@ -227,6 +230,9 @@ const chats = {
handleMessageError (state, { chatId, fakeId, isRetry }) {
const chatMessageService = state.openedChatMessageServices[chatId]
chatService.handleMessageError(chatMessageService, fakeId, isRetry)
+ },
+ cullOlderMessages (state, chatId) {
+ chatService.cullOlderMessages(state.openedChatMessageServices[chatId])
}
}
}
diff --git a/src/modules/config.js b/src/modules/config.js
index e591a506..825535cd 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -11,7 +11,8 @@ const browserLocale = (window.navigator.language || 'en').split('-')[0]
*/
export const multiChoiceProperties = [
'postContentType',
- 'subjectLineBehavior'
+ 'subjectLineBehavior',
+ 'mentionLinkDisplay' // short | full_for_remote | full
]
export const defaultState = {
@@ -21,8 +22,11 @@ export const defaultState = {
customThemeSource: undefined,
hideISP: false,
hideInstanceWallpaper: false,
+ hideShoutbox: false,
// bad name: actually hides posts of muted USERS
hideMutedPosts: undefined, // instance default
+ hideMutedThreads: undefined, // instance default
+ hideWordFilteredPosts: undefined, // instance default
collapseMessageWithSubject: undefined, // instance default
padEmoji: true,
hideAttachments: false,
@@ -34,6 +38,7 @@ export const defaultState = {
loopVideoSilentOnly: true,
streaming: false,
emojiReactionsOnTimeline: true,
+ alwaysShowNewPostButton: false,
autohideFloatingPostButton: false,
pauseOnUnfocused: true,
stopGifs: false,
@@ -55,6 +60,7 @@ export const defaultState = {
interfaceLanguage: browserLocale,
hideScopeNotice: false,
useStreamingApi: false,
+ sidebarRight: undefined, // instance default
scopeCopy: undefined, // instance default
subjectLineBehavior: undefined, // instance default
alwaysShowSubjectInput: undefined, // instance default
@@ -66,9 +72,17 @@ export const defaultState = {
useOneClickNsfw: false,
useContainFit: false,
greentext: undefined, // instance default
+ useAtIcon: undefined, // instance default
+ mentionLinkDisplay: undefined, // instance default
+ mentionLinkShowTooltip: undefined, // instance default
+ mentionLinkShowAvatar: undefined, // instance default
+ mentionLinkFadeDomain: undefined, // instance default
+ mentionLinkShowYous: undefined, // instance default
+ mentionLinkBoldenYou: undefined, // instance default
hidePostStats: undefined, // instance default
hideUserStats: undefined, // instance default
- virtualScrolling: undefined // instance default
+ virtualScrolling: undefined, // instance default
+ sensitiveByDefault: undefined // instance default
}
// caching the instance default properties
@@ -77,18 +91,23 @@ export const instanceDefaultProperties = Object.entries(defaultState)
.map(([key, value]) => key)
const config = {
- state: defaultState,
+ state: { ...defaultState },
getters: {
- mergedConfig (state, getters, rootState, rootGetters) {
+ defaultConfig (state, getters, rootState, rootGetters) {
const { instance } = rootState
return {
- ...state,
- ...instanceDefaultProperties
- .map(key => [key, state[key] === undefined
- ? instance[key]
- : state[key]
- ])
- .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
+ ...defaultState,
+ ...Object.fromEntries(
+ instanceDefaultProperties.map(key => [key, instance[key]])
+ )
+ }
+ },
+ mergedConfig (state, getters, rootState, rootGetters) {
+ const { defaultConfig } = rootGetters
+ return {
+ ...defaultConfig,
+ // Do not override with undefined
+ ...Object.fromEntries(Object.entries(state).filter(([k, v]) => v !== undefined))
}
}
},
@@ -106,6 +125,20 @@ const config = {
}
},
actions: {
+ loadSettings ({ dispatch }, data) {
+ const knownKeys = new Set(Object.keys(defaultState))
+ const presentKeys = new Set(Object.keys(data))
+ const intersection = new Set()
+ for (let elem of presentKeys) {
+ if (knownKeys.has(elem)) {
+ intersection.add(elem)
+ }
+ }
+
+ intersection.forEach(
+ name => dispatch('setOption', { name, value: data[name] })
+ )
+ },
setHighlight ({ commit, dispatch }, { user, color, type }) {
commit('setHighlight', { user, color, type })
},
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 411b1caa..1abd784f 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -19,10 +19,19 @@ const defaultState = {
defaultBanner: '/images/banner.png',
background: '/static/aurora_borealis.jpg',
collapseMessageWithSubject: false,
- disableChat: false,
greentext: false,
+ useAtIcon: false,
+ mentionLinkDisplay: 'short',
+ mentionLinkShowTooltip: true,
+ mentionLinkShowAvatar: false,
+ mentionLinkFadeDomain: true,
+ mentionLinkShowYous: false,
+ mentionLinkBoldenYou: true,
hideFilteredStatuses: false,
+ // bad name: actually hides posts of muted USERS
hideMutedPosts: false,
+ hideMutedThreads: true,
+ hideWordFilteredPosts: false,
hidePostStats: false,
hideSitename: false,
hideUserStats: false,
@@ -43,6 +52,7 @@ const defaultState = {
subjectLineBehavior: 'email',
theme: 'pleroma-dark',
virtualScrolling: true,
+ sensitiveByDefault: false,
// Nasty stuff
customEmoji: [],
@@ -56,7 +66,7 @@ const defaultState = {
knownDomains: [],
// Feature-set, apparently, not everything here is reported...
- chatAvailable: false,
+ shoutAvailable: false,
pleromaChatMessagesAvailable: false,
gopherAvailable: false,
mediaProxyAvailable: false,
@@ -97,6 +107,9 @@ const instance = {
return instanceDefaultProperties
.map(key => [key, state[key]])
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
+ },
+ instanceDomain (state) {
+ return new URL(state.server).hostname
}
},
actions: {
@@ -106,7 +119,7 @@ const instance = {
case 'name':
dispatch('setPageTitle')
break
- case 'chatAvailable':
+ case 'shoutAvailable':
if (value) {
dispatch('initializeSocket')
}
diff --git a/src/modules/media_viewer.js b/src/modules/media_viewer.js
index 721c25e6..ebcba01d 100644
--- a/src/modules/media_viewer.js
+++ b/src/modules/media_viewer.js
@@ -1,4 +1,5 @@
import fileTypeService from '../services/file_type/file_type.service.js'
+const supportedTypes = new Set(['image', 'video', 'audio', 'flash'])
const mediaViewer = {
state: {
@@ -10,7 +11,7 @@ const mediaViewer = {
setMedia (state, media) {
state.media = media
},
- setCurrent (state, index) {
+ setCurrentMedia (state, index) {
state.activated = true
state.currentIndex = index
},
@@ -22,13 +23,13 @@ const mediaViewer = {
setMedia ({ commit }, attachments) {
const media = attachments.filter(attachment => {
const type = fileTypeService.fileType(attachment.mimetype)
- return type === 'image' || type === 'video' || type === 'audio'
+ return supportedTypes.has(type)
})
commit('setMedia', media)
},
- setCurrent ({ commit, state }, current) {
+ setCurrentMedia ({ commit, state }, current) {
const index = state.media.indexOf(current)
- commit('setCurrent', index || 0)
+ commit('setCurrentMedia', index || 0)
},
closeMediaViewer ({ commit }) {
commit('close')
diff --git a/src/modules/chat.js b/src/modules/shout.js
index c798549d..507a4d83 100644
--- a/src/modules/chat.js
+++ b/src/modules/shout.js
@@ -1,4 +1,4 @@
-const chat = {
+const shout = {
state: {
messages: [],
channel: { state: '' }
@@ -16,7 +16,7 @@ const chat = {
}
},
actions: {
- initializeChat (store, socket) {
+ initializeShout (store, socket) {
const channel = socket.channel('chat:public')
channel.on('new_msg', (msg) => {
store.commit('addMessage', msg)
@@ -30,4 +30,4 @@ const chat = {
}
}
-export default chat
+export default shout
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index d5343cd4..cb2a8d46 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -13,7 +13,11 @@ import {
omitBy
} from 'lodash'
import { set } from 'vue'
-import { isStatusNotification, maybeShowNotification } from '../services/notification_utils/notification_utils.js'
+import {
+ isStatusNotification,
+ isValidNotification,
+ maybeShowNotification
+} from '../services/notification_utils/notification_utils.js'
import apiService from '../services/api/api.service.js'
const emptyTl = (userId = 0) => ({
@@ -310,8 +314,24 @@ 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
@@ -327,12 +347,7 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot
// Only add a new notification if we don't have one for the same action
if (!state.notifications.idStore.hasOwnProperty(notification.id)) {
- 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
+ updateNotificationsMinMaxId(state, notification)
state.notifications.data.push(notification)
state.notifications.idStore[notification.id] = notification
diff --git a/src/modules/users.js b/src/modules/users.js
index 655db4c7..05ff44d5 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -246,6 +246,11 @@ export const getters = {
}
return result
},
+ findUserByUrl: state => query => {
+ return state.users
+ .find(u => u.statusnet_profile_url &&
+ u.statusnet_profile_url.toLowerCase() === query.toLowerCase())
+ },
relationship: state => id => {
const rel = id && state.relationships[id]
return rel || { id, loading: true }
@@ -388,7 +393,7 @@ const users = {
toggleActivationStatus ({ rootState, commit }, { user }) {
const api = user.deactivated ? rootState.api.backendInteractor.activateUser : rootState.api.backendInteractor.deactivateUser
api({ user })
- .then(({ deactivated }) => commit('updateActivationStatus', { user, deactivated }))
+ .then((user) => { let deactivated = !user.is_active; commit('updateActivationStatus', { user, deactivated }) })
},
registerPushNotifications (store) {
const token = store.state.currentUser.credentials
@@ -531,7 +536,7 @@ const users = {
if (user.token) {
store.dispatch('setWsToken', user.token)
- // Initialize the chat socket.
+ // Initialize the shout socket.
store.dispatch('initializeSocket')
}
@@ -547,9 +552,10 @@ const users = {
}
if (store.getters.mergedConfig.useStreamingApi) {
- store.dispatch('enableMastoSockets').catch((error) => {
+ store.dispatch('fetchTimeline', 'friends', { since: null })
+ store.dispatch('fetchNotifications', { since: null })
+ store.dispatch('enableMastoSockets', true).catch((error) => {
console.error('Failed initializing MastoAPI Streaming socket', error)
- startPolling()
}).then(() => {
store.dispatch('fetchChats', { latest: true })
setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000)