about summary refs log tree commit diff
path: root/assets/src/network/channel/messages/handler.ts
blob: cc8f005de16b27ea0098d7c9f762b82870d218bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import type { Channel } from "phoenix";
import type { AnyMessage } from "./messages";

export type Handlers = {
    [event: string]: EventHandler<unknown>;
};

type EventHandler<Message extends AnyMessage> = {
    tokenHandler: TokenHandler<Message>;
    directHandlers: Handler<Message>[];
};

export type TokenHandler<Message extends AnyMessage> = {
    [token: string]: Handler<Message>;
};

export type Handler<Message extends AnyMessage> = (
    message?: Message,
    unregister?: UnregisterHandler
) => void;

export type UnregisterHandler = () => void;

export function registerTokenHandler<Message extends AnyMessage>(
    handlers: Handlers,
    channel: Channel,
    event: string,
    token: string | null,
    handler: Handler<Message>
): UnregisterHandler {
    let eventHandler = handlers[event];

    // If this event did not yet have any handlers registered we have to register it
    if (eventHandler === undefined) {
        eventHandler = {
            tokenHandler: {},
            directHandlers: [],
        };

        handlers[event] = eventHandler;

        registerNewEvent<Message>(channel, eventHandler, event);
    }

    let unregister: UnregisterHandler;

    if (token === null) {
        const directHandlers = eventHandler.directHandlers;
        directHandlers.push(handler);

        unregister = makeDirectUnregister(directHandlers, handler);
    } else {
        const tokenHandler = eventHandler.tokenHandler;
        tokenHandler[token] = handler;

        unregister = makeTokenUnregister(tokenHandler, token);
    }

    return unregister;
}

function registerNewEvent<Message extends AnyMessage>(
    channel: Channel,
    eventHandler: EventHandler<Message>,
    event: string
) {
    const callback = (data: Message) => {
        handleEvent<Message>(eventHandler, data);
    };

    channel.on(event, callback);
}

function handleEvent<Message extends AnyMessage>(
    eventHandler: EventHandler<Message>,
    message: Message
) {
    if (message["token"] !== undefined) {
        const token = message["token"];

        const tokenHandler = eventHandler.tokenHandler;
        const handler: Handler<Message> = tokenHandler[token];

        if (handler !== undefined) {
            handler(message, makeTokenUnregister(tokenHandler, token));
        }
    }

    const directHandlers = eventHandler.directHandlers;
    for (const handler of directHandlers) {
        handler(message, makeDirectUnregister(directHandlers, handler));
    }
}

function makeDirectUnregister<Message extends AnyMessage>(
    directHandlers: Handler<Message>[],
    handler: Handler<Message>
): UnregisterHandler {
    return () => {
        const index = directHandlers.findIndex(h => h === handler);
        directHandlers.splice(index, 1);
    };
}

function makeTokenUnregister<Message extends AnyMessage>(
    tokenHandler: TokenHandler<Message>,
    token: string
): UnregisterHandler {
    return () => {
        delete tokenHandler[token];
    };
}