better wrapper for websocket

This commit is contained in:
Henry Jameson 2019-12-08 19:18:38 +02:00
parent 1b1620a755
commit 505fb26061
3 changed files with 69 additions and 35 deletions

View file

@ -34,10 +34,10 @@ const api = {
// MastoAPI 'User' sockets // MastoAPI 'User' sockets
startMastoUserSocket (store) { startMastoUserSocket (store) {
const { state, dispatch } = store const { state, dispatch } = store
state.mastoUserSocket = state.backendInteractor state.mastoUserSocket = state.backendInteractor.startUserSocket({ store })
.startUserSocket({ state.mastoUserSocket.addEventListener(
store, 'message',
onMessage: (message) => { ({ detail: message }) => {
if (!message) return // pings if (!message) return // pings
if (message.event === 'notification') { if (message.event === 'notification') {
dispatch('addNewNotifications', { dispatch('addNewNotifications', {
@ -53,17 +53,16 @@ const api = {
}) })
} }
} }
}) )
state.mastoUserSocket.addEventListener('error', error => { state.mastoUserSocket.addEventListener('error', ({ detail: error }) => {
console.error('Error in MastoAPI websocket:', error) console.error('Error in MastoAPI websocket:', error)
}) })
state.mastoUserSocket.addEventListener('close', closeEvent => { state.mastoUserSocket.addEventListener('close', ({ detail: closeEvent }) => {
const ignoreCodes = new Set([ const ignoreCodes = new Set([
1000, // Normal (intended) closure 1000, // Normal (intended) closure
1001 // Going away 1001 // Going away
]) ])
const { code } = closeEvent const { code } = closeEvent
console.debug('Socket closure event:', closeEvent)
if (ignoreCodes.has(code)) { if (ignoreCodes.has(code)) {
console.debug(`Not restarting socket becasue of closure code ${code} is in ignore list`) console.debug(`Not restarting socket becasue of closure code ${code} is in ignore list`)
} else { } else {

View file

@ -953,8 +953,52 @@ const MASTODON_STREAMING_EVENTS = new Set([
'filters_changed' 'filters_changed'
]) ])
// A thin wrapper around WebSocket API that allows adding a pre-processor to it
// Uses EventTarget and a CustomEvent to proxy events
export const ProcessedWS = ({
url,
preprocessor = handleMastoWS,
id = 'Unknown'
}) => {
const eventTarget = new EventTarget()
const socket = new WebSocket(url)
if (!socket) throw new Error(`Failed to create socket ${id}`)
const proxy = (original, eventName, processor = a => a) => {
original.addEventListener(eventName, (eventData) => {
eventTarget.dispatchEvent(new CustomEvent(
eventName,
{ detail: processor(eventData) }
))
})
}
socket.addEventListener('open', (wsEvent) => {
console.debug(`[WS][${id}] Socket connected`, wsEvent)
})
socket.addEventListener('error', (wsEvent) => {
console.debug(`[WS][${id}] Socket errored`, wsEvent)
})
socket.addEventListener('close', (wsEvent) => {
console.debug(
`[WS][${id}] Socket disconnected with code ${wsEvent.code}`,
wsEvent
)
})
socket.addEventListener('message', (wsEvent) => {
console.debug(
`[WS][${id}] Message received`,
wsEvent
)
})
proxy(socket, 'open')
proxy(socket, 'close')
proxy(socket, 'message', preprocessor)
proxy(socket, 'error')
return eventTarget
}
export const handleMastoWS = (wsEvent) => { export const handleMastoWS = (wsEvent) => {
console.debug('Event', wsEvent)
const { data } = wsEvent const { data } = wsEvent
if (!data) return if (!data) return
const parsedEvent = JSON.parse(data) const parsedEvent = JSON.parse(data)

View file

@ -1,4 +1,4 @@
import apiService, { getMastodonSocketURI, handleMastoWS } from '../api/api.service.js' import apiService, { getMastodonSocketURI, ProcessedWS } from '../api/api.service.js'
import timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js' import timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js'
import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js' import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js'
import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service' import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service'
@ -20,19 +20,10 @@ const backendInteractorService = credentials => ({
return followRequestFetcher.startFetching({ store, credentials }) return followRequestFetcher.startFetching({ store, credentials })
}, },
startUserSocket ({ store, onMessage }) { startUserSocket ({ store }) {
const serv = store.rootState.instance.server.replace('http', 'ws') const serv = store.rootState.instance.server.replace('http', 'ws')
const url = serv + getMastodonSocketURI({ credentials, stream: 'user' }) const url = serv + getMastodonSocketURI({ credentials, stream: 'user' })
const socket = new WebSocket(url) return ProcessedWS({ url, id: 'User' })
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('MastoApi User WebSocket Error:', error))
return socket
} else {
throw new Error('failed to connect to socket')
}
}, },
...Object.entries(apiService).reduce((acc, [key, func]) => { ...Object.entries(apiService).reduce((acc, [key, func]) => {