Using one global video renderer instead of detached HTML elements for each connection

master
WolverinDEV 2021-04-29 20:12:50 +02:00
parent 1cae741b17
commit a0ba132182
7 changed files with 70 additions and 83 deletions

View File

@ -34,7 +34,7 @@ export class AppController {
constructor() {
this.uiEvents = new Registry<AppUiEvents>();
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
});
}
}

View File

@ -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<ChannelTreeUIEvents> | undefined,
handlerId: string
},
notify_video_container: {
container: HTMLDivElement | undefined
notify_video: {
events: Registry<ChannelVideoEvents> | undefined,
handlerId: string
}
}

View File

@ -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<AppUiEvents> }) => {
const refElement = React.useRef<HTMLDivElement>();
const [ container, setContainer ] = useState<HTMLDivElement | undefined>(() => {
props.events.fire("query_video_container");
return undefined;
const [ data, setData ] = useState<{ events: Registry<ChannelVideoEvents> | 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 <div ref={refElement} />;
return <ChannelVideoRenderer handlerId={data.handlerId} events={data.events} key={"video-" + data.handlerId} />;
});
const ChannelTree = React.memo((props: { events: Registry<AppUiEvents> }) => {

View File

@ -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<ChannelVideoEvents>;
@ -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<ChannelVideoEvents>;
private container: HTMLDivElement;
private controller: ChannelVideoController;
constructor(handle: ConnectionHandler) {
@ -807,38 +795,15 @@ export class ChannelVideoFrame {
this.events = new Registry<ChannelVideoEvents>();
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<ChannelVideoEvents> {
return this.events;
}
}

View File

@ -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,

View File

@ -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 {

View File

@ -752,20 +752,44 @@ const PanelContainer = (props: { children }) => {
);
}
export const ChannelVideoRenderer = (props: { handlerId: string, events: Registry<ChannelVideoEvents> }) => {
const VisibilityHandler = React.memo((props: {
children
}) => {
const events = useContext(EventContext);
const [ streamingCount, setStreamingCount ] = useState<number>(() => {
events.fire("query_videos");
return 0;
});
const [ expanded, setExpanded ] = useState<boolean>(() => {
events.fire("query_expended");
return false;
})
events.reactUse("notify_videos", event => setStreamingCount(event.videoActiveCount));
events.reactUse("notify_expended", event => setExpanded(event.expended));
return (
<div className={joinClassList(cssStyle.container, streamingCount === 0 && cssStyle.hidden, expanded && cssStyle.expended)}>
{props.children}
</div>
)
});
export const ChannelVideoRenderer = React.memo((props: { handlerId: string, events: Registry<ChannelVideoEvents> }) => {
return (
<EventContext.Provider value={props.events}>
<HandlerIdContext.Provider value={props.handlerId}>
<PanelContainer>
<VideoSubscribeContextProvider>
<VideoBar />
<ExpendArrow />
<ErrorBoundary>
<Spotlight />
</ErrorBoundary>
</VideoSubscribeContextProvider>
</PanelContainer>
<VisibilityHandler>
<PanelContainer>
<VideoSubscribeContextProvider>
<VideoBar />
<ExpendArrow />
<ErrorBoundary>
<Spotlight />
</ErrorBoundary>
</VideoSubscribeContextProvider>
</PanelContainer>
</VisibilityHandler>
</HandlerIdContext.Provider>
</EventContext.Provider>
);
};
});