<commit_before>/*
 * Copyright (C) 2012-2022  Online-Go.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import * as React from "react";
import { socket } from "sockets";

interface UIPushProperties {
    event: string;
    channel?: string;
    action: (data: any) => any;
}

class Handler {
    id: number;
    event: string;
    cb: (data, event?) => void;
}

let last_handler_id = 0;
class UIPushManager {
    handlers: {
        [id: string]: Array<Handler>;
    };
    subscriptions: {
        [id: string]: number;
    };

    constructor() {
        this.handlers = {};
        this.subscriptions = {};

        socket.on("ui-push", (msg) => {
            if (msg.event in this.handlers) {
                for (const handler of this.handlers[msg.event]) {
                    handler.cb(msg.data, msg.event);
                }
            }
        });
        socket.on("connect", () => {
            /* handle resubscriptions */
            for (const channel in this.subscriptions) {
                socket.send("ui-pushes/subscribe", { channel: channel });
            }
        });
    }

    on(event, cb) {
        const handler = new Handler();
        (handler.id = ++last_handler_id), (handler.event = event);
        handler.cb = cb;

        if (!(event in this.handlers)) {
            this.handlers[event] = [];
        }
        this.handlers[event].push(handler);
        return handler;
    }

    off(handler) {
        for (let i = 0; i < this.handlers[handler.event].length; ++i) {
            if (this.handlers[handler.event][i].id === handler.id) {
                this.handlers[handler.event].splice(i, 1);
                return;
            }
        }
    }

    subscribe(channel) {
        if (channel in this.subscriptions) {
            this.subscriptions[channel]++;
        } else {
            this.subscriptions[channel] = 1;
            if ((socket as any).connected) {
                socket.send("ui-pushes/subscribe", { channel: channel });
            }
        }
    }
    unsubscribe(channel) {
        if (this.subscriptions[channel] > 1) {
            this.subscriptions[channel]--;
        } else {
            delete this.subscriptions[channel];
            if ((socket as any).connected) {
                socket.send("ui-pushes/unsubscribe", { channel: channel });
            }
        }
    }
}

export const push_manager = new UIPushManager();

export class UIPush extends React.Component<UIPushProperties> {
    handler: Handler = null;
    channel: string = null; // I'm here

    constructor(props) {
        super(props);
    }

    shouldComponentUpdate(next) {
        if (
            this.props.event === next.event &&
            this.props.action === next.action &&
            this.props.channel === next.channel
        ) {
            return false;
        }
        return true;
    }

    removeHandler() {
        if (this.handler) {
            push_manager.off(this.handler);
            this.handler = null;
        }
    }
    unsubscribe() {
        if (this.channel && this.channel !== "undefined") {
            push_manager.unsubscribe(this.channel);
            this.channel = null;
        }
    }

    sync() {
        if (this.handler) {
            this.removeHandler();
        }
        if (this.props.event) {
            this.handler = push_manager.on(this.props.event, this.props.action);
        }

        if (this.props.channel !== this.channel) {
            this.unsubscribe();
            this.channel = this.props.channel;

            if (this.channel && this.channel !== "undefined") {
                push_manager.subscribe(this.channel);
            }
        }
    }

    componentDidUpdate() {
        this.sync();
    }
    componentDidMount() {
        this.sync();
    }
    componentWillUnmount() {
        this.removeHandler();
        this.unsubscribe();
    }

    render() {
        return null;
    }
}<commit_msg>Hookify UIPush<commit_after>/*
 * Copyright (C) 2012-2022  Online-Go.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import * as React from "react";
import { socket } from "sockets";

interface UIPushProperties {
    event: string;
    channel?: string;
    action: (data: any) => any;
}

class Handler {
    id: number;
    event: string;
    cb: (data, event?) => void;
}

let last_handler_id = 0;
class UIPushManager {
    handlers: {
        [id: string]: Array<Handler>;
    };
    subscriptions: {
        [id: string]: number;
    };

    constructor() {
        this.handlers = {};
        this.subscriptions = {};

        socket.on("ui-push", (msg) => {
            if (msg.event in this.handlers) {
                for (const handler of this.handlers[msg.event]) {
                    handler.cb(msg.data, msg.event);
                }
            }
        });
        socket.on("connect", () => {
            /* handle resubscriptions */
            for (const channel in this.subscriptions) {
                socket.send("ui-pushes/subscribe", { channel: channel });
            }
        });
    }

    on(event, cb) {
        const handler = new Handler();
        (handler.id = ++last_handler_id), (handler.event = event);
        handler.cb = cb;

        if (!(event in this.handlers)) {
            this.handlers[event] = [];
        }
        this.handlers[event].push(handler);
        return handler;
    }

    off(handler) {
        for (let i = 0; i < this.handlers[handler.event].length; ++i) {
            if (this.handlers[handler.event][i].id === handler.id) {
                this.handlers[handler.event].splice(i, 1);
                return;
            }
        }
    }

    subscribe(channel) {
        if (channel in this.subscriptions) {
            this.subscriptions[channel]++;
        } else {
            this.subscriptions[channel] = 1;
            if ((socket as any).connected) {
                socket.send("ui-pushes/subscribe", { channel: channel });
            }
        }
    }
    unsubscribe(channel) {
        if (this.subscriptions[channel] > 1) {
            this.subscriptions[channel]--;
        } else {
            delete this.subscriptions[channel];
            if ((socket as any).connected) {
                socket.send("ui-pushes/unsubscribe", { channel: channel });
            }
        }
    }
}

export const push_manager = new UIPushManager();

export function UIPush({ event, channel, action }: UIPushProperties): JSX.Element {
    React.useEffect(() => {
        if (event && action) {
            const handler = push_manager.on(event, action);
            return () => {
                push_manager.off(handler);
            };
        }
    }, [event, action]);

    React.useEffect(() => {
        if (channel) {
            push_manager.subscribe(channel);
            return () => {
                push_manager.unsubscribe(channel);
            };
        }
    }, [channel]);

    return null;
}
{'fraction_non_alphanumeric': 0.12492345376607471, 'fraction_numerical': 0.003429271279853031, 'mean_word_length': 1.8403478260869566, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4490884', 'n_tokens_mistral': 2327, 'n_tokens_neox': 2252, 'n_words': 593}