From ddb6fb9217789e90490a4ec1ce7a2dd9ced67631 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 24 Nov 2019 13:57:46 +0200 Subject: Backend Interactor service overhaul, removed the need for copypasting --- src/modules/users.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src/modules/users.js') diff --git a/src/modules/users.js b/src/modules/users.js index 14b2d8b5..e1373220 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -32,7 +32,7 @@ const getNotificationPermission = () => { } const blockUser = (store, id) => { - return store.rootState.api.backendInteractor.blockUser(id) + return store.rootState.api.backendInteractor.blockUser({ id }) .then((relationship) => { store.commit('updateUserRelationship', [relationship]) store.commit('addBlockId', id) @@ -43,12 +43,12 @@ const blockUser = (store, id) => { } const unblockUser = (store, id) => { - return store.rootState.api.backendInteractor.unblockUser(id) + return store.rootState.api.backendInteractor.unblockUser({ id }) .then((relationship) => store.commit('updateUserRelationship', [relationship])) } const muteUser = (store, id) => { - return store.rootState.api.backendInteractor.muteUser(id) + return store.rootState.api.backendInteractor.muteUser({ id }) .then((relationship) => { store.commit('updateUserRelationship', [relationship]) store.commit('addMuteId', id) @@ -56,7 +56,7 @@ const muteUser = (store, id) => { } const unmuteUser = (store, id) => { - return store.rootState.api.backendInteractor.unmuteUser(id) + return store.rootState.api.backendInteractor.unmuteUser({ id }) .then((relationship) => store.commit('updateUserRelationship', [relationship])) } @@ -324,11 +324,11 @@ const users = { commit('clearFollowers', userId) }, subscribeUser ({ rootState, commit }, id) { - return rootState.api.backendInteractor.subscribeUser(id) + return rootState.api.backendInteractor.subscribeUser({ id }) .then((relationship) => commit('updateUserRelationship', [relationship])) }, unsubscribeUser ({ rootState, commit }, id) { - return rootState.api.backendInteractor.unsubscribeUser(id) + return rootState.api.backendInteractor.unsubscribeUser({ id }) .then((relationship) => commit('updateUserRelationship', [relationship])) }, registerPushNotifications (store) { @@ -382,7 +382,7 @@ const users = { }) }, searchUsers (store, query) { - return store.rootState.api.backendInteractor.searchUsers(query) + return store.rootState.api.backendInteractor.searchUsers({ query }) .then((users) => { store.commit('addNewUsers', users) return users @@ -394,7 +394,7 @@ const users = { let rootState = store.rootState try { - let data = await rootState.api.backendInteractor.register(userInfo) + let data = await rootState.api.backendInteractor.register({ ...userInfo }) store.commit('signUpSuccess') store.commit('setToken', data.access_token) store.dispatch('loginUser', data.access_token) -- cgit v1.2.3-70-g09d2 From 319bb4ac2895b8eb62da42e3f95addc9bb67b1a0 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 24 Nov 2019 18:50:28 +0200 Subject: initial streaming work --- src/modules/api.js | 15 ++++++++ src/modules/users.js | 13 ++++--- src/services/api/api.service.js | 40 ++++++++++++++++++++++ .../backend_interactor_service.js | 15 +++++++- 4 files changed, 77 insertions(+), 6 deletions(-) (limited to 'src/modules/users.js') diff --git a/src/modules/api.js b/src/modules/api.js index 1293e3c8..1bf65db5 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -6,6 +6,7 @@ const api = { backendInteractor: backendInteractorService(), fetchers: {}, socket: null, + mastoSocket: null, followRequests: [] }, mutations: { @@ -29,6 +30,20 @@ const api = { } }, actions: { + startMastoSocket (store) { + store.state.mastoSocket = store.state.backendInteractor + .startUserSocket({ + store, + onMessage: (message) => { + if (!message) return + if (message.event === 'notification') { + store.dispatch('addNewNotifications', { notifications: [message.notification], older: false }) + } else if (message.event === 'update') { + store.dispatch('addNewStatuses', { statuses: [message.status], userId: false, showImmediately: false, timeline: 'friends' }) + } + } + }) + }, startFetchingTimeline (store, { timeline = 'friends', tag = false, userId = false }) { // Don't start fetching if we already are. if (store.state.fetchers[timeline]) return diff --git a/src/modules/users.js b/src/modules/users.js index e1373220..861a2f4f 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -469,11 +469,14 @@ const users = { store.dispatch('initializeSocket') } - // Start getting fresh posts. - store.dispatch('startFetchingTimeline', { timeline: 'friends' }) - - // Start fetching notifications - store.dispatch('startFetchingNotifications') + store.dispatch('startMastoSocket').catch((error) => { + console.error(error) + // Start getting fresh posts. + store.dispatch('startFetchingTimeline', { timeline: 'friends' }) + + // Start fetching notifications + store.dispatch('startFetchingNotifications') + }) // Get user mutes store.dispatch('fetchMutes') diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 8f5eb416..7f27564f 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -71,6 +71,7 @@ const MASTODON_MUTE_CONVERSATION = id => `/api/v1/statuses/${id}/mute` const MASTODON_UNMUTE_CONVERSATION = id => `/api/v1/statuses/${id}/unmute` const MASTODON_SEARCH_2 = `/api/v2/search` const MASTODON_USER_SEARCH_URL = '/api/v1/accounts/search' +const MASTODON_STREAMING = '/api/v1/streaming' const oldfetch = window.fetch @@ -932,6 +933,45 @@ const search2 = ({ credentials, q, resolve, limit, offset, following }) => { }) } +export const getMastodonSocketURI = ({ credentials, stream, args = {} }) => { + return Object.entries({ + ...(credentials + ? { access_token: credentials } + : {} + ), + stream, + ...args + }).reduce((acc, [key, val]) => { + return acc + `${key}=${val}&` + }, MASTODON_STREAMING + '?') +} + +const MASTODON_STREAMING_EVENTS = new Set([ + 'update', + 'notification', + 'delete', + 'filters_changed' +]) + +export const handleMastoWS = (wsEvent) => { + console.debug('Event', wsEvent) + const { data } = wsEvent + if (!data) return + const parsedEvent = JSON.parse(data) + const { event, payload } = parsedEvent + if (MASTODON_STREAMING_EVENTS.has(event)) { + const data = payload ? JSON.parse(payload) : null + if (event === 'update') { + return { event, status: parseStatus(data) } + } else if (event === 'notification') { + return { event, notification: parseNotification(data) } + } + } else { + console.warn('Unknown event', wsEvent) + return null + } +} + const apiService = { verifyCredentials, fetchTimeline, diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index 57fdccde..0cef4640 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -1,4 +1,4 @@ -import apiService from '../api/api.service.js' +import apiService, { getMastodonSocketURI, handleMastoWS } from '../api/api.service.js' import timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js' import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js' import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service' @@ -16,6 +16,19 @@ const backendInteractorService = credentials => ({ return followRequestFetcher.startFetching({ store, credentials }) }, + startUserSocket ({ store, onMessage }) { + const serv = store.rootState.instance.server.replace('https', 'wss') + // const serb = 'ws://localhost:8080/' + const url = serv + getMastodonSocketURI({ credentials, stream: 'user' }) + const socket = new WebSocket(url) + console.log(socket) + if (socket) { + socket.addEventListener('message', (wsEvent) => onMessage(handleMastoWS(wsEvent))) + } else { + throw new Error('failed to connect to socket') + } + }, + ...Object.entries(apiService).reduce((acc, [key, func]) => { return { ...acc, -- cgit v1.2.3-70-g09d2 From 172ebaf4e67358852bfaafd8f069763ca5e602b1 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 24 Nov 2019 22:01:12 +0200 Subject: improved initial notifications fetching --- src/components/notifications/notifications.js | 5 +++++ src/modules/api.js | 23 +++++++++++++++++++--- src/modules/users.js | 2 +- .../backend_interactor_service.js | 9 +++++++-- 4 files changed, 33 insertions(+), 6 deletions(-) (limited to 'src/modules/users.js') diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 6c4054fd..a89c0cdc 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -47,6 +47,11 @@ const Notifications = { components: { Notification }, + created () { + const { dispatch } = this.$store + + dispatch('fetchAndUpdateNotifications') + }, watch: { unseenCount (count) { if (count > 0) { diff --git a/src/modules/api.js b/src/modules/api.js index 1bf65db5..0e7e5e19 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -31,18 +31,32 @@ const api = { }, actions: { startMastoSocket (store) { - store.state.mastoSocket = store.state.backendInteractor + const { state, dispatch } = store + state.mastoSocket = state.backendInteractor .startUserSocket({ store, onMessage: (message) => { if (!message) return if (message.event === 'notification') { - store.dispatch('addNewNotifications', { notifications: [message.notification], older: false }) + dispatch('addNewNotifications', { + notifications: [message.notification], + older: false + }) } else if (message.event === 'update') { - store.dispatch('addNewStatuses', { statuses: [message.status], userId: false, showImmediately: false, timeline: 'friends' }) + dispatch('addNewStatuses', { + statuses: [message.status], + userId: false, + showImmediately: false, + timeline: 'friends' + }) } } }) + state.mastoSocket.addEventListener('error', error => { + console.error('Error with MastoAPI websocket:', error) + dispatch('startFetchingTimeline', { timeline: 'friends' }) + dispatch('startFetchingNotifications') + }) }, startFetchingTimeline (store, { timeline = 'friends', tag = false, userId = false }) { // Don't start fetching if we already are. @@ -58,6 +72,9 @@ const api = { const fetcher = store.state.backendInteractor.startFetchingNotifications({ store }) store.commit('addFetcher', { fetcherName: 'notifications', fetcher }) }, + fetchAndUpdateNotifications (store) { + store.state.backendInteractor.fetchAndUpdateNotifications({ store }) + }, startFetchingFollowRequest (store) { // Don't start fetching if we already are. if (store.state.fetchers['followRequest']) return diff --git a/src/modules/users.js b/src/modules/users.js index 861a2f4f..eff0c5d5 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -470,7 +470,7 @@ const users = { } store.dispatch('startMastoSocket').catch((error) => { - console.error(error) + console.error('Failed initializing MastoAPI Streaming socket', error) // Start getting fresh posts. store.dispatch('startFetchingTimeline', { timeline: 'friends' }) diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index 0cef4640..850b7867 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -12,18 +12,23 @@ const backendInteractorService = credentials => ({ return notificationsFetcher.startFetching({ store, credentials }) }, + fetchAndUpdateNotifications ({ store }) { + return notificationsFetcher.fetchAndUpdate({ store, credentials }) + }, + startFetchingFollowRequest ({ store }) { return followRequestFetcher.startFetching({ store, credentials }) }, startUserSocket ({ store, onMessage }) { - const serv = store.rootState.instance.server.replace('https', 'wss') - // const serb = 'ws://localhost:8080/' + const serv = store.rootState.instance.server.replace('http', 'ws') const url = serv + getMastodonSocketURI({ credentials, stream: 'user' }) const socket = new WebSocket(url) console.log(socket) if (socket) { socket.addEventListener('message', (wsEvent) => onMessage(handleMastoWS(wsEvent))) + socket.addEventListener('error', (error) => console.error('WebSocket Error:', error)) + return socket } else { throw new Error('failed to connect to socket') } -- cgit v1.2.3-70-g09d2 From ff95d865d223fed5bab2a4d72ff3a22f014d3c56 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 8 Dec 2019 16:05:41 +0200 Subject: Updated streaming and improved error-handling, some more refactoring to api --- .../public_and_external_timeline.js | 2 +- src/components/public_timeline/public_timeline.js | 2 +- src/components/tag_timeline/tag_timeline.js | 2 +- src/components/user_profile/user_profile.js | 6 +- src/modules/api.js | 99 ++++++++++++++++------ src/modules/users.js | 8 +- .../backend_interactor_service.js | 5 +- 7 files changed, 84 insertions(+), 40 deletions(-) (limited to 'src/modules/users.js') diff --git a/src/components/public_and_external_timeline/public_and_external_timeline.js b/src/components/public_and_external_timeline/public_and_external_timeline.js index f614c13b..cbd4491b 100644 --- a/src/components/public_and_external_timeline/public_and_external_timeline.js +++ b/src/components/public_and_external_timeline/public_and_external_timeline.js @@ -10,7 +10,7 @@ const PublicAndExternalTimeline = { this.$store.dispatch('startFetchingTimeline', { timeline: 'publicAndExternal' }) }, destroyed () { - this.$store.dispatch('stopFetching', 'publicAndExternal') + this.$store.dispatch('stopFetchingTimeline', 'publicAndExternal') } } diff --git a/src/components/public_timeline/public_timeline.js b/src/components/public_timeline/public_timeline.js index 8976a99c..66c40d3a 100644 --- a/src/components/public_timeline/public_timeline.js +++ b/src/components/public_timeline/public_timeline.js @@ -10,7 +10,7 @@ const PublicTimeline = { this.$store.dispatch('startFetchingTimeline', { timeline: 'public' }) }, destroyed () { - this.$store.dispatch('stopFetching', 'public') + this.$store.dispatch('stopFetchingTimeline', 'public') } } diff --git a/src/components/tag_timeline/tag_timeline.js b/src/components/tag_timeline/tag_timeline.js index 458eb1c5..400c6a4b 100644 --- a/src/components/tag_timeline/tag_timeline.js +++ b/src/components/tag_timeline/tag_timeline.js @@ -19,7 +19,7 @@ const TagTimeline = { } }, destroyed () { - this.$store.dispatch('stopFetching', 'tag') + this.$store.dispatch('stopFetchingTimeline', 'tag') } } diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 00055707..9558a0bd 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -112,9 +112,9 @@ const UserProfile = { } }, stopFetching () { - this.$store.dispatch('stopFetching', 'user') - this.$store.dispatch('stopFetching', 'favorites') - this.$store.dispatch('stopFetching', 'media') + this.$store.dispatch('stopFetchingTimeline', 'user') + this.$store.dispatch('stopFetchingTimeline', 'favorites') + this.$store.dispatch('stopFetchingTimeline', 'media') }, switchUser (userNameOrId) { this.stopFetching() diff --git a/src/modules/api.js b/src/modules/api.js index 0e7e5e19..185c9db6 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -6,7 +6,7 @@ const api = { backendInteractor: backendInteractorService(), fetchers: {}, socket: null, - mastoSocket: null, + mastoUserSocket: null, followRequests: [] }, mutations: { @@ -16,7 +16,8 @@ const api = { addFetcher (state, { fetcherName, fetcher }) { state.fetchers[fetcherName] = fetcher }, - removeFetcher (state, { fetcherName }) { + removeFetcher (state, { fetcherName, fetcher }) { + window.clearInterval(fetcher) delete state.fetchers[fetcherName] }, setWsToken (state, token) { @@ -30,13 +31,14 @@ const api = { } }, actions: { - startMastoSocket (store) { + // MastoAPI 'User' sockets + startMastoUserSocket (store) { const { state, dispatch } = store - state.mastoSocket = state.backendInteractor + state.mastoUserSocket = state.backendInteractor .startUserSocket({ store, onMessage: (message) => { - if (!message) return + if (!message) return // pings if (message.event === 'notification') { dispatch('addNewNotifications', { notifications: [message.notification], @@ -52,41 +54,86 @@ const api = { } } }) - state.mastoSocket.addEventListener('error', error => { - console.error('Error with MastoAPI websocket:', error) - dispatch('startFetchingTimeline', { timeline: 'friends' }) - dispatch('startFetchingNotifications') + state.mastoUserSocket.addEventListener('error', error => { + console.error('Error in MastoAPI websocket:', error) + }) + state.mastoUserSocket.addEventListener('close', closeEvent => { + const ignoreCodes = new Set([ + 1000, // Normal (intended) closure + 1001 // Going away + ]) + const { code } = closeEvent + console.debug('Socket closure event:', closeEvent) + if (ignoreCodes.has(code)) { + console.debug(`Not restarting socket becasue of closure code ${code} is in ignore list`) + } else { + console.warn(`MastoAPI websocket disconnected, restarting. CloseEvent code: ${code}`) + dispatch('startFetchingTimeline', { timeline: 'friends' }) + dispatch('startFetchingNotifications') + dispatch('restartMastoUserSocket') + } }) }, - startFetchingTimeline (store, { timeline = 'friends', tag = false, userId = false }) { - // Don't start fetching if we already are. + restartMastoUserSocket ({ dispatch }) { + // This basically starts MastoAPI user socket and stops conventional + // fetchers when connection reestablished + dispatch('startMastoUserSocket').then(() => { + dispatch('stopFetchingTimeline', { timeline: 'friends' }) + dispatch('stopFetchingNotifications') + }) + }, + + // Timelines + startFetchingTimeline (store, { + timeline = 'friends', + tag = false, + userId = false + }) { if (store.state.fetchers[timeline]) return - const fetcher = store.state.backendInteractor.startFetchingTimeline({ timeline, store, userId, tag }) + const fetcher = store.state.backendInteractor.startFetchingTimeline({ + timeline, store, userId, tag + }) store.commit('addFetcher', { fetcherName: timeline, fetcher }) }, - startFetchingNotifications (store) { - // Don't start fetching if we already are. - if (store.state.fetchers['notifications']) return + stopFetchingTimeline (store, timeline) { + const fetcher = store.state.fetchers[timeline] + if (!fetcher) return + store.commit('removeFetcher', { fetcherName: timeline, fetcher }) + }, + // Notifications + startFetchingNotifications (store) { + if (store.state.fetchers.notifications) return const fetcher = store.state.backendInteractor.startFetchingNotifications({ store }) store.commit('addFetcher', { fetcherName: 'notifications', fetcher }) }, + stopFetchingNotifications (store) { + const fetcher = store.state.fetchers.notifications + if (!fetcher) return + store.commit('removeFetcher', { fetcherName: 'notifications', fetcher }) + }, fetchAndUpdateNotifications (store) { store.state.backendInteractor.fetchAndUpdateNotifications({ store }) }, - startFetchingFollowRequest (store) { - // Don't start fetching if we already are. - if (store.state.fetchers['followRequest']) return - const fetcher = store.state.backendInteractor.startFetchingFollowRequest({ store }) - store.commit('addFetcher', { fetcherName: 'followRequest', fetcher }) + // Follow requests + startFetchingFollowRequests (store) { + if (store.state.fetchers['followRequests']) return + const fetcher = store.state.backendInteractor.startFetchingFollowRequests({ store }) + store.commit('addFetcher', { fetcherName: 'followRequests', fetcher }) }, - stopFetching (store, fetcherName) { - const fetcher = store.state.fetchers[fetcherName] - window.clearInterval(fetcher) - store.commit('removeFetcher', { fetcherName }) + stopFetchingFollowRequests (store) { + const fetcher = store.state.fetchers.followRequests + if (!fetcher) return + store.commit('removeFetcher', { fetcherName: 'followRequests', fetcher }) + }, + removeFollowRequest (store, request) { + let requests = store.state.followRequests.filter((it) => it !== request) + store.commit('setFollowRequests', requests) }, + + // Pleroma websocket setWsToken (store, token) { store.commit('setWsToken', token) }, @@ -104,10 +151,6 @@ const api = { disconnectFromSocket ({ commit, state }) { state.socket && state.socket.disconnect() commit('setSocket', null) - }, - removeFollowRequest (store, request) { - let requests = store.state.followRequests.filter((it) => it !== request) - store.commit('setFollowRequests', requests) } } } diff --git a/src/modules/users.js b/src/modules/users.js index eff0c5d5..6bdaf193 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -431,10 +431,10 @@ const users = { store.commit('clearCurrentUser') store.dispatch('disconnectFromSocket') store.commit('clearToken') - store.dispatch('stopFetching', 'friends') + store.dispatch('stopFetchingTimeline', 'friends') store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken())) - store.dispatch('stopFetching', 'notifications') - store.dispatch('stopFetching', 'followRequest') + store.dispatch('stopFetchingNotifications') + store.dispatch('stopFetchingFollowRequests') store.commit('clearNotifications') store.commit('resetStatuses') }) @@ -469,7 +469,7 @@ const users = { store.dispatch('initializeSocket') } - store.dispatch('startMastoSocket').catch((error) => { + store.dispatch('startMastoUserSocket').catch((error) => { console.error('Failed initializing MastoAPI Streaming socket', error) // Start getting fresh posts. store.dispatch('startFetchingTimeline', { timeline: 'friends' }) diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index 850b7867..33b79a40 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -24,10 +24,11 @@ const backendInteractorService = credentials => ({ const serv = store.rootState.instance.server.replace('http', 'ws') const url = serv + getMastodonSocketURI({ credentials, stream: 'user' }) const socket = new WebSocket(url) - console.log(socket) + console.debug('Socket created:', socket) if (socket) { + socket.addEventListener('open', (wsEvent) => console.debug('MastoAPI User WebSocket connection established')) socket.addEventListener('message', (wsEvent) => onMessage(handleMastoWS(wsEvent))) - socket.addEventListener('error', (error) => console.error('WebSocket Error:', error)) + socket.addEventListener('error', (error) => console.error('MastoApi User WebSocket Error:', error)) return socket } else { throw new Error('failed to connect to socket') -- cgit v1.2.3-70-g09d2 From 6acd889589e46b18491d96b5fa992154b4e58d88 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 10 Dec 2019 21:30:27 +0200 Subject: Option to enable streaming --- src/components/settings/settings.js | 14 +++++++++++++- src/components/settings/settings.vue | 9 +++++++++ src/i18n/en.json | 2 ++ src/i18n/ru.json | 2 ++ src/modules/api.js | 18 ++++++++++++++++++ src/modules/config.js | 1 + src/modules/users.js | 14 +++++++++++--- src/services/api/api.service.js | 6 ++++++ 8 files changed, 62 insertions(+), 4 deletions(-) (limited to 'src/modules/users.js') diff --git a/src/components/settings/settings.js b/src/components/settings/settings.js index c49083f9..2d7723cc 100644 --- a/src/components/settings/settings.js +++ b/src/components/settings/settings.js @@ -84,7 +84,7 @@ const settings = { } }]) .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}), - // Special cases (need to transform values) + // Special cases (need to transform values or perform actions first) muteWordsString: { get () { return this.$store.getters.mergedConfig.muteWords.join('\n') }, set (value) { @@ -93,6 +93,18 @@ const settings = { value: filter(value.split('\n'), (word) => trim(word).length > 0) }) } + }, + useStreamingApi: { + get () { return this.$store.getters.mergedConfig.useStreamingApi }, + set (value) { + const promise = value + ? this.$store.dispatch('enableMastoSockets') + : this.$store.dispatch('disableMastoSockets') + + promise.then(() => { + this.$store.dispatch('setOption', { name: 'useStreamingApi', value }) + }) + } } }, // Updating nested properties diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index c4021137..b40c85dd 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -73,6 +73,15 @@ +
  • + + {{ $t('settings.useStreamingApi') }} +
    + + {{ $t('settings.useStreamingApiWarning') }} + +
    +
  • {{ $t('settings.autoload') }} diff --git a/src/i18n/en.json b/src/i18n/en.json index 85146ef5..60fc792f 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -358,6 +358,8 @@ "post_status_content_type": "Post status content type", "stop_gifs": "Play-on-hover GIFs", "streaming": "Enable automatic streaming of new posts when scrolled to the top", + "useStreamingApi": "Receive posts and notifications real-time", + "useStreamingApiWarning": "(Not recommended, experimental, known to skip posts)", "text": "Text", "theme": "Theme", "theme_help": "Use hex color codes (#rrggbb) to customize your color theme.", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 19e10f1e..4cb2d497 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -219,6 +219,8 @@ "subject_input_always_show": "Всегда показывать поле ввода темы", "stop_gifs": "Проигрывать GIF анимации только при наведении", "streaming": "Включить автоматическую загрузку новых сообщений при прокрутке вверх", + "useStreamingApi": "Получать сообщения и уведомления в реальном времени", + "useStreamingApiWarning": "(Не рекомендуется, экспериментально, сообщения могут пропадать)", "text": "Текст", "theme": "Тема", "theme_help": "Используйте шестнадцатеричные коды цветов (#rrggbb) для настройки темы.", diff --git a/src/modules/api.js b/src/modules/api.js index 593f8498..dc91d00e 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -31,6 +31,18 @@ const api = { } }, 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 + dispatch('startMastoUserSocket') + }, + disableMastoSockets (store) { + const { state, dispatch } = store + if (!state.mastoUserSocket) return + dispatch('stopMastoUserSocket') + }, + // MastoAPI 'User' sockets startMastoUserSocket (store) { const { state, dispatch } = store @@ -81,6 +93,12 @@ const api = { dispatch('stopFetchingNotifications') }) }, + stopMastoUserSocket ({ state, dispatch }) { + dispatch('startFetchingTimeline', { timeline: 'friends' }) + dispatch('startFetchingNotifications') + console.log(state.mastoUserSocket) + state.mastoUserSocket.close() + }, // Timelines startFetchingTimeline (store, { diff --git a/src/modules/config.js b/src/modules/config.js index 329b4091..74025db1 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -35,6 +35,7 @@ export const defaultState = { highlight: {}, interfaceLanguage: browserLocale, hideScopeNotice: false, + useStreamingApi: false, scopeCopy: undefined, // instance default subjectLineBehavior: undefined, // instance default alwaysShowSubjectInput: undefined, // instance default diff --git a/src/modules/users.js b/src/modules/users.js index 6bdaf193..cbec6063 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -469,14 +469,22 @@ const users = { store.dispatch('initializeSocket') } - store.dispatch('startMastoUserSocket').catch((error) => { - console.error('Failed initializing MastoAPI Streaming socket', error) + const startPolling = () => { // Start getting fresh posts. store.dispatch('startFetchingTimeline', { timeline: 'friends' }) // Start fetching notifications store.dispatch('startFetchingNotifications') - }) + } + + if (store.getters.mergedConfig.useStreamingApi) { + store.dispatch('enableMastoSockets').catch((error) => { + console.error('Failed initializing MastoAPI Streaming socket', error) + startPolling() + }) + } else { + startPolling() + } // Get user mutes store.dispatch('fetchMutes') diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index c6818df4..517b953e 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -983,18 +983,24 @@ export const ProcessedWS = ({ wsEvent ) }) + // Commented code reason: very spammy, uncomment to enable message debug logging + /* socket.addEventListener('message', (wsEvent) => { console.debug( `[WS][${id}] Message received`, wsEvent ) }) + /**/ proxy(socket, 'open') proxy(socket, 'close') proxy(socket, 'message', preprocessor) proxy(socket, 'error') + // 1000 = Normal Closure + eventTarget.close = () => { socket.close(1000, 'Shutting down socket') } + return eventTarget } -- cgit v1.2.3-70-g09d2 From c3e7806acbd32483eab497a347f947158ab8febf Mon Sep 17 00:00:00 2001 From: kPherox Date: Wed, 11 Dec 2019 18:48:18 +0900 Subject: remove unused fallback --- src/components/notification/notification.js | 16 ++-------------- src/modules/users.js | 2 ++ 2 files changed, 4 insertions(+), 14 deletions(-) (limited to 'src/modules/users.js') diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 93edf2fa..e7bd769e 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -43,26 +43,14 @@ const Notification = { const user = this.notification.from_profile return highlightStyle(highlight[user.screen_name]) }, - userInStore () { - return this.$store.getters.findUser(this.notification.from_profile.id) - }, user () { - if (this.userInStore) { - return this.userInStore - } - return this.notification.from_profile + return this.$store.getters.findUser(this.notification.from_profile.id) }, userProfileLink () { return this.generateUserProfileLink(this.user) }, - targetUserInStore () { - return this.$store.getters.findUser(this.notification.target.id) - }, targetUser () { - if (this.targetUserInStore) { - return this.targetUserInStore - } - return this.notification.target + return this.$store.getters.findUser(this.notification.target.id) }, targetUserProfileLink () { return this.generateUserProfileLink(this.targetUser) diff --git a/src/modules/users.js b/src/modules/users.js index 14b2d8b5..f209b23b 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -368,8 +368,10 @@ const users = { }, addNewNotifications (store, { notifications }) { const users = map(notifications, 'from_profile') + const targetUsers = map(notifications, 'target') const notificationIds = notifications.map(_ => _.id) store.commit('addNewUsers', users) + store.commit('addNewUsers', targetUsers) const notificationsObject = store.rootState.statuses.notifications.idStore const relevantNotifications = Object.entries(notificationsObject) -- cgit v1.2.3-70-g09d2 From 585702b1cedf25547ff5faf0170263088e5484a6 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 12 Dec 2019 18:53:36 +0200 Subject: fix desktop notifications not working with streaming --- src/modules/users.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/modules/users.js') diff --git a/src/modules/users.js b/src/modules/users.js index cbec6063..5dc2f36f 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -481,6 +481,8 @@ const users = { store.dispatch('enableMastoSockets').catch((error) => { console.error('Failed initializing MastoAPI Streaming socket', error) startPolling() + }).then(() => { + setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000) }) } else { startPolling() -- cgit v1.2.3-70-g09d2 From 36376ce57c93c81317b6a8b0b50699d6b8488f57 Mon Sep 17 00:00:00 2001 From: taehoon Date: Mon, 18 Nov 2019 20:42:10 -0500 Subject: use vuex action --- src/components/moderation_tools/moderation_tools.js | 7 +------ src/modules/users.js | 10 ++++++++-- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src/modules/users.js') diff --git a/src/components/moderation_tools/moderation_tools.js b/src/components/moderation_tools/moderation_tools.js index 10a20709..02b92fef 100644 --- a/src/components/moderation_tools/moderation_tools.js +++ b/src/components/moderation_tools/moderation_tools.js @@ -71,12 +71,7 @@ const ModerationTools = { } }, toggleActivationStatus () { - const store = this.$store - const status = !!this.user.deactivated - store.state.api.backendInteractor.toggleActivationStatus(this.user).then(response => { - if (!response.ok) { return } - store.commit('updateActivationStatus', { user: this.user, status: status }) - }) + this.$store.dispatch('toggleActivationStatus', this.user) }, deleteUserDialog (show) { this.showDeleteUserDialog = show diff --git a/src/modules/users.js b/src/modules/users.js index 14b2d8b5..ec2ef608 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -95,9 +95,9 @@ export const mutations = { newRights[right] = value set(user, 'rights', newRights) }, - updateActivationStatus (state, { user: { id }, status }) { + updateActivationStatus (state, { user: { id }, deactivated }) { const user = state.usersObject[id] - set(user, 'deactivated', !status) + set(user, 'deactivated', deactivated) }, setCurrentUser (state, user) { state.lastLoginName = user.screen_name @@ -331,6 +331,12 @@ const users = { return rootState.api.backendInteractor.unsubscribeUser(id) .then((relationship) => commit('updateUserRelationship', [relationship])) }, + toggleActivationStatus ({ rootState, commit }, user) { + rootState.api.backendInteractor.toggleActivationStatus(user) + .then(response => { + commit('updateActivationStatus', { user, deactivated: response.deactivated }) + }) + }, registerPushNotifications (store) { const token = store.state.currentUser.credentials const vapidPublicKey = store.rootState.instance.vapidPublicKey -- cgit v1.2.3-70-g09d2 From 4e4c4af422c400d016e4605f8bcf699f7154a8b4 Mon Sep 17 00:00:00 2001 From: taehoon Date: Tue, 19 Nov 2019 14:41:39 -0500 Subject: toggle_activation api is also deprecated --- src/modules/users.js | 7 +++-- src/services/api/api.service.js | 31 +++++++++++++++++----- .../backend_interactor_service.js | 12 ++++++--- 3 files changed, 37 insertions(+), 13 deletions(-) (limited to 'src/modules/users.js') diff --git a/src/modules/users.js b/src/modules/users.js index ec2ef608..82d3c4e8 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -332,10 +332,9 @@ const users = { .then((relationship) => commit('updateUserRelationship', [relationship])) }, toggleActivationStatus ({ rootState, commit }, user) { - rootState.api.backendInteractor.toggleActivationStatus(user) - .then(response => { - commit('updateActivationStatus', { user, deactivated: response.deactivated }) - }) + const api = user.deactivated ? rootState.api.backendInteractor.activateUser : rootState.api.backendInteractor.deactivateUser + api(user) + .then(({ deactivated }) => commit('updateActivationStatus', { user, deactivated })) }, registerPushNotifications (store) { const token = store.state.currentUser.credentials diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index b739ec1f..a69fa53c 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -1,4 +1,4 @@ -import { each, map, concat, last } from 'lodash' +import { each, map, concat, last, get } from 'lodash' import { parseStatus, parseUser, parseNotification, parseAttachment } from '../entity_normalizer/entity_normalizer.service.js' import 'whatwg-fetch' import { RegistrationError, StatusCodeError } from '../errors/errors' @@ -12,7 +12,8 @@ const CHANGE_EMAIL_URL = '/api/pleroma/change_email' const CHANGE_PASSWORD_URL = '/api/pleroma/change_password' const TAG_USER_URL = '/api/pleroma/admin/users/tag' const PERMISSION_GROUP_URL = (screenName, right) => `/api/pleroma/admin/users/${screenName}/permission_group/${right}` -const TOGGLE_ACTIVATION_URL = screenName => `/api/pleroma/admin/users/${screenName}/toggle_activation` +const ACTIVATE_USER_URL = '/api/pleroma/admin/users/activate' +const DEACTIVATE_USER_URL = '/api/pleroma/admin/users/deactivate' const ADMIN_USERS_URL = '/api/pleroma/admin/users' const SUGGESTIONS_URL = '/api/v1/suggestions' const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings' @@ -450,9 +451,26 @@ const deleteRight = ({ right, credentials, ...user }) => { }) } -// eslint-disable-next-line camelcase -const toggleActivationStatus = ({ credentials, screen_name }) => { - return promisedRequest({ url: TOGGLE_ACTIVATION_URL(screen_name), method: 'PATCH', credentials }) +const activateUser = ({ credentials, screen_name: nickname }) => { + return promisedRequest({ + url: ACTIVATE_USER_URL, + method: 'PATCH', + credentials, + payload: { + nicknames: [nickname] + } + }).then(response => get(response, 'users.0')) +} + +const deactivateUser = ({ credentials, screen_name: nickname }) => { + return promisedRequest({ + url: DEACTIVATE_USER_URL, + method: 'PATCH', + credentials, + payload: { + nicknames: [nickname] + } + }).then(response => get(response, 'users.0')) } const deleteUser = ({ credentials, ...user }) => { @@ -968,7 +986,8 @@ const apiService = { deleteUser, addRight, deleteRight, - toggleActivationStatus, + activateUser, + deactivateUser, register, getCaptcha, updateAvatar, diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index e0a15d3b..3d4ec6ac 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -89,8 +89,13 @@ const backendInteractorService = credentials => { } // eslint-disable-next-line camelcase - const toggleActivationStatus = ({ screen_name }) => { - return apiService.toggleActivationStatus({ screen_name, credentials }) + const activateUser = ({ screen_name }) => { + return apiService.activateUser({ screen_name, credentials }) + } + + // eslint-disable-next-line camelcase + const deactivateUser = ({ screen_name }) => { + return apiService.deactivateUser({ screen_name, credentials }) } // eslint-disable-next-line camelcase @@ -191,7 +196,8 @@ const backendInteractorService = credentials => { addRight, deleteRight, deleteUser, - toggleActivationStatus, + activateUser, + deactivateUser, register, getCaptcha, updateAvatar, -- cgit v1.2.3-70-g09d2 From 662afe973a72efc17e61fd4c071ed985bfe4bf6b Mon Sep 17 00:00:00 2001 From: Shpuld Shpludson Date: Tue, 14 Jan 2020 13:45:00 +0000 Subject: Fix #750 , fix error messages and captcha resetting --- CHANGELOG.md | 2 ++ src/components/registration/registration.js | 3 ++- src/components/registration/registration.vue | 2 +- src/modules/users.js | 4 +++- src/services/errors/errors.js | 14 ++++++++++---- 5 files changed, 18 insertions(+), 7 deletions(-) (limited to 'src/modules/users.js') diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f5a9305..6dcbc7da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] ### Changed +- Captcha now resets on failed registrations - Notifications column now cleans itself up to optimize performance when tab is left open for a long time ### Fixed - Single notifications left unread when hitting read on another device/tab +- Registration fixed ## [1.1.8] - 2020-01-10 ### Added diff --git a/src/components/registration/registration.js b/src/components/registration/registration.js index 57f3caf0..ace8cc7c 100644 --- a/src/components/registration/registration.js +++ b/src/components/registration/registration.js @@ -63,7 +63,8 @@ const registration = { await this.signUp(this.user) this.$router.push({ name: 'friends' }) } catch (error) { - console.warn('Registration failed: ' + error) + console.warn('Registration failed: ', error) + this.setCaptcha() } } }, diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue index 222b67a8..fdbda007 100644 --- a/src/components/registration/registration.vue +++ b/src/components/registration/registration.vue @@ -170,7 +170,7 @@ + >{{ $t('registration.captcha') }}