From a0ba132182c1395ec14dc8ea7ceb0d8fd052ff12 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Thu, 29 Apr 2021 20:12:50 +0200 Subject: [PATCH] Using one global video renderer instead of detached HTML elements for each connection --- shared/js/ui/AppController.ts | 7 ++-- shared/js/ui/AppDefinitions.ts | 8 +++-- shared/js/ui/AppRenderer.tsx | 26 +++++--------- shared/js/ui/frames/video/Controller.ts | 43 +++------------------- shared/js/ui/frames/video/Definitions.ts | 4 ++- shared/js/ui/frames/video/Renderer.scss | 19 +++++----- shared/js/ui/frames/video/Renderer.tsx | 46 ++++++++++++++++++------ 7 files changed, 70 insertions(+), 83 deletions(-) diff --git a/shared/js/ui/AppController.ts b/shared/js/ui/AppController.ts index 4a4a685f..9d0cf7a2 100644 --- a/shared/js/ui/AppController.ts +++ b/shared/js/ui/AppController.ts @@ -34,7 +34,7 @@ export class AppController { constructor() { this.uiEvents = new Registry(); this.uiEvents.on("query_channel_tree", () => this.notifyChannelTree()); - this.uiEvents.on("query_video_container", () => this.notifyVideoContainer()); + this.uiEvents.on("query_video", () => this.notifyVideoContainer()); this.listener = []; } @@ -126,8 +126,9 @@ export class AppController { } private notifyVideoContainer() { - this.uiEvents.fire_react("notify_video_container", { - container: this.currentConnection?.video_frame.getContainer() + this.uiEvents.fire_react("notify_video", { + events: this.currentConnection?.video_frame.getEvents(), + handlerId: this.currentConnection?.handlerId }); } } diff --git a/shared/js/ui/AppDefinitions.ts b/shared/js/ui/AppDefinitions.ts index 3a9cdfa7..6db8e80c 100644 --- a/shared/js/ui/AppDefinitions.ts +++ b/shared/js/ui/AppDefinitions.ts @@ -1,15 +1,17 @@ import {Registry} from "tc-shared/events"; import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions"; +import {ChannelVideoEvents} from "tc-shared/ui/frames/video/Definitions"; export interface AppUiEvents { query_channel_tree: {}, - query_video_container: {}, + query_video: {}, notify_channel_tree: { events: Registry | undefined, handlerId: string }, - notify_video_container: { - container: HTMLDivElement | undefined + notify_video: { + events: Registry | undefined, + handlerId: string } } \ No newline at end of file diff --git a/shared/js/ui/AppRenderer.tsx b/shared/js/ui/AppRenderer.tsx index ae97c0cc..c77f6a07 100644 --- a/shared/js/ui/AppRenderer.tsx +++ b/shared/js/ui/AppRenderer.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import {useEffect, useState} from "react"; +import {useState} from "react"; import {ControlBar2} from "tc-shared/ui/frames/control-bar/Renderer"; import {Registry} from "tc-shared/events"; import {ControlBarEvents} from "tc-shared/ui/frames/control-bar/Definitions"; @@ -21,30 +21,22 @@ import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions"; import {ImagePreviewHook} from "tc-shared/ui/frames/ImagePreview"; import {InternalModalHook} from "tc-shared/ui/react-elements/modal/internal"; import {TooltipHook} from "tc-shared/ui/react-elements/Tooltip"; +import {ChannelVideoEvents} from "tc-shared/ui/frames/video/Definitions"; +import {ChannelVideoRenderer} from "tc-shared/ui/frames/video/Renderer"; const cssStyle = require("./AppRenderer.scss"); const VideoFrame = React.memo((props: { events: Registry }) => { - const refElement = React.useRef(); - const [ container, setContainer ] = useState(() => { - props.events.fire("query_video_container"); - return undefined; + const [ data, setData ] = useState<{ events: Registry | undefined, handlerId: string | undefined }>(() => { + props.events.fire("query_video"); + return { events: undefined, handlerId: undefined }; }); - props.events.reactUse("notify_video_container", event => setContainer(event.container)); + props.events.reactUse("notify_video", event => setData({ handlerId: event.handlerId, events: event.events })); - useEffect(() => { - if(!refElement.current || !container) { - return; - } - - refElement.current.replaceWith(container); - return () => container.replaceWith(refElement.current); - }); - - if(!container) { + if(!data.events) { return null; } - return
; + return ; }); const ChannelTree = React.memo((props: { events: Registry }) => { diff --git a/shared/js/ui/frames/video/Controller.ts b/shared/js/ui/frames/video/Controller.ts index 20e4fda7..395c6721 100644 --- a/shared/js/ui/frames/video/Controller.ts +++ b/shared/js/ui/frames/video/Controller.ts @@ -346,8 +346,6 @@ class LocalVideoController extends RemoteClientVideoController { } class ChannelVideoController { - callbackVisibilityChanged: (visible: boolean) => void; - private readonly connection: ConnectionHandler; private readonly videoConnection: VideoConnection; private readonly events: Registry; @@ -710,13 +708,13 @@ class ChannelVideoController { } } - this.updateVisibility(videoStreamingCount !== 0); if(this.expended) { this.currentSpotlights.forEach(entry => videoIds.remove(entry)); } this.events.fire_react("notify_videos", { - videoIds: videoIds + videoIds: videoIds, + videoActiveCount: videoStreamingCount }); } @@ -785,21 +783,11 @@ class ChannelVideoController { } this.events.fire_react("notify_viewer_count", { camera: cameraViewers, screen: screenViewers }); } - - private updateVisibility(target: boolean) { - if(this.currentlyVisible === target) { return; } - - this.currentlyVisible = target; - if(this.callbackVisibilityChanged) { - this.callbackVisibilityChanged(target); - } - } } export class ChannelVideoFrame { private readonly handle: ConnectionHandler; private readonly events: Registry; - private container: HTMLDivElement; private controller: ChannelVideoController; constructor(handle: ConnectionHandler) { @@ -807,38 +795,15 @@ export class ChannelVideoFrame { this.events = new Registry(); this.controller = new ChannelVideoController(this.events, handle); this.controller.initialize(); - - this.container = document.createElement("div"); - this.container.classList.add(cssStyle.container, cssStyle.hidden); - - ReactDOM.render(React.createElement(ChannelVideoRenderer, { handlerId: handle.handlerId, events: this.events }), this.container); - - this.events.on("notify_expended", event => { - this.container.classList.toggle(cssStyle.expended, event.expended); - }); - this.controller.callbackVisibilityChanged = flag => { - this.container.classList.toggle(cssStyle.hidden, !flag); - if(!flag) { - this.events.fire("action_toggle_expended", { expended: false }) - } - }; } destroy() { this.controller?.destroy(); this.controller = undefined; - - if(this.container) { - this.container.remove(); - ReactDOM.unmountComponentAtNode(this.container); - - this.container = undefined; - } - this.events.destroy(); } - getContainer() : HTMLDivElement { - return this.container; + getEvents() : Registry { + return this.events; } } \ No newline at end of file diff --git a/shared/js/ui/frames/video/Definitions.ts b/shared/js/ui/frames/video/Definitions.ts index b463fa6c..9e094bf6 100644 --- a/shared/js/ui/frames/video/Definitions.ts +++ b/shared/js/ui/frames/video/Definitions.ts @@ -82,6 +82,7 @@ export interface ChannelVideoEvents { action_show_viewers: {}, query_expended: {}, + query_visible: {}, query_videos: {}, query_video: { videoId: string }, query_video_info: { videoId: string }, @@ -93,7 +94,8 @@ export interface ChannelVideoEvents { notify_expended: { expended: boolean }, notify_videos: { - videoIds: string[] + videoIds: string[], + videoActiveCount: number, }, notify_video: { videoId: string, diff --git a/shared/js/ui/frames/video/Renderer.scss b/shared/js/ui/frames/video/Renderer.scss index ef7f3018..014daa38 100644 --- a/shared/js/ui/frames/video/Renderer.scss +++ b/shared/js/ui/frames/video/Renderer.scss @@ -28,15 +28,6 @@ $small_height: 10em; } } - .heightProvider { - height: 100%; /* the footer size (version etc) */ - - .spotlight { - margin-left: 0; - margin-right: 0; - } - } - &.expended { .panel { height: 100%; /* the footer size (version etc) */ @@ -48,6 +39,16 @@ $small_height: 10em; @include transform(rotate(90deg)!important); } } + + /* Needs to be within the .container class else dosn't work */ + .heightProvider { + height: 100%; /* the footer size (version etc) */ + + .spotlight { + margin-left: 0; + margin-right: 0; + } + } } .panel { diff --git a/shared/js/ui/frames/video/Renderer.tsx b/shared/js/ui/frames/video/Renderer.tsx index 8a8a6de6..df60f96e 100644 --- a/shared/js/ui/frames/video/Renderer.tsx +++ b/shared/js/ui/frames/video/Renderer.tsx @@ -752,20 +752,44 @@ const PanelContainer = (props: { children }) => { ); } -export const ChannelVideoRenderer = (props: { handlerId: string, events: Registry }) => { +const VisibilityHandler = React.memo((props: { + children +}) => { + const events = useContext(EventContext); + const [ streamingCount, setStreamingCount ] = useState(() => { + events.fire("query_videos"); + return 0; + }); + const [ expanded, setExpanded ] = useState(() => { + events.fire("query_expended"); + return false; + }) + + events.reactUse("notify_videos", event => setStreamingCount(event.videoActiveCount)); + events.reactUse("notify_expended", event => setExpanded(event.expended)); + return ( +
+ {props.children} +
+ ) +}); + +export const ChannelVideoRenderer = React.memo((props: { handlerId: string, events: Registry }) => { return ( - - - - - - - - - + + + + + + + + + + + ); -}; \ No newline at end of file +}); \ No newline at end of file