From 319bb4ac2895b8eb62da42e3f95addc9bb67b1a0 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 24 Nov 2019 18:50:28 +0200 Subject: [PATCH] initial streaming work --- src/modules/api.js | 15 +++++++ src/modules/users.js | 11 +++-- src/services/api/api.service.js | 40 +++++++++++++++++++ .../backend_interactor_service.js | 15 ++++++- 4 files changed, 76 insertions(+), 5 deletions(-) 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' }) + store.dispatch('startMastoSocket').catch((error) => { + console.error(error) + // Start getting fresh posts. + store.dispatch('startFetchingTimeline', { timeline: 'friends' }) - // Start fetching notifications - store.dispatch('startFetchingNotifications') + // 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,