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