webapp/src/context/connect.tsx

90 lines
2.7 KiB
TypeScript
Raw Normal View History

2023-11-28 13:18:25 +00:00
import type { Accessor, JSX } from 'solid-js'
import { fetchEventSource } from '@microsoft/fetch-event-source'
import { createContext, useContext, createSignal, createEffect } from 'solid-js'
2023-11-28 15:36:00 +00:00
2023-11-28 13:18:25 +00:00
import { useSession } from './session'
2023-12-03 10:22:42 +00:00
import { useAuthorizer } from './authorizer'
2023-11-28 13:18:25 +00:00
export interface SSEMessage {
id: string
entity: string
action: string
payload: any // Author | Shout | Reaction | Message
created_at?: number // unixtime x1000
seen?: boolean
}
type MessageHandler = (m: SSEMessage) => void
export interface ConnectContextType {
addHandler: (handler: MessageHandler) => void
connected: Accessor<boolean>
}
const ConnectContext = createContext<ConnectContextType>()
export const ConnectProvider = (props: { children: JSX.Element }) => {
const [messageHandlers, setHandlers] = createSignal<Array<MessageHandler>>([])
// const [messages, setMessages] = createSignal<Array<SSEMessage>>([]);
const [connected, setConnected] = createSignal(false)
2023-12-03 10:22:42 +00:00
const {
isAuthenticated,
actions: { getToken },
} = useSession()
2023-11-28 13:18:25 +00:00
const addHandler = (handler: MessageHandler) => {
setHandlers((hhh) => [...hhh, handler])
}
const [retried, setRetried] = createSignal<number>(0)
2023-11-28 13:18:25 +00:00
const listen = () => {
if (isAuthenticated() && !connected() && retried() < 4) {
try {
fetchEventSource('https://connect.discours.io', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: getToken(),
},
onmessage(event) {
const m: SSEMessage = JSON.parse(event.data)
console.log('[context.connect] Received message:', m)
2023-11-28 13:18:25 +00:00
// Iterate over all registered handlers and call them
messageHandlers().forEach((handler) => handler(m))
},
onclose() {
console.log('[context.connect] sse connection closed by server')
setConnected(false)
},
onerror(err) {
console.error('[context.connect] sse connection closed by error', err)
setConnected(false)
throw new Error(err) // NOTE: simple hack to close the connection
},
})
} catch (err) {
console.error(err)
setRetried((r) => r + 1)
if (retried() + 1 > 3) {
console.warn('not trying to reconnect anymore, listen() should be called again')
}
}
2023-11-28 13:18:25 +00:00
}
}
createEffect(() => {
if (isAuthenticated() && !connected()) {
listen()
setConnected(true)
}
})
const value: ConnectContextType = { addHandler, connected }
return <ConnectContext.Provider value={value}>{props.children}</ConnectContext.Provider>
}
export const useConnect = () => useContext(ConnectContext)