From 69287ebbd1c716306989d9667a9feebd67faf642 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Mon, 28 Sep 2020 09:59:20 +0200 Subject: [PATCH] Reworked the favicon handler to prevent memory leakage due to react memorization --- web/app/ui/FaviconRenderer.tsx | 69 +++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/web/app/ui/FaviconRenderer.tsx b/web/app/ui/FaviconRenderer.tsx index c08f549c..964f205e 100644 --- a/web/app/ui/FaviconRenderer.tsx +++ b/web/app/ui/FaviconRenderer.tsx @@ -2,8 +2,8 @@ import * as loader from "tc-loader"; import {Stage} from "tc-loader"; import {ConnectionHandler, ConnectionState} from "tc-shared/ConnectionHandler"; import * as React from "react"; -import * as ReactDOM from "react-dom"; import {useState} from "react"; +import * as ReactDOM from "react-dom"; import {server_connections} from "tc-shared/ConnectionManager"; import { @@ -11,9 +11,18 @@ import { spriteEntries as kClientSpriteEntries, spriteUrl as kClientSpriteUrl } from "svg-sprites/client-icons"; +import {Registry} from "tc-shared/events"; + +interface FaviconEvents { + query_icon: {}, + notify_icon: { icon: ClientIcon | undefined } +} let iconImage: HTMLImageElement; async function initializeFaviconRenderer() { + const events = new Registry(); + initializeFaviconController(events); + iconImage = new Image(); iconImage.src = kClientSpriteUrl; await new Promise((resolve, reject) => { @@ -22,7 +31,7 @@ async function initializeFaviconRenderer() { }); let container = document.createElement("span"); - ReactDOM.render(ReactDOM.createPortal(, document.head), container, () => { + ReactDOM.render(ReactDOM.createPortal(, document.head), container, () => { document.getElementById("favicon").remove(); }); //container.remove(); @@ -44,13 +53,40 @@ function clientIconToDataUrl(icon: ClientIcon) : string | undefined { return canvas.toDataURL(); } -const FaviconRenderer = () => { - const [ handler, setHandler ] = useState(server_connections.active_connection()); +function initializeFaviconController(events: Registry) { + let currentHandler: ConnectionHandler; + let currentEvents: (() => void)[] = []; - server_connections.events().reactUse("notify_active_handler_changed", event => setHandler(event.newHandler)); + server_connections.events().on("notify_active_handler_changed", event => setCurrentHandler(event.newHandler)); - return handler ? : ; -}; + const setCurrentHandler = (handler: ConnectionHandler) => { + finalizeCurrentHandler(); + initializeCurrentHandler(handler); + sendFavicon(); + } + + const initializeCurrentHandler = (handler: ConnectionHandler) => { + currentEvents.push(handler.events().on("notify_connection_state_changed", () => sendFavicon())); + currentEvents.push(handler.getClient().events.on("notify_status_icon_changed", () => sendFavicon())); + }; + + const finalizeCurrentHandler = () => { + currentEvents.forEach(callback => callback()); + currentEvents = []; + } + + const sendFavicon = () => { + let icon: ClientIcon; + + if(currentHandler?.connection_state === ConnectionState.CONNECTED) { + icon = currentHandler.getClient().getStatusIcon(); + } + + events.fire_async("notify_icon", { icon: icon }) + }; + + events.on("query_icon", () => sendFavicon()); +} const DefaultFaviconRenderer = () => ; const ClientIconFaviconRenderer = (props: { icon: ClientIcon }) => { @@ -62,21 +98,20 @@ const ClientIconFaviconRenderer = (props: { icon: ClientIcon }) => { } }; -const HandlerFaviconRenderer = (props: { connection: ConnectionHandler }) => { - const [ showClientStatus, setShowClientStatus ] = useState(props.connection.connection_state === ConnectionState.CONNECTED); - props.connection.events().reactUse("notify_connection_state_changed", event => setShowClientStatus(event.new_state === ConnectionState.CONNECTED)); +const FaviconRenderer = (props: { events: Registry }) => { + const [ favicon, setFavicon ] = useState(() => { + props.events.fire("query_icon"); + return undefined; + }); + props.events.reactUse("notify_icon", event => setFavicon(event.icon)); - const [ statusIcon, setStatusIcon ] = useState(props.connection.getClient().getStatusIcon()); - props.connection.getClient().events.reactUse("notify_status_icon_changed", event => setStatusIcon(event.newIcon)); - - if(showClientStatus) { - return ; - } else { + if(!favicon) { return ; + } else { + return ; } } - loader.register_task(Stage.JAVASCRIPT_INITIALIZING, { name: "favicon renderer", function: initializeFaviconRenderer,