|
|
|
@ -6,9 +6,11 @@ |
|
|
|
import { Middleware } from "redux"; |
|
|
|
|
|
|
|
import { |
|
|
|
SocketMessageDataHandler, |
|
|
|
SocketMessageHandler, |
|
|
|
parseMessage, |
|
|
|
serializeMessage, |
|
|
|
SocketMessage, |
|
|
|
SocketMessageMiddleware, |
|
|
|
SocketMessageSender, |
|
|
|
} from "./message"; |
|
|
|
import { |
|
|
|
socketOpen, |
|
|
|
@ -22,6 +24,13 @@ import { AppDispatch, RootState } from "../../app/store"; |
|
|
|
// The WebSocket singleton.
|
|
|
|
let socket: WebSocket | undefined; |
|
|
|
|
|
|
|
function socketMessageSender(socket: WebSocket): SocketMessageSender { |
|
|
|
return (message: SocketMessage) => { |
|
|
|
console.log("WebSocket: sending message", message); |
|
|
|
socket.send(serializeMessage(message)); |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
const makeOnOpen = (dispatch: AppDispatch) => (event: Event) => { |
|
|
|
const target = event.target as WebSocket; |
|
|
|
console.log("WebSocket open", target.url); |
|
|
|
@ -32,28 +41,11 @@ const makeOnClose = (dispatch: AppDispatch) => () => { |
|
|
|
dispatch(socketClosed()); |
|
|
|
}; |
|
|
|
|
|
|
|
type HandlerMap = Map<string, SocketMessageDataHandler[]>; |
|
|
|
|
|
|
|
function prepareHandlers(handlers: SocketMessageHandler[]): HandlerMap { |
|
|
|
const map = new Map(); |
|
|
|
|
|
|
|
for (const handler of handlers) { |
|
|
|
console.log(`Registering handler for message variant ${handler.variant}`); |
|
|
|
|
|
|
|
let list = map.get(handler.variant); |
|
|
|
if (list === undefined) { |
|
|
|
list = []; |
|
|
|
map.set(handler.variant, list); |
|
|
|
} |
|
|
|
|
|
|
|
list.push(handler.handleData); |
|
|
|
} |
|
|
|
|
|
|
|
return map; |
|
|
|
} |
|
|
|
|
|
|
|
const makeOnMessage = |
|
|
|
(dispatch: AppDispatch, handlers: HandlerMap) => (event: MessageEvent) => { |
|
|
|
const makeOnMessage = ( |
|
|
|
dispatch: AppDispatch, |
|
|
|
middlewares: SocketMessageMiddleware[] |
|
|
|
) => { |
|
|
|
return (event: MessageEvent) => { |
|
|
|
console.log("WebSocket received message", event.data); |
|
|
|
|
|
|
|
let message; |
|
|
|
@ -64,28 +56,21 @@ const makeOnMessage = |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const list = handlers.get(message.variant); |
|
|
|
if (list === undefined) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
for (const handler of list) { |
|
|
|
const action = handler(message); |
|
|
|
if (action !== undefined) { |
|
|
|
dispatch(action); |
|
|
|
} |
|
|
|
for (const middleware of middlewares) { |
|
|
|
middleware.handleMessage(dispatch, message); |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
type SocketFactory = (url: string) => WebSocket; |
|
|
|
|
|
|
|
function makeSocketFactory( |
|
|
|
dispatch: AppDispatch, |
|
|
|
handlers: HandlerMap |
|
|
|
middlewares: SocketMessageMiddleware[] |
|
|
|
): SocketFactory { |
|
|
|
const onOpen = makeOnOpen(dispatch); |
|
|
|
const onClose = makeOnClose(dispatch); |
|
|
|
const onMessage = makeOnMessage(dispatch, handlers); |
|
|
|
const onMessage = makeOnMessage(dispatch, middlewares); |
|
|
|
|
|
|
|
return (url) => { |
|
|
|
socket = new WebSocket(url); |
|
|
|
@ -101,12 +86,13 @@ function makeSocketFactory( |
|
|
|
|
|
|
|
// See: https://redux.js.org/tutorials/fundamentals/part-4-store#writing-custom-middleware
|
|
|
|
function makeMiddleware( |
|
|
|
handlers: SocketMessageHandler[] |
|
|
|
messageMiddlewares: SocketMessageMiddleware[] |
|
|
|
): Middleware<{}, RootState> { |
|
|
|
const handlerMap = prepareHandlers(handlers); |
|
|
|
|
|
|
|
return (storeApi) => { |
|
|
|
const socketFactory = makeSocketFactory(storeApi.dispatch, handlerMap); |
|
|
|
const socketFactory = makeSocketFactory( |
|
|
|
storeApi.dispatch, |
|
|
|
messageMiddlewares |
|
|
|
); |
|
|
|
|
|
|
|
return (next) => (action) => { |
|
|
|
if (socketOpen.match(action)) { |
|
|
|
@ -128,10 +114,14 @@ function makeMiddleware( |
|
|
|
} else if (socketSendMessage.match(action)) { |
|
|
|
if (socket !== undefined) { |
|
|
|
console.log("WebSocket sending message", action.payload); |
|
|
|
socket.send(JSON.stringify(action.payload)); |
|
|
|
socket.send(serializeMessage(action.payload)); |
|
|
|
} else { |
|
|
|
console.log("Ignoring socketSendMessage action, socket is closed."); |
|
|
|
} |
|
|
|
} else if (socket !== undefined) { |
|
|
|
for (const middleware of messageMiddlewares) { |
|
|
|
middleware.handleAction(socketMessageSender(socket), action); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return next(action); |
|
|
|
|