diff options
| author | Mel <einebeere@gmail.com> | 2022-02-19 19:47:44 +0100 |
|---|---|---|
| committer | Mel <einebeere@gmail.com> | 2022-02-19 19:58:33 +0100 |
| commit | 5384c34952b031995ecb8aa58d72954b0c685e18 (patch) | |
| tree | ba7c9dfe77d77e5c4bce03c60d08f056f40235c0 /assets/src | |
| parent | d5f7201eb7b10826e77eccb33f9cca784261091f (diff) | |
| download | rook-5384c34952b031995ecb8aa58d72954b0c685e18.tar.zst rook-5384c34952b031995ecb8aa58d72954b0c685e18.zip | |
Re-architect event handler for simplicity
Diffstat (limited to 'assets/src')
| -rw-r--r-- | assets/src/network/channel/messages/event_handler.ts | 166 | ||||
| -rw-r--r-- | assets/src/network/channel/messages/message_handler.ts | 25 | ||||
| -rw-r--r-- | assets/src/network/channel/messages/messages.ts | 5 |
3 files changed, 25 insertions, 171 deletions
diff --git a/assets/src/network/channel/messages/event_handler.ts b/assets/src/network/channel/messages/event_handler.ts deleted file mode 100644 index d0936e0..0000000 --- a/assets/src/network/channel/messages/event_handler.ts +++ /dev/null @@ -1,166 +0,0 @@ -import type { Channel } from "phoenix"; -import type { - AnyMessage, - EventName, - MessageForEvent, - TokenizedMessage, -} from "./messages"; - -// The single handler for all events, which is used to dispatch to the correct -// handler for each event and token. -// Every event can only have either one single handler, or multiple handlers for different tokens. -export type EventHandler = { - [EN in EventName]?: HandlerForMessage<MessageForEvent<EN>>; -}; - -// A handler for a specific event and message. -// A message handler can either be a single handler or can have multiple handlers for different tokens -type HandlerForMessage<M extends AnyMessage> = - // A single handler - | { type: "single"; handler: HandlerFn<M> } - // A group of handlers for different tokens - // Can only be used for messages which have a "token" field. - | (M extends TokenizedMessage - ? { - type: "token"; - handler: HandlerForTokenizedMessage<M>; - } - : never); - -// A map of token to handler for a specific event. -export type HandlerForTokenizedMessage<M extends TokenizedMessage> = Map< - string, - HandlerFn<M> ->; - -// A function which handles a message for a specific event. -export type HandlerFn<M extends AnyMessage> = (message?: M) => void; - -// A function that unregisters a single event handler. -export type UnregisterFn = () => void; - -// Adds a single handler for a specific event. -export function registerHandler<M extends AnyMessage>( - eventHandler: EventHandler, - channel: Channel, - event: M["event_name"], - handler: HandlerFn<M> -) { - const messageHandler: HandlerForMessage<M> = { - type: "single", - handler, - }; - - let unregisterChannelEvent: UnregisterFn | null = null; - if (typeof eventHandler[event] === "undefined") { - // Register a new event handler, since this is the first handler for this event - unregisterChannelEvent = registerNewEvent<M>( - channel, - messageHandler, - event - ); - } else { - throw new Error("Event already has a handler attached to it."); - } - - // TODO: Check if we there is a possibility for TS to accept this. - // Technically this should work, because TS is afraid of the generic type parameter - // not matching the message type of the handler. But it should match, since - // the handler should accept only the exact message type from which the event_name was derived. - // @ts-ignore - eventHandler[event] = messageHandler; - - // This could cause problems if we would allow to redefine the handler, - // as that would cause the Unsubscribe function to no longer apply to this specific handler, - // but as we throw an error on redefinition, this should be fine. - return () => { - delete eventHandler[event]; - - // If we registered a new event on the channel, we need to unregister it - if (unregisterChannelEvent !== null) { - unregisterChannelEvent(); - } - }; -} - -export function registerHandlerForSpecificToken<M extends TokenizedMessage>( - eventHandler: EventHandler, - channel: Channel, - event: M["event_name"], - token: string, - handler: HandlerFn<M> -): UnregisterFn { - const messageHandler = eventHandler[event]; - - if (typeof messageHandler === "undefined") { - // TODO: Same as above, this should probably be valid. - // @ts-ignore - eventHandler[event] = { - type: "token", - handler: new Map<string, HandlerFn<M>>(), - }; - // @ts-ignore - registerNewEvent<M>(channel, eventHandler[event], event); - } else if ( - messageHandler.type === "token" && - messageHandler.handler.size === 0 - ) { - // If there is already a token handler with no token, we need to register the event again - // @ts-ignore - registerNewEvent<M>(channel, messageHandler, event); - } else if (messageHandler.type === "single") { - throw new Error("Event already has a handler attached to it."); - } - - // @ts-ignore This shoudl be valid, as we derive the event name from the message type. - const tokenHandler: HandlerForTokenizedMessage<M> = - eventHandler[event].handler; - tokenHandler.set(token, handler); - - return () => { - // Unregister the handler for this specific token - tokenHandler.delete(token); - // If there are no more handlers for this event, we can unregister the event - if (tokenHandler.size === 0) { - // We should technically use the ref here, but we don't yet have a way to get it, as the event - // could have been registered outside of this function call. - channel.off(event); - } - }; -} - -// Adds a callback for a new event -function registerNewEvent<M extends AnyMessage>( - channel: Channel, - messageHandler: HandlerForMessage<M>, - event: M["event_name"] -): UnregisterFn { - const callback = (data: M) => { - // Add event_name to message, so the type definitions match - const message = { event_name: event, ...data }; - onEvent<M>(messageHandler, message); - }; - - const ref = channel.on(event, callback); - - return () => channel.off(event, ref); -} - -function onEvent<M extends AnyMessage>( - messageHandler: HandlerForMessage<M>, - message: M -) { - if (messageHandler.type === "token") { - const token: string = message["token"]; - - const handler = messageHandler.handler.get(token); - - if (typeof handler !== "undefined") { - (handler as HandlerFn<M>)(message); - } else { - console.warn(`Received message for unknown token: ${token}`); - } - } else { - messageHandler.handler(message); - } -} diff --git a/assets/src/network/channel/messages/message_handler.ts b/assets/src/network/channel/messages/message_handler.ts new file mode 100644 index 0000000..2aefa1d --- /dev/null +++ b/assets/src/network/channel/messages/message_handler.ts @@ -0,0 +1,25 @@ +import type { AnyMessage, RequestMessage, ShareMessage } from "./messages"; + +export type HandlerFn<Message> = (message: Message) => void; + +export type MessageHandler<Messages extends AnyMessage> = { + [M in Messages as M["event_name"]]?: HandlerFn<M>; +}; + +export type RequestMessageHandler = MessageHandler<RequestMessage>; +export type ShareMessageHandler = MessageHandler<ShareMessage>; + +const defaultHandlerFn: HandlerFn<AnyMessage> = m => { + console.error( + `Received unknown event "${m.event_name}": ${JSON.stringify(m)}` + ); +}; + +export function routeEventToHandler( + event: string, + message: any, + handlers: MessageHandler<AnyMessage> +): void { + const handler = handlers[event] || defaultHandlerFn; + handler(message); +} diff --git a/assets/src/network/channel/messages/messages.ts b/assets/src/network/channel/messages/messages.ts index 24380ae..7eee4a8 100644 --- a/assets/src/network/channel/messages/messages.ts +++ b/assets/src/network/channel/messages/messages.ts @@ -1,11 +1,6 @@ export type AnyMessage = ShareMessage | RequestMessage; export type EventName = AnyMessage["event_name"]; -export type MessageForEvent<EN> = Extract<AnyMessage, { event_name: EN }>; - -export type TokenizedMessage = { - token: string; -} & AnyMessage; // Messages for the sharer |
