Rendering the main UI only via IPC
This commit is contained in:
parent
a0ba132182
commit
f2ab9800d4
14 changed files with 212 additions and 139 deletions
|
@ -9,20 +9,22 @@ import {initializeConnectionListController} from "tc-shared/ui/frames/connection
|
||||||
import * as loader from "tc-loader";
|
import * as loader from "tc-loader";
|
||||||
import {Stage} from "tc-loader";
|
import {Stage} from "tc-loader";
|
||||||
import {server_connections} from "tc-shared/ConnectionManager";
|
import {server_connections} from "tc-shared/ConnectionManager";
|
||||||
import {AppUiEvents} from "tc-shared/ui/AppDefinitions";
|
import {AppUiEvents, AppUiVariables} from "tc-shared/ui/AppDefinitions";
|
||||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||||
import {SideBarController} from "tc-shared/ui/frames/SideBarController";
|
import {SideBarController} from "tc-shared/ui/frames/SideBarController";
|
||||||
import {ServerEventLogController} from "tc-shared/ui/frames/log/Controller";
|
import {ServerEventLogController} from "tc-shared/ui/frames/log/Controller";
|
||||||
import {HostBannerController} from "tc-shared/ui/frames/HostBannerController";
|
import {HostBannerController} from "tc-shared/ui/frames/HostBannerController";
|
||||||
|
import {UiVariableProvider} from "tc-shared/ui/utils/Variable";
|
||||||
|
import {createIpcUiVariableProvider, IpcUiVariableProvider} from "tc-shared/ui/utils/IpcVariable";
|
||||||
|
|
||||||
export class AppController {
|
export class AppController {
|
||||||
private uiEvents: Registry<AppUiEvents>;
|
|
||||||
|
|
||||||
private listener: (() => void)[];
|
private listener: (() => void)[];
|
||||||
|
|
||||||
private currentConnection: ConnectionHandler;
|
private currentConnection: ConnectionHandler;
|
||||||
private listenerConnection: (() => void)[];
|
private listenerConnection: (() => void)[];
|
||||||
|
|
||||||
|
private variables: IpcUiVariableProvider<AppUiVariables>;
|
||||||
|
|
||||||
private container: HTMLDivElement;
|
private container: HTMLDivElement;
|
||||||
private controlBarEvents: Registry<ControlBarEvents>;
|
private controlBarEvents: Registry<ControlBarEvents>;
|
||||||
private connectionListEvents: Registry<ConnectionListUIEvents>;
|
private connectionListEvents: Registry<ConnectionListUIEvents>;
|
||||||
|
@ -32,9 +34,21 @@ export class AppController {
|
||||||
private hostBannerController: HostBannerController;
|
private hostBannerController: HostBannerController;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.uiEvents = new Registry<AppUiEvents>();
|
this.variables = createIpcUiVariableProvider();
|
||||||
this.uiEvents.on("query_channel_tree", () => this.notifyChannelTree());
|
this.variables.setVariableProvider("connectionList", () => this.connectionListEvents.generateIpcDescription());
|
||||||
this.uiEvents.on("query_video", () => this.notifyVideoContainer());
|
this.variables.setVariableProvider("controlBar", () => this.controlBarEvents.generateIpcDescription());
|
||||||
|
this.variables.setVariableProvider("hostBanner", () => this.hostBannerController.uiEvents.generateIpcDescription());
|
||||||
|
this.variables.setVariableProvider("log", () => this.serverLogController.events.generateIpcDescription());
|
||||||
|
this.variables.setVariableProvider("sidebar", () => this.sideBarController.uiEvents.generateIpcDescription());
|
||||||
|
this.variables.setVariableProvider("sidebarHeader", () => this.sideBarController.getHeaderController().uiEvents.generateIpcDescription());
|
||||||
|
this.variables.setVariableProvider("channelTree", () => ({
|
||||||
|
events: this.currentConnection?.channelTree.mainTreeUiEvents.generateIpcDescription(),
|
||||||
|
handlerId: this.currentConnection?.handlerId
|
||||||
|
}));
|
||||||
|
this.variables.setVariableProvider("channelVideo", () => ({
|
||||||
|
events: this.currentConnection?.video_frame.getEvents().generateIpcDescription(),
|
||||||
|
handlerId: this.currentConnection?.handlerId
|
||||||
|
}));
|
||||||
|
|
||||||
this.listener = [];
|
this.listener = [];
|
||||||
}
|
}
|
||||||
|
@ -64,8 +78,8 @@ export class AppController {
|
||||||
this.hostBannerController?.destroy();
|
this.hostBannerController?.destroy();
|
||||||
this.hostBannerController = undefined;
|
this.hostBannerController = undefined;
|
||||||
|
|
||||||
this.uiEvents?.destroy();
|
this.variables?.destroy();
|
||||||
this.uiEvents = undefined;
|
this.variables = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
|
@ -102,35 +116,15 @@ export class AppController {
|
||||||
this.serverLogController.setConnectionHandler(connection);
|
this.serverLogController.setConnectionHandler(connection);
|
||||||
this.hostBannerController.setConnectionHandler(connection);
|
this.hostBannerController.setConnectionHandler(connection);
|
||||||
|
|
||||||
this.notifyChannelTree();
|
this.variables.sendVariable("channelTree");
|
||||||
this.notifyVideoContainer();
|
this.variables.sendVariable("channelVideo");
|
||||||
}
|
}
|
||||||
|
|
||||||
renderApp() {
|
renderApp() {
|
||||||
ReactDOM.render(React.createElement(TeaAppMainView, {
|
ReactDOM.render(React.createElement(TeaAppMainView, {
|
||||||
controlBar: this.controlBarEvents,
|
variables: this.variables.generateConsumerDescription(),
|
||||||
connectionList: this.connectionListEvents,
|
|
||||||
sidebar: this.sideBarController.uiEvents,
|
|
||||||
sidebarHeader: this.sideBarController.getHeaderController().uiEvents,
|
|
||||||
log: this.serverLogController.events,
|
|
||||||
events: this.uiEvents,
|
|
||||||
hostBanner: this.hostBannerController.uiEvents
|
|
||||||
}), this.container);
|
}), this.container);
|
||||||
}
|
}
|
||||||
|
|
||||||
private notifyChannelTree() {
|
|
||||||
this.uiEvents.fire_react("notify_channel_tree", {
|
|
||||||
handlerId: this.currentConnection?.handlerId,
|
|
||||||
events: this.currentConnection?.channelTree.mainTreeUiEvents
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private notifyVideoContainer() {
|
|
||||||
this.uiEvents.fire_react("notify_video", {
|
|
||||||
events: this.currentConnection?.video_frame.getEvents(),
|
|
||||||
handlerId: this.currentConnection?.handlerId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export let appViewController: AppController;
|
export let appViewController: AppController;
|
||||||
|
|
|
@ -1,6 +1,29 @@
|
||||||
import {Registry} from "tc-shared/events";
|
import {IpcRegistryDescription, Registry} from "tc-shared/events";
|
||||||
import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions";
|
import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions";
|
||||||
import {ChannelVideoEvents} from "tc-shared/ui/frames/video/Definitions";
|
import {ChannelVideoEvents} from "tc-shared/ui/frames/video/Definitions";
|
||||||
|
import {ControlBarEvents} from "tc-shared/ui/frames/control-bar/Definitions";
|
||||||
|
import {ConnectionListUIEvents} from "tc-shared/ui/frames/connection-handler-list/Definitions";
|
||||||
|
import {SideBarEvents} from "tc-shared/ui/frames/SideBarDefinitions";
|
||||||
|
import {SideHeaderEvents} from "tc-shared/ui/frames/side/HeaderDefinitions";
|
||||||
|
import {ServerEventLogUiEvents} from "tc-shared/ui/frames/log/Definitions";
|
||||||
|
import {HostBannerUiEvents} from "tc-shared/ui/frames/HostBannerDefinitions";
|
||||||
|
|
||||||
|
export interface AppUiVariables {
|
||||||
|
readonly channelTree: {
|
||||||
|
events: IpcRegistryDescription<ChannelTreeUIEvents> | undefined,
|
||||||
|
handlerId: string
|
||||||
|
},
|
||||||
|
readonly channelVideo: {
|
||||||
|
events: IpcRegistryDescription<ChannelVideoEvents>,
|
||||||
|
handlerId: string
|
||||||
|
},
|
||||||
|
readonly controlBar: IpcRegistryDescription<ControlBarEvents>,
|
||||||
|
readonly connectionList: IpcRegistryDescription<ConnectionListUIEvents>,
|
||||||
|
readonly sidebar: IpcRegistryDescription<SideBarEvents>,
|
||||||
|
readonly sidebarHeader: IpcRegistryDescription<SideHeaderEvents>,
|
||||||
|
readonly log: IpcRegistryDescription<ServerEventLogUiEvents>,
|
||||||
|
readonly hostBanner: IpcRegistryDescription<HostBannerUiEvents>,
|
||||||
|
}
|
||||||
|
|
||||||
export interface AppUiEvents {
|
export interface AppUiEvents {
|
||||||
query_channel_tree: {},
|
query_channel_tree: {},
|
||||||
|
|
|
@ -1,96 +1,117 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {useState} from "react";
|
import {useEffect, useMemo} from "react";
|
||||||
import {ControlBar2} from "tc-shared/ui/frames/control-bar/Renderer";
|
import {ControlBar2} from "tc-shared/ui/frames/control-bar/Renderer";
|
||||||
import {Registry} from "tc-shared/events";
|
import {IpcRegistryDescription, Registry} from "tc-shared/events";
|
||||||
import {ControlBarEvents} from "tc-shared/ui/frames/control-bar/Definitions";
|
|
||||||
import {ConnectionListUIEvents} from "tc-shared/ui/frames/connection-handler-list/Definitions";
|
|
||||||
import {ConnectionHandlerList} from "tc-shared/ui/frames/connection-handler-list/Renderer";
|
import {ConnectionHandlerList} from "tc-shared/ui/frames/connection-handler-list/Renderer";
|
||||||
import {ErrorBoundary} from "tc-shared/ui/react-elements/ErrorBoundary";
|
import {ErrorBoundary} from "tc-shared/ui/react-elements/ErrorBoundary";
|
||||||
import {ContextDivider} from "tc-shared/ui/react-elements/ContextDivider";
|
import {ContextDivider} from "tc-shared/ui/react-elements/ContextDivider";
|
||||||
import {SideBarRenderer} from "tc-shared/ui/frames/SideBarRenderer";
|
import {SideBarRenderer} from "tc-shared/ui/frames/SideBarRenderer";
|
||||||
import {SideBarEvents} from "tc-shared/ui/frames/SideBarDefinitions";
|
|
||||||
import {SideHeaderEvents} from "tc-shared/ui/frames/side/HeaderDefinitions";
|
|
||||||
import {ServerLogFrame} from "tc-shared/ui/frames/log/Renderer";
|
import {ServerLogFrame} from "tc-shared/ui/frames/log/Renderer";
|
||||||
import {ServerEventLogUiEvents} from "tc-shared/ui/frames/log/Definitions";
|
|
||||||
import {FooterRenderer} from "tc-shared/ui/frames/footer/Renderer";
|
import {FooterRenderer} from "tc-shared/ui/frames/footer/Renderer";
|
||||||
import {HostBanner} from "tc-shared/ui/frames/HostBannerRenderer";
|
import {HostBanner} from "tc-shared/ui/frames/HostBannerRenderer";
|
||||||
import {HostBannerUiEvents} from "tc-shared/ui/frames/HostBannerDefinitions";
|
import {AppUiVariables} from "tc-shared/ui/AppDefinitions";
|
||||||
import {AppUiEvents} from "tc-shared/ui/AppDefinitions";
|
|
||||||
import {ChannelTreeRenderer} from "tc-shared/ui/tree/Renderer";
|
import {ChannelTreeRenderer} from "tc-shared/ui/tree/Renderer";
|
||||||
import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions";
|
|
||||||
import {ImagePreviewHook} from "tc-shared/ui/frames/ImagePreview";
|
import {ImagePreviewHook} from "tc-shared/ui/frames/ImagePreview";
|
||||||
import {InternalModalHook} from "tc-shared/ui/react-elements/modal/internal";
|
import {InternalModalHook} from "tc-shared/ui/react-elements/modal/internal";
|
||||||
import {TooltipHook} from "tc-shared/ui/react-elements/Tooltip";
|
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";
|
import {ChannelVideoRenderer} from "tc-shared/ui/frames/video/Renderer";
|
||||||
|
import {
|
||||||
|
createIpcUiVariableConsumer,
|
||||||
|
IpcVariableDescriptor
|
||||||
|
} from "tc-shared/ui/utils/IpcVariable";
|
||||||
|
import {UiVariableConsumer} from "tc-shared/ui/utils/Variable";
|
||||||
|
import {useRegistry} from "tc-shared/ui/react-elements/Helper";
|
||||||
const cssStyle = require("./AppRenderer.scss");
|
const cssStyle = require("./AppRenderer.scss");
|
||||||
const VideoFrame = React.memo((props: { events: Registry<AppUiEvents> }) => {
|
|
||||||
const [ data, setData ] = useState<{ events: Registry<ChannelVideoEvents> | undefined, handlerId: string | undefined }>(() => {
|
const VideoFrame = React.memo((props: { variables: UiVariableConsumer<AppUiVariables> }) => {
|
||||||
props.events.fire("query_video");
|
const data = props.variables.useReadOnly("channelVideo", undefined, { handlerId: undefined, events: undefined });
|
||||||
return { events: undefined, handlerId: undefined };
|
const events = useRegistry(data.events);
|
||||||
});
|
|
||||||
props.events.reactUse("notify_video", event => setData({ handlerId: event.handlerId, events: event.events }));
|
|
||||||
|
|
||||||
if(!data.events) {
|
if(!data.events) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ChannelVideoRenderer handlerId={data.handlerId} events={data.events} key={"video-" + data.handlerId} />;
|
return <ChannelVideoRenderer handlerId={data.handlerId} events={events} key={"video-" + data.handlerId} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
const ChannelTree = React.memo((props: { events: Registry<AppUiEvents> }) => {
|
const ChannelTree = React.memo((props: { variables: UiVariableConsumer<AppUiVariables> }) => {
|
||||||
const [ data, setData ] = useState<{ events: Registry<ChannelTreeUIEvents>, handlerId: string }>(() => {
|
const data = props.variables.useReadOnly("channelTree", undefined, { handlerId: undefined, events: undefined });
|
||||||
props.events.fire("query_channel_tree");
|
const events = useRegistry(data.events);
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
props.events.reactUse("notify_channel_tree", event => {
|
if(!events) {
|
||||||
setData({ events: event.events, handlerId: event.handlerId });
|
|
||||||
}, undefined, []);
|
|
||||||
|
|
||||||
if(!data?.events) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ChannelTreeRenderer handlerId={data.handlerId} events={data.events} />;
|
return <ChannelTreeRenderer handlerId={data.handlerId} events={events} key={"tree-" + data.handlerId} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const SideBar = React.memo((props: { variables: UiVariableConsumer<AppUiVariables> }) => (
|
||||||
|
<EventProvider registry={props.variables.useReadOnly("sidebar", undefined, undefined)}>
|
||||||
|
{sidebarRegistry => (
|
||||||
|
<EventProvider registry={props.variables.useReadOnly("sidebarHeader", undefined, undefined)}>
|
||||||
|
{sidebarHeaderRegistry => sidebarRegistry && sidebarHeaderRegistry ? (
|
||||||
|
<SideBarRenderer events={sidebarRegistry} eventsHeader={sidebarHeaderRegistry} className={cssStyle.sideBar} />
|
||||||
|
) : null}
|
||||||
|
</EventProvider>
|
||||||
|
)}
|
||||||
|
</EventProvider>
|
||||||
|
));
|
||||||
|
|
||||||
|
function EventProvider<T>(props: {
|
||||||
|
registry: IpcRegistryDescription<T> | undefined,
|
||||||
|
children: (registry: Registry<T> | undefined) => React.ReactElement
|
||||||
|
}) : React.ReactElement {
|
||||||
|
return props.children(useRegistry(props.registry));
|
||||||
|
}
|
||||||
|
|
||||||
export const TeaAppMainView = (props: {
|
export const TeaAppMainView = (props: {
|
||||||
events: Registry<AppUiEvents>
|
variables: IpcVariableDescriptor<AppUiVariables>,
|
||||||
controlBar: Registry<ControlBarEvents>,
|
|
||||||
connectionList: Registry<ConnectionListUIEvents>,
|
|
||||||
sidebar: Registry<SideBarEvents>,
|
|
||||||
sidebarHeader: Registry<SideHeaderEvents>,
|
|
||||||
log: Registry<ServerEventLogUiEvents>,
|
|
||||||
hostBanner: Registry<HostBannerUiEvents>
|
|
||||||
}) => {
|
}) => {
|
||||||
|
const variables = useMemo(() => createIpcUiVariableConsumer(props.variables), [ props.variables ]);
|
||||||
|
useEffect(() => () => variables.destroy(), [ props.variables ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cssStyle.app}>
|
<div className={cssStyle.app}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<ControlBar2 events={props.controlBar} className={cssStyle.controlBar} />
|
<EventProvider registry={variables.useReadOnly("controlBar", undefined, undefined)}>
|
||||||
|
{registry => registry ? (
|
||||||
|
<ControlBar2 events={registry} className={cssStyle.controlBar} />
|
||||||
|
) : null}
|
||||||
|
</EventProvider>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<ConnectionHandlerList events={props.connectionList} />
|
<EventProvider registry={variables.useReadOnly("connectionList", undefined, undefined)}>
|
||||||
|
{registry => registry ? (
|
||||||
|
<ConnectionHandlerList events={registry} />
|
||||||
|
) : null}
|
||||||
|
</EventProvider>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
|
||||||
<div className={cssStyle.mainContainer}>
|
<div className={cssStyle.mainContainer}>
|
||||||
<VideoFrame events={props.events} />
|
<VideoFrame variables={variables} />
|
||||||
|
|
||||||
<div className={cssStyle.channelTreeAndSidebar}>
|
<div className={cssStyle.channelTreeAndSidebar}>
|
||||||
<div className={cssStyle.channelTree}>
|
<div className={cssStyle.channelTree}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<HostBanner events={props.hostBanner} />
|
<EventProvider registry={variables.useReadOnly("hostBanner", undefined, undefined)}>
|
||||||
<ChannelTree events={props.events} />
|
{registry => registry ? (
|
||||||
|
<HostBanner events={registry} />
|
||||||
|
) : null}
|
||||||
|
</EventProvider>
|
||||||
|
<ChannelTree variables={variables} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
<ContextDivider id={"channel-chat"} direction={"horizontal"} defaultValue={25} />
|
<ContextDivider id={"channel-chat"} direction={"horizontal"} defaultValue={25} />
|
||||||
<SideBarRenderer events={props.sidebar} eventsHeader={props.sidebarHeader} className={cssStyle.sideBar} />
|
<SideBar variables={variables} />
|
||||||
</div>
|
</div>
|
||||||
<ContextDivider id={"main-log"} direction={"vertical"} defaultValue={75} />
|
<ContextDivider id={"main-log"} direction={"vertical"} defaultValue={75} />
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<div className={cssStyle.containerLog}>
|
<div className={cssStyle.containerLog}>
|
||||||
<ServerLogFrame events={props.log} />
|
<EventProvider registry={variables.useReadOnly("log", undefined, undefined)}>
|
||||||
|
{registry => registry ? (
|
||||||
|
<ServerLogFrame events={registry} />
|
||||||
|
) : null}
|
||||||
|
</EventProvider>
|
||||||
</div>
|
</div>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -73,15 +73,6 @@ export class SideBarController {
|
||||||
this.privateConversations = undefined;
|
this.privateConversations = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInto(container: HTMLDivElement) {
|
|
||||||
/*
|
|
||||||
ReactDOM.render(React.createElement(SideBarRenderer, {
|
|
||||||
events: this.uiEvents,
|
|
||||||
eventsHeader: this.header["uiEvents"],
|
|
||||||
}), container);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
getMusicController() : MusicBotController {
|
getMusicController() : MusicBotController {
|
||||||
return this.musicPanel;
|
return this.musicPanel;
|
||||||
}
|
}
|
||||||
|
@ -116,7 +107,7 @@ export class SideBarController {
|
||||||
this.uiEvents.fire_react("notify_content_data", {
|
this.uiEvents.fire_react("notify_content_data", {
|
||||||
content: "channel",
|
content: "channel",
|
||||||
data: {
|
data: {
|
||||||
events: this.channelBar.uiEvents,
|
events: this.channelBar.uiEvents.generateIpcDescription(),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -125,7 +116,7 @@ export class SideBarController {
|
||||||
this.uiEvents.fire_react("notify_content_data", {
|
this.uiEvents.fire_react("notify_content_data", {
|
||||||
content: "server",
|
content: "server",
|
||||||
data: this.currentConnection ? {
|
data: this.currentConnection ? {
|
||||||
chatEvents: this.channelBar.getChannelConversationController().getUiEvents(),
|
chatEvents: this.channelBar.getChannelConversationController().getUiEvents().generateIpcDescription(),
|
||||||
handlerId: this.currentConnection.handlerId
|
handlerId: this.currentConnection.handlerId
|
||||||
} : undefined
|
} : undefined
|
||||||
});
|
});
|
||||||
|
@ -140,7 +131,7 @@ export class SideBarController {
|
||||||
this.uiEvents.fire_react("notify_content_data", {
|
this.uiEvents.fire_react("notify_content_data", {
|
||||||
content: "private-chat",
|
content: "private-chat",
|
||||||
data: {
|
data: {
|
||||||
events: this.privateConversations["uiEvents"],
|
events: this.privateConversations["uiEvents"].generateIpcDescription(),
|
||||||
handlerId: this.currentConnection.handlerId
|
handlerId: this.currentConnection.handlerId
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -155,7 +146,7 @@ export class SideBarController {
|
||||||
this.uiEvents.fire_react("notify_content_data", {
|
this.uiEvents.fire_react("notify_content_data", {
|
||||||
content: "client-info",
|
content: "client-info",
|
||||||
data: {
|
data: {
|
||||||
events: this.clientInfo["uiEvents"],
|
events: this.clientInfo["uiEvents"].generateIpcDescription(),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -169,8 +160,8 @@ export class SideBarController {
|
||||||
this.uiEvents.fire_react("notify_content_data", {
|
this.uiEvents.fire_react("notify_content_data", {
|
||||||
content: "music-manage",
|
content: "music-manage",
|
||||||
data: {
|
data: {
|
||||||
botEvents: this.musicPanel.getBotUiEvents(),
|
botEvents: this.musicPanel.getBotUiEvents().generateIpcDescription(),
|
||||||
playlistEvents: this.musicPanel.getPlaylistUiEvents()
|
playlistEvents: this.musicPanel.getPlaylistUiEvents().generateIpcDescription()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {Registry} from "tc-shared/events";
|
import {IpcRegistryDescription, Registry} from "tc-shared/events";
|
||||||
import {PrivateConversationUIEvents} from "tc-shared/ui/frames/side/PrivateConversationDefinitions";
|
import {PrivateConversationUIEvents} from "tc-shared/ui/frames/side/PrivateConversationDefinitions";
|
||||||
import {ClientInfoEvents} from "tc-shared/ui/frames/side/ClientInfoDefinitions";
|
import {ClientInfoEvents} from "tc-shared/ui/frames/side/ClientInfoDefinitions";
|
||||||
import {SideHeaderEvents} from "tc-shared/ui/frames/side/HeaderDefinitions";
|
import {SideHeaderEvents} from "tc-shared/ui/frames/side/HeaderDefinitions";
|
||||||
|
@ -7,28 +7,26 @@ import {MusicBotUiEvents} from "tc-shared/ui/frames/side/MusicBotDefinitions";
|
||||||
import {MusicPlaylistUiEvents} from "tc-shared/ui/frames/side/MusicPlaylistDefinitions";
|
import {MusicPlaylistUiEvents} from "tc-shared/ui/frames/side/MusicPlaylistDefinitions";
|
||||||
import {ChannelConversationUiEvents} from "tc-shared/ui/frames/side/ChannelConversationDefinitions";
|
import {ChannelConversationUiEvents} from "tc-shared/ui/frames/side/ChannelConversationDefinitions";
|
||||||
|
|
||||||
/* TODO: Somehow outsource the event registries to IPC? */
|
|
||||||
|
|
||||||
export type SideBarType = "none" | "server" | "channel" | "private-chat" | "client-info" | "music-manage";
|
export type SideBarType = "none" | "server" | "channel" | "private-chat" | "client-info" | "music-manage";
|
||||||
export interface SideBarTypeData {
|
export interface SideBarTypeData {
|
||||||
"none": {},
|
"none": {},
|
||||||
"channel": {
|
"channel": {
|
||||||
events: Registry<ChannelBarUiEvents>
|
events: IpcRegistryDescription<ChannelBarUiEvents>
|
||||||
},
|
},
|
||||||
"private-chat": {
|
"private-chat": {
|
||||||
events: Registry<PrivateConversationUIEvents>,
|
events: IpcRegistryDescription<PrivateConversationUIEvents>,
|
||||||
handlerId: string
|
handlerId: string
|
||||||
},
|
},
|
||||||
"client-info": {
|
"client-info": {
|
||||||
events: Registry<ClientInfoEvents>,
|
events: IpcRegistryDescription<ClientInfoEvents>,
|
||||||
},
|
},
|
||||||
"music-manage": {
|
"music-manage": {
|
||||||
botEvents: Registry<MusicBotUiEvents>,
|
botEvents: IpcRegistryDescription<MusicBotUiEvents>,
|
||||||
playlistEvents: Registry<MusicPlaylistUiEvents>
|
playlistEvents: IpcRegistryDescription<MusicPlaylistUiEvents>
|
||||||
},
|
},
|
||||||
"server": {
|
"server": {
|
||||||
handlerId: string,
|
handlerId: string,
|
||||||
chatEvents: Registry<ChannelConversationUiEvents>
|
chatEvents: IpcRegistryDescription<ChannelConversationUiEvents>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,9 +38,7 @@ export type SideBarNotifyContentData<T extends SideBarType> = {
|
||||||
export interface SideBarEvents {
|
export interface SideBarEvents {
|
||||||
query_content: {},
|
query_content: {},
|
||||||
query_content_data: { content: SideBarType },
|
query_content_data: { content: SideBarType },
|
||||||
query_header_data: {},
|
|
||||||
|
|
||||||
notify_content: { content: SideBarType },
|
notify_content: { content: SideBarType },
|
||||||
notify_content_data: SideBarNotifyContentData<SideBarType>,
|
notify_content_data: SideBarNotifyContentData<SideBarType>,
|
||||||
notify_header_data: { events: Registry<SideHeaderEvents> }
|
|
||||||
}
|
}
|
|
@ -11,6 +11,7 @@ import {ErrorBoundary} from "tc-shared/ui/react-elements/ErrorBoundary";
|
||||||
import {MusicBotRenderer} from "tc-shared/ui/frames/side/MusicBotRenderer";
|
import {MusicBotRenderer} from "tc-shared/ui/frames/side/MusicBotRenderer";
|
||||||
import {ConversationPanel} from "tc-shared/ui/frames/side/AbstractConversationRenderer";
|
import {ConversationPanel} from "tc-shared/ui/frames/side/AbstractConversationRenderer";
|
||||||
import React = require("react");
|
import React = require("react");
|
||||||
|
import {useRegistry} from "tc-shared/ui/react-elements/Helper";
|
||||||
|
|
||||||
const cssStyle = require("./SideBarRenderer.scss");
|
const cssStyle = require("./SideBarRenderer.scss");
|
||||||
|
|
||||||
|
@ -29,24 +30,28 @@ function useContentData<T extends SideBarType>(type: T) : SideBarTypeData[T] {
|
||||||
|
|
||||||
const ContentRendererChannel = () => {
|
const ContentRendererChannel = () => {
|
||||||
const contentData = useContentData("channel");
|
const contentData = useContentData("channel");
|
||||||
if(!contentData) { return null; }
|
const events = useRegistry(contentData?.events);
|
||||||
|
|
||||||
|
if(!contentData || !events) { return null; }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChannelBarRenderer
|
<ChannelBarRenderer
|
||||||
key={"channel"}
|
key={"channel"}
|
||||||
events={contentData.events}
|
events={events}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ContentRendererServer = () => {
|
const ContentRendererServer = () => {
|
||||||
const contentData = useContentData("server");
|
const contentData = useContentData("server");
|
||||||
if(!contentData) { return null; }
|
const events = useRegistry(contentData?.chatEvents);
|
||||||
|
|
||||||
|
if(!contentData || !events) { return null; }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConversationPanel
|
<ConversationPanel
|
||||||
key={"server"}
|
key={"server"}
|
||||||
events={contentData.chatEvents}
|
events={events}
|
||||||
handlerId={contentData.handlerId}
|
handlerId={contentData.handlerId}
|
||||||
messagesDeletable={true}
|
messagesDeletable={true}
|
||||||
noFirstMessageOverlay={false}
|
noFirstMessageOverlay={false}
|
||||||
|
@ -57,11 +62,13 @@ const ContentRendererServer = () => {
|
||||||
|
|
||||||
const ContentRendererPrivateConversation = () => {
|
const ContentRendererPrivateConversation = () => {
|
||||||
const contentData = useContentData("private-chat");
|
const contentData = useContentData("private-chat");
|
||||||
if(!contentData) { return null; }
|
const events = useRegistry(contentData?.events);
|
||||||
|
|
||||||
|
if(!contentData || !events) { return null; }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PrivateConversationsPanel
|
<PrivateConversationsPanel
|
||||||
events={contentData.events}
|
events={events}
|
||||||
handlerId={contentData.handlerId}
|
handlerId={contentData.handlerId}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -69,23 +76,26 @@ const ContentRendererPrivateConversation = () => {
|
||||||
|
|
||||||
const ContentRendererClientInfo = () => {
|
const ContentRendererClientInfo = () => {
|
||||||
const contentData = useContentData("client-info");
|
const contentData = useContentData("client-info");
|
||||||
if(!contentData) { return null; }
|
const events = useRegistry(contentData?.events);
|
||||||
|
if(!contentData || !events) { return null; }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ClientInfoRenderer
|
<ClientInfoRenderer
|
||||||
events={contentData.events}
|
events={events}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ContentRendererMusicManage = () => {
|
const ContentRendererMusicManage = () => {
|
||||||
const contentData = useContentData("music-manage");
|
const contentData = useContentData("music-manage");
|
||||||
if(!contentData) { return null; }
|
const botEvents = useRegistry(contentData?.botEvents);
|
||||||
|
const playlistEvents = useRegistry(contentData?.playlistEvents);
|
||||||
|
if(!contentData || !botEvents || !playlistEvents) { return null; }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MusicBotRenderer
|
<MusicBotRenderer
|
||||||
botEvents={contentData.botEvents}
|
botEvents={botEvents}
|
||||||
playlistEvents={contentData.playlistEvents}
|
playlistEvents={playlistEvents}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -181,7 +181,7 @@ export class ChannelBarController {
|
||||||
this.uiEvents.fire_react("notify_data", {
|
this.uiEvents.fire_react("notify_data", {
|
||||||
content: "conversation",
|
content: "conversation",
|
||||||
data: {
|
data: {
|
||||||
events: this.channelConversations.getUiEvents()
|
events: this.channelConversations.getUiEvents().generateIpcDescription()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -190,7 +190,7 @@ export class ChannelBarController {
|
||||||
this.uiEvents.fire_react("notify_data", {
|
this.uiEvents.fire_react("notify_data", {
|
||||||
content: "description",
|
content: "description",
|
||||||
data: {
|
data: {
|
||||||
events: this.description.uiEvents
|
events: this.description.uiEvents.generateIpcDescription()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -199,7 +199,7 @@ export class ChannelBarController {
|
||||||
this.uiEvents.fire_react("notify_data", {
|
this.uiEvents.fire_react("notify_data", {
|
||||||
content: "file-transfer",
|
content: "file-transfer",
|
||||||
data: {
|
data: {
|
||||||
events: this.fileBrowser.uiEvents
|
events: this.fileBrowser.uiEvents.generateIpcDescription()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {Registry} from "tc-shared/events";
|
import {IpcRegistryDescription} from "tc-shared/events";
|
||||||
import {ChannelConversationUiEvents} from "tc-shared/ui/frames/side/ChannelConversationDefinitions";
|
import {ChannelConversationUiEvents} from "tc-shared/ui/frames/side/ChannelConversationDefinitions";
|
||||||
import {ChannelDescriptionUiEvents} from "tc-shared/ui/frames/side/ChannelDescriptionDefinitions";
|
import {ChannelDescriptionUiEvents} from "tc-shared/ui/frames/side/ChannelDescriptionDefinitions";
|
||||||
import {ChannelFileBrowserUiEvents} from "tc-shared/ui/frames/side/ChannelFileBrowserDefinitions";
|
import {ChannelFileBrowserUiEvents} from "tc-shared/ui/frames/side/ChannelFileBrowserDefinitions";
|
||||||
|
@ -7,13 +7,13 @@ export type ChannelBarMode = "conversation" | "description" | "file-transfer" |
|
||||||
|
|
||||||
export interface ChannelBarModeData {
|
export interface ChannelBarModeData {
|
||||||
"conversation": {
|
"conversation": {
|
||||||
events: Registry<ChannelConversationUiEvents>,
|
events: IpcRegistryDescription<ChannelConversationUiEvents>,
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
events: Registry<ChannelDescriptionUiEvents>
|
events: IpcRegistryDescription<ChannelDescriptionUiEvents>
|
||||||
},
|
},
|
||||||
"file-transfer": {
|
"file-transfer": {
|
||||||
events: Registry<ChannelFileBrowserUiEvents>
|
events: IpcRegistryDescription<ChannelFileBrowserUiEvents>
|
||||||
},
|
},
|
||||||
"none": {}
|
"none": {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {ChannelBarMode, ChannelBarModeData, ChannelBarUiEvents} from "tc-shared/
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {useContext, useState} from "react";
|
import {useContext, useState} from "react";
|
||||||
import {ConversationPanel} from "tc-shared/ui/frames/side/AbstractConversationRenderer";
|
import {ConversationPanel} from "tc-shared/ui/frames/side/AbstractConversationRenderer";
|
||||||
import {useDependentState} from "tc-shared/ui/react-elements/Helper";
|
import {useDependentState, useRegistry} from "tc-shared/ui/react-elements/Helper";
|
||||||
import {ChannelDescriptionRenderer} from "tc-shared/ui/frames/side/ChannelDescriptionRenderer";
|
import {ChannelDescriptionRenderer} from "tc-shared/ui/frames/side/ChannelDescriptionRenderer";
|
||||||
import {ChannelFileBrowser} from "tc-shared/ui/frames/side/ChannelFileBrowserRenderer";
|
import {ChannelFileBrowser} from "tc-shared/ui/frames/side/ChannelFileBrowserRenderer";
|
||||||
|
|
||||||
|
@ -49,12 +49,13 @@ const ModeRenderer = () => {
|
||||||
const ModeRendererConversation = React.memo(() => {
|
const ModeRendererConversation = React.memo(() => {
|
||||||
const channelContext = useContext(ChannelContext);
|
const channelContext = useContext(ChannelContext);
|
||||||
const data = useModeData("conversation", [ channelContext ]);
|
const data = useModeData("conversation", [ channelContext ]);
|
||||||
if(!data) { return null; }
|
const events = useRegistry(data?.events);
|
||||||
|
if(!data || !events) { return null; }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConversationPanel
|
<ConversationPanel
|
||||||
key={"conversation"}
|
key={"conversation"}
|
||||||
events={data.events}
|
events={events}
|
||||||
handlerId={channelContext.handlerId}
|
handlerId={channelContext.handlerId}
|
||||||
messagesDeletable={true}
|
messagesDeletable={true}
|
||||||
noFirstMessageOverlay={false}
|
noFirstMessageOverlay={false}
|
||||||
|
@ -66,20 +67,22 @@ const ModeRendererConversation = React.memo(() => {
|
||||||
const ModeRendererDescription = React.memo(() => {
|
const ModeRendererDescription = React.memo(() => {
|
||||||
const channelContext = useContext(ChannelContext);
|
const channelContext = useContext(ChannelContext);
|
||||||
const data = useModeData("description", [ channelContext ]);
|
const data = useModeData("description", [ channelContext ]);
|
||||||
if(!data) { return null; }
|
const events = useRegistry(data?.events);
|
||||||
|
if(!data || !events) { return null; }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChannelDescriptionRenderer events={data.events} />
|
<ChannelDescriptionRenderer events={events} />
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const ModeRendererFileTransfer = React.memo(() => {
|
const ModeRendererFileTransfer = React.memo(() => {
|
||||||
const channelContext = useContext(ChannelContext);
|
const channelContext = useContext(ChannelContext);
|
||||||
const data = useModeData("file-transfer", [ channelContext ]);
|
const data = useModeData("file-transfer", [ channelContext ]);
|
||||||
if(!data) { return null; }
|
const events = useRegistry(data?.events);
|
||||||
|
if(!data || !events) { return null; }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChannelFileBrowser events={data.events} />
|
<ChannelFileBrowser events={events} />
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||||
import * as React from "react";
|
|
||||||
import * as ReactDOM from "react-dom";
|
|
||||||
import {ChannelVideoRenderer} from "tc-shared/ui/frames/video/Renderer";
|
|
||||||
import {Registry} from "tc-shared/events";
|
import {Registry} from "tc-shared/events";
|
||||||
import {
|
import {
|
||||||
ChannelVideoEvents,
|
ChannelVideoEvents,
|
||||||
ChannelVideoStreamState,
|
ChannelVideoStreamState, getVideoStreamMap,
|
||||||
kLocalVideoId,
|
kLocalVideoId,
|
||||||
VideoStreamState
|
VideoStreamState
|
||||||
} from "tc-shared/ui/frames/video/Definitions";
|
} from "tc-shared/ui/frames/video/Definitions";
|
||||||
|
@ -24,8 +21,7 @@ import * as _ from "lodash";
|
||||||
import PermissionType from "tc-shared/permission/PermissionType";
|
import PermissionType from "tc-shared/permission/PermissionType";
|
||||||
import {createErrorModal} from "tc-shared/ui/elements/Modal";
|
import {createErrorModal} from "tc-shared/ui/elements/Modal";
|
||||||
import {spawnVideoViewerInfo} from "tc-shared/ui/modal/video-viewers/Controller";
|
import {spawnVideoViewerInfo} from "tc-shared/ui/modal/video-viewers/Controller";
|
||||||
|
import {guid} from "tc-shared/crypto/uid";
|
||||||
const cssStyle = require("./Renderer.scss");
|
|
||||||
|
|
||||||
let videoIdIndex = 0;
|
let videoIdIndex = 0;
|
||||||
interface ClientVideoController {
|
interface ClientVideoController {
|
||||||
|
@ -234,7 +230,12 @@ class RemoteClientVideoController implements ClientVideoController {
|
||||||
if(!stream) {
|
if(!stream) {
|
||||||
state = { state: "failed", reason: tr("Missing video stream") };
|
state = { state: "failed", reason: tr("Missing video stream") };
|
||||||
} else {
|
} else {
|
||||||
state = { state: "connected", stream: stream };
|
const streamUuid = guid();
|
||||||
|
const streamMap = getVideoStreamMap();
|
||||||
|
streamMap[streamUuid] = stream;
|
||||||
|
setTimeout(() => delete streamMap[streamUuid], 60 * 1000);
|
||||||
|
|
||||||
|
state = { state: "connected", streamObjectId: streamUuid };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ export type VideoStreamState = {
|
||||||
reason?: string
|
reason?: string
|
||||||
} | {
|
} | {
|
||||||
state: "connected",
|
state: "connected",
|
||||||
stream: MediaStream
|
streamObjectId: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VideoSubscribeInfo = {
|
export type VideoSubscribeInfo = {
|
||||||
|
@ -173,4 +173,15 @@ export function makeVideoAutoplay(video: HTMLVideoElement) : () => void {
|
||||||
video.removeEventListener("pause", listenerPause);
|
video.removeEventListener("pause", listenerPause);
|
||||||
video.removeEventListener("ended", listenerEnded);
|
video.removeEventListener("ended", listenerEnded);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const kVideoStreamMapName = "__teaspeak_video_streams__" + __build.timestamp + "_" + __build.version;
|
||||||
|
export function getVideoStreamMap() : { [key: string]: MediaStream } {
|
||||||
|
let windowInstance = window;
|
||||||
|
while(windowInstance.opener) {
|
||||||
|
/* Are we sure about this while loop? */
|
||||||
|
windowInstance = windowInstance.opener;
|
||||||
|
}
|
||||||
|
|
||||||
|
return windowInstance[kVideoStreamMapName] || (windowInstance[kVideoStreamMapName] = {});
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@ import {ClientIcon} from "svg-sprites/client-icons";
|
||||||
import {Registry} from "tc-shared/events";
|
import {Registry} from "tc-shared/events";
|
||||||
import {
|
import {
|
||||||
ChannelVideoEvents, ChannelVideoInfo,
|
ChannelVideoEvents, ChannelVideoInfo,
|
||||||
ChannelVideoStreamState,
|
ChannelVideoStreamState, getVideoStreamMap,
|
||||||
kLocalVideoId, makeVideoAutoplay,
|
kLocalVideoId, makeVideoAutoplay,
|
||||||
VideoStreamState,
|
VideoStreamState,
|
||||||
VideoSubscribeInfo
|
VideoSubscribeInfo
|
||||||
|
@ -269,7 +269,7 @@ const MediaStreamVideoRenderer = React.memo((props: { stream: MediaStream | unde
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
const VideoStreamPlayer = (props: { videoId: string, streamType: VideoBroadcastType, className?: string }) => {
|
const VideoStreamPlayer = React.memo((props: { videoId: string, streamType: VideoBroadcastType, className?: string }) => {
|
||||||
const events = useContext(EventContext);
|
const events = useContext(EventContext);
|
||||||
const [ state, setState ] = useState<VideoStreamState>(() => {
|
const [ state, setState ] = useState<VideoStreamState>(() => {
|
||||||
events.fire("query_video_stream", { videoId: props.videoId, broadcastType: props.streamType });
|
events.fire("query_video_stream", { videoId: props.videoId, broadcastType: props.streamType });
|
||||||
|
@ -301,9 +301,20 @@ const VideoStreamPlayer = (props: { videoId: string, streamType: VideoBroadcastT
|
||||||
);
|
);
|
||||||
|
|
||||||
case "connected":
|
case "connected":
|
||||||
|
const streamMap = getVideoStreamMap();
|
||||||
|
if(typeof streamMap[state.streamObjectId] === "undefined") {
|
||||||
|
return (
|
||||||
|
<div className={cssStyle.text} key={"missing-stream-object"}>
|
||||||
|
<div>
|
||||||
|
<Translatable>Missing stream object</Translatable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MediaStreamVideoRenderer
|
<MediaStreamVideoRenderer
|
||||||
stream={state.stream}
|
stream={streamMap[state.streamObjectId]}
|
||||||
className={props.className}
|
className={props.className}
|
||||||
title={props.streamType === "camera" ? tr("Camera") : tr("Screen")}
|
title={props.streamType === "camera" ? tr("Camera") : tr("Screen")}
|
||||||
key={"connected"}
|
key={"connected"}
|
||||||
|
@ -324,7 +335,7 @@ const VideoStreamPlayer = (props: { videoId: string, streamType: VideoBroadcastT
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
const VideoPlayer = React.memo((props: { videoId: string, cameraState: ChannelVideoStreamState, screenState: ChannelVideoStreamState }) => {
|
const VideoPlayer = React.memo((props: { videoId: string, cameraState: ChannelVideoStreamState, screenState: ChannelVideoStreamState }) => {
|
||||||
const streamElements = [];
|
const streamElements = [];
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {Dispatch, SetStateAction, useEffect, useMemo, useState} from "react";
|
import {Dispatch, SetStateAction, useEffect, useMemo, useState} from "react";
|
||||||
import {RegistryKey, RegistryValueType, settings, ValuedRegistryKey} from "tc-shared/settings";
|
import {RegistryKey, RegistryValueType, settings, ValuedRegistryKey} from "tc-shared/settings";
|
||||||
|
import {IpcRegistryDescription, Registry} from "tc-events";
|
||||||
|
|
||||||
export function useDependentState<S>(
|
export function useDependentState<S>(
|
||||||
factory: (prevState?: S) => S,
|
factory: (prevState?: S) => S,
|
||||||
|
@ -42,4 +43,12 @@ export function useGlobalSetting(key, defaultValue) {
|
||||||
useEffect(() => settings.globalChangeListener(key, value => setValue(value)), []);
|
useEffect(() => settings.globalChangeListener(key, value => setValue(value)), []);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRegistry<T>(description: IpcRegistryDescription<T> | undefined) : Registry<T> | undefined;
|
||||||
|
export function useRegistry<T>(description: IpcRegistryDescription<T>) : Registry<T>
|
||||||
|
export function useRegistry(description) {
|
||||||
|
const events = useMemo(() => description ? Registry.fromIpcDescription(description) : undefined, [ description ]);
|
||||||
|
useEffect(() => () => events?.destroy(), [ description ]);
|
||||||
|
return events;
|
||||||
}
|
}
|
|
@ -150,6 +150,9 @@ export class WebWindowManager implements WindowManager {
|
||||||
});
|
});
|
||||||
|
|
||||||
const features = {
|
const features = {
|
||||||
|
/* TODO: Configureable and enabled by default! */
|
||||||
|
noopener: "no",
|
||||||
|
|
||||||
status: "no",
|
status: "no",
|
||||||
location: "no",
|
location: "no",
|
||||||
toolbar: "no",
|
toolbar: "no",
|
||||||
|
|
Loading…
Add table
Reference in a new issue