The sidebar now is completely disconnected from the connection handler itself and only renders what's needed.
parent
cb505d39cf
commit
787de619de
|
@ -7,6 +7,7 @@
|
|||
- Enabled context menus for all clickable client tags
|
||||
- Allowing to drag client tags
|
||||
- Fixed the context menu within popout windows for the web client
|
||||
- Reworked the whole sidebar (Hightly decreased memory footprint)
|
||||
|
||||
* **08.12.20**
|
||||
- Fixed the permission editor not resolving unique ids
|
||||
|
|
|
@ -13,7 +13,6 @@ import * as htmltags from "./ui/htmltags";
|
|||
import {FilterMode, InputState, MediaStreamRequestResult} from "./voice/RecorderBase";
|
||||
import {CommandResult} from "./connection/ServerConnectionDeclaration";
|
||||
import {defaultRecorder, RecorderProfile} from "./voice/RecorderProfile";
|
||||
import {Frame} from "./ui/frames/chat_frame";
|
||||
import {Hostbanner} from "./ui/frames/hostbanner";
|
||||
import {connection_log, Regex} from "./ui/modal/ModalConnect";
|
||||
import {formatMessage} from "./ui/frames/chat";
|
||||
|
@ -40,7 +39,8 @@ import {ChannelVideoFrame} from "tc-shared/ui/frames/video/Controller";
|
|||
import {global_client_actions} from "tc-shared/events/GlobalEvents";
|
||||
import {ChannelConversationManager} from "./conversations/ChannelConversationManager";
|
||||
import {PrivateConversationManager} from "tc-shared/conversations/PrivateConversationManager";
|
||||
import {ChannelConversationController} from "./ui/frames/side/ChannelConversationController";
|
||||
import {SelectedClientInfo} from "./SelectedClientInfo";
|
||||
import {SideBarManager} from "tc-shared/SideBarManager";
|
||||
|
||||
export enum InputHardwareState {
|
||||
MISSING,
|
||||
|
@ -145,7 +145,6 @@ export class ConnectionHandler {
|
|||
permissions: PermissionManager;
|
||||
groups: GroupManager;
|
||||
|
||||
side_bar: Frame;
|
||||
video_frame: ChannelVideoFrame;
|
||||
|
||||
settings: ServerSettings;
|
||||
|
@ -157,9 +156,13 @@ export class ConnectionHandler {
|
|||
|
||||
serverFeatures: ServerFeatures;
|
||||
|
||||
private sideBar: SideBarManager;
|
||||
|
||||
private channelConversations: ChannelConversationManager;
|
||||
private privateConversations: PrivateConversationManager;
|
||||
|
||||
private clientInfoManager: SelectedClientInfo;
|
||||
|
||||
private _clientId: number = 0;
|
||||
private localClient: LocalClientEntry;
|
||||
|
||||
|
@ -211,14 +214,15 @@ export class ConnectionHandler {
|
|||
this.fileManager = new FileManager(this);
|
||||
this.permissions = new PermissionManager(this);
|
||||
|
||||
this.sideBar = new SideBarManager(this);
|
||||
this.privateConversations = new PrivateConversationManager(this);
|
||||
this.channelConversations = new ChannelConversationManager(this);
|
||||
this.clientInfoManager = new SelectedClientInfo(this);
|
||||
|
||||
this.pluginCmdRegistry = new PluginCmdRegistry(this);
|
||||
this.video_frame = new ChannelVideoFrame(this);
|
||||
|
||||
this.log = new ServerEventLog(this);
|
||||
this.side_bar = new Frame(this);
|
||||
this.sound = new SoundManager(this);
|
||||
this.hostbanner = new Hostbanner(this);
|
||||
|
||||
|
@ -358,6 +362,14 @@ export class ConnectionHandler {
|
|||
return this.channelConversations;
|
||||
}
|
||||
|
||||
getSelectedClientInfo() : SelectedClientInfo {
|
||||
return this.clientInfoManager;
|
||||
}
|
||||
|
||||
getSideBar() : SideBarManager {
|
||||
return this.sideBar;
|
||||
}
|
||||
|
||||
initializeLocalClient(clientId: number, acceptedName: string) {
|
||||
this._clientId = clientId;
|
||||
this.localClient["_clientId"] = clientId;
|
||||
|
@ -1042,8 +1054,11 @@ export class ConnectionHandler {
|
|||
this.channelTree?.destroy();
|
||||
this.channelTree = undefined;
|
||||
|
||||
this.side_bar?.destroy();
|
||||
this.side_bar = undefined;
|
||||
this.sideBar?.destroy();
|
||||
this.sideBar = undefined;
|
||||
|
||||
this.clientInfoManager?.destroy();
|
||||
this.clientInfoManager = undefined;
|
||||
|
||||
this.log?.destroy();
|
||||
this.log = undefined;
|
||||
|
|
|
@ -5,6 +5,7 @@ import {Stage} from "tc-loader";
|
|||
import {FooterRenderer} from "tc-shared/ui/frames/footer/Renderer";
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import {SideBarController} from "tc-shared/ui/frames/SideBarController";
|
||||
|
||||
export let server_connections: ConnectionManager;
|
||||
|
||||
|
@ -36,17 +37,21 @@ export class ConnectionManager {
|
|||
private containerSideBar: HTMLDivElement;
|
||||
private containerFooter: HTMLDivElement;
|
||||
|
||||
private sideBarController: SideBarController;
|
||||
|
||||
constructor() {
|
||||
this.event_registry = new Registry<ConnectionManagerEvents>();
|
||||
this.event_registry.enableDebug("connection-manager");
|
||||
|
||||
this.sideBarController = new SideBarController();
|
||||
|
||||
this.containerChannelVideo = new ReplaceableContainer(document.getElementById("channel-video") as HTMLDivElement);
|
||||
this.containerSideBar = document.getElementById("chat") as HTMLDivElement;
|
||||
this._container_log_server = $("#server-log");
|
||||
this._container_channel_tree = $("#channelTree");
|
||||
this._container_hostbanner = $("#hostbanner");
|
||||
this.containerFooter = document.getElementById("container-footer") as HTMLDivElement;
|
||||
|
||||
this.sideBarController.renderInto(document.getElementById("chat") as HTMLDivElement);
|
||||
this.set_active_connection(undefined);
|
||||
}
|
||||
|
||||
|
@ -111,6 +116,8 @@ export class ConnectionManager {
|
|||
}
|
||||
|
||||
private set_active_connection_(handler: ConnectionHandler) {
|
||||
this.sideBarController.setConnection(handler);
|
||||
|
||||
this._container_channel_tree.children().detach();
|
||||
this._container_log_server.children().detach();
|
||||
this._container_hostbanner.children().detach();
|
||||
|
@ -120,8 +127,8 @@ export class ConnectionManager {
|
|||
this._container_hostbanner.append(handler.hostbanner.html_tag);
|
||||
this._container_channel_tree.append(handler.channelTree.tag_tree());
|
||||
this._container_log_server.append(handler.log.getHTMLTag());
|
||||
handler.side_bar.renderInto(this.containerSideBar);
|
||||
}
|
||||
|
||||
const old_handler = this.active_handler;
|
||||
this.active_handler = handler;
|
||||
this.event_registry.fire("notify_active_handler_changed", {
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
import {ConnectionHandler, ConnectionState} from "tc-shared/ConnectionHandler";
|
||||
import {
|
||||
ClientForumInfo,
|
||||
ClientInfoType,
|
||||
ClientStatusInfo,
|
||||
ClientVersionInfo
|
||||
} from "tc-shared/ui/frames/side/ClientInfoDefinitions";
|
||||
import {ClientEntry, ClientType, LocalClientEntry} from "tc-shared/tree/Client";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import * as i18nc from "tc-shared/i18n/country";
|
||||
|
||||
export type CachedClientInfoCategory = "name" | "description" | "online-state" | "country" | "volume" | "status" | "forum-account" | "group-channel" | "groups-server" | "version";
|
||||
|
||||
export type CachedClientInfo = {
|
||||
type: ClientInfoType;
|
||||
name: string,
|
||||
uniqueId: string,
|
||||
databaseId: number,
|
||||
clientId: number,
|
||||
|
||||
description: string,
|
||||
joinTimestamp: number,
|
||||
leaveTimestamp: number,
|
||||
country: { name: string, flag: string },
|
||||
volume: { volume: number, muted: boolean },
|
||||
status: ClientStatusInfo,
|
||||
forumAccount: ClientForumInfo | undefined,
|
||||
channelGroup: number,
|
||||
serverGroups: number[],
|
||||
version: ClientVersionInfo
|
||||
}
|
||||
|
||||
export interface ClientInfoManagerEvents {
|
||||
notify_client_changed: { newClient: ClientEntry | undefined },
|
||||
notify_cache_changed: { category: CachedClientInfoCategory },
|
||||
}
|
||||
|
||||
export class SelectedClientInfo {
|
||||
readonly events: Registry<ClientInfoManagerEvents>;
|
||||
|
||||
private readonly connection: ConnectionHandler;
|
||||
private readonly listenerConnection: (() => void)[];
|
||||
|
||||
private listenerClient: (() => void)[];
|
||||
private currentClient: ClientEntry | undefined;
|
||||
private currentClientStatus: CachedClientInfo | undefined;
|
||||
|
||||
constructor(connection: ConnectionHandler) {
|
||||
this.connection = connection;
|
||||
this.events = new Registry<ClientInfoManagerEvents>();
|
||||
|
||||
this.listenerClient = [];
|
||||
this.listenerConnection = [];
|
||||
this.listenerConnection.push(connection.channelTree.events.on("notify_client_leave_view", event => {
|
||||
if(event.client !== this.currentClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentClientStatus.leaveTimestamp = Date.now() / 1000;
|
||||
this.currentClientStatus.clientId = 0;
|
||||
this.currentClient = undefined;
|
||||
this.unregisterClientEvents();
|
||||
this.events.fire("notify_cache_changed", { category: "online-state" });
|
||||
}));
|
||||
|
||||
this.listenerConnection.push(connection.events().on("notify_connection_state_changed", event => {
|
||||
if(event.newState !== ConnectionState.CONNECTED && this.currentClientStatus) {
|
||||
this.currentClient = undefined;
|
||||
this.currentClientStatus.leaveTimestamp = Date.now() / 1000;
|
||||
this.events.fire("notify_cache_changed", { category: "online-state" });
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.listenerConnection.forEach(callback => callback());
|
||||
this.listenerConnection.splice(0, this.listenerConnection.length);
|
||||
|
||||
this.unregisterClientEvents();
|
||||
}
|
||||
|
||||
getInfo() : CachedClientInfo {
|
||||
return this.currentClientStatus;
|
||||
}
|
||||
|
||||
setClient(client: ClientEntry | undefined) {
|
||||
if(this.currentClient === client) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(client.channelTree.client !== this.connection) {
|
||||
throw tr("client does not belong to current connection handler");
|
||||
}
|
||||
|
||||
this.unregisterClientEvents();
|
||||
this.currentClient = client;
|
||||
this.currentClientStatus = undefined;
|
||||
if(this.currentClient) {
|
||||
this.currentClient.updateClientVariables().then(undefined);
|
||||
this.registerClientEvents(this.currentClient);
|
||||
this.initializeClientInfo(this.currentClient);
|
||||
}
|
||||
|
||||
this.events.fire("notify_client_changed", { newClient: client });
|
||||
}
|
||||
|
||||
getClient() : ClientEntry | undefined {
|
||||
return this.currentClient;
|
||||
}
|
||||
|
||||
private unregisterClientEvents() {
|
||||
this.listenerClient.forEach(callback => callback());
|
||||
this.listenerClient = [];
|
||||
}
|
||||
|
||||
private registerClientEvents(client: ClientEntry) {
|
||||
const events = this.listenerClient;
|
||||
|
||||
events.push(client.events.on("notify_properties_updated", event => {
|
||||
if('client_nickname' in event.updated_properties) {
|
||||
this.currentClientStatus.name = event.client_properties.client_nickname;
|
||||
this.events.fire("notify_cache_changed", { category: "name" });
|
||||
}
|
||||
|
||||
if('client_description' in event.updated_properties) {
|
||||
this.currentClientStatus.description = event.client_properties.client_description;
|
||||
this.events.fire("notify_cache_changed", { category: "description" });
|
||||
}
|
||||
|
||||
if('client_channel_group_id' in event.updated_properties) {
|
||||
this.currentClientStatus.channelGroup = event.client_properties.client_channel_group_id;
|
||||
this.events.fire("notify_cache_changed", { category: "group-channel" });
|
||||
}
|
||||
|
||||
if('client_servergroups' in event.updated_properties) {
|
||||
this.currentClientStatus.serverGroups = client.assignedServerGroupIds();
|
||||
this.events.fire("notify_cache_changed", { category: "groups-server" });
|
||||
}
|
||||
|
||||
/* Can happen since that variable isn't in view on client appearance */
|
||||
if('client_lastconnected' in event.updated_properties) {
|
||||
this.currentClientStatus.joinTimestamp = event.client_properties.client_lastconnected;
|
||||
this.events.fire("notify_cache_changed", { category: "online-state" });
|
||||
}
|
||||
|
||||
if('client_country' in event.updated_properties) {
|
||||
this.updateCachedCountry(client);
|
||||
this.events.fire("notify_cache_changed", { category: "country" });
|
||||
}
|
||||
|
||||
for(const key of ["client_away", "client_away_message", "client_input_muted", "client_input_hardware", "client_output_muted", "client_output_hardware"]) {
|
||||
if(key in event.updated_properties) {
|
||||
this.updateCachedClientStatus(client);
|
||||
this.events.fire("notify_cache_changed", { category: "status" });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if('client_platform' in event.updated_properties || 'client_version' in event.updated_properties) {
|
||||
this.currentClientStatus.version = {
|
||||
platform: client.properties.client_platform,
|
||||
version: client.properties.client_version
|
||||
};
|
||||
this.events.fire("notify_cache_changed", { category: "version" });
|
||||
}
|
||||
|
||||
if('client_teaforo_flags' in event.updated_properties || 'client_teaforo_name' in event.updated_properties || 'client_teaforo_id' in event.updated_properties) {
|
||||
this.updateForumAccount(client);
|
||||
this.events.fire("notify_cache_changed", { category: "forum-account" });
|
||||
}
|
||||
}));
|
||||
|
||||
events.push(client.events.on("notify_audio_level_changed", () => {
|
||||
this.updateCachedVolume(client);
|
||||
this.events.fire("notify_cache_changed", { category: "volume" });
|
||||
}));
|
||||
|
||||
events.push(client.events.on("notify_mute_state_change", () => {
|
||||
this.updateCachedVolume(client);
|
||||
this.events.fire("notify_cache_changed", { category: "volume" });
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
private updateCachedClientStatus(client: ClientEntry) {
|
||||
this.currentClientStatus.status = {
|
||||
away: client.properties.client_away ? client.properties.client_away_message ? client.properties.client_away_message : true : false,
|
||||
microphoneMuted: client.properties.client_input_muted,
|
||||
microphoneDisabled: !client.properties.client_input_hardware,
|
||||
speakerMuted: client.properties.client_output_muted,
|
||||
speakerDisabled: !client.properties.client_output_hardware
|
||||
};
|
||||
}
|
||||
|
||||
private updateCachedCountry(client: ClientEntry) {
|
||||
this.currentClientStatus.country = {
|
||||
flag: client.properties.client_country,
|
||||
name: i18nc.country_name(client.properties.client_country.toUpperCase()),
|
||||
};
|
||||
}
|
||||
|
||||
private updateCachedVolume(client: ClientEntry) {
|
||||
this.currentClientStatus.volume = {
|
||||
volume: client.getAudioVolume(),
|
||||
muted: client.isMuted()
|
||||
}
|
||||
}
|
||||
|
||||
private updateForumAccount(client: ClientEntry) {
|
||||
if(client.properties.client_teaforo_id) {
|
||||
this.currentClientStatus.forumAccount = {
|
||||
flags: client.properties.client_teaforo_flags,
|
||||
nickname: client.properties.client_teaforo_name,
|
||||
userId: client.properties.client_teaforo_id
|
||||
};
|
||||
} else {
|
||||
this.currentClientStatus.forumAccount = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private initializeClientInfo(client: ClientEntry) {
|
||||
this.currentClientStatus = {
|
||||
type: client instanceof LocalClientEntry ? "self" : client.properties.client_type === ClientType.CLIENT_QUERY ? "query" : "voice",
|
||||
name: client.properties.client_nickname,
|
||||
databaseId: client.properties.client_database_id,
|
||||
uniqueId: client.properties.client_unique_identifier,
|
||||
clientId: client.clientId(),
|
||||
|
||||
description: client.properties.client_description,
|
||||
channelGroup: client.properties.client_channel_group_id,
|
||||
serverGroups: client.assignedServerGroupIds(),
|
||||
country: undefined,
|
||||
forumAccount: undefined,
|
||||
joinTimestamp: client.properties.client_lastconnected,
|
||||
leaveTimestamp: 0,
|
||||
status: undefined,
|
||||
volume: undefined,
|
||||
version: {
|
||||
platform: client.properties.client_platform,
|
||||
version: client.properties.client_version
|
||||
}
|
||||
};
|
||||
this.updateCachedClientStatus(client);
|
||||
this.updateCachedCountry(client);
|
||||
this.updateCachedVolume(client);
|
||||
this.updateForumAccount(client);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
import {SideBarType} from "tc-shared/ui/frames/SideBarDefinitions";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {ClientEntry, MusicClientEntry} from "tc-shared/tree/Client";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
|
||||
export interface SideBarManagerEvents {
|
||||
notify_content_type_changed: { newContent: SideBarType }
|
||||
}
|
||||
|
||||
export class SideBarManager {
|
||||
readonly events: Registry<SideBarManagerEvents>;
|
||||
private readonly connection: ConnectionHandler;
|
||||
private currentType: SideBarType;
|
||||
|
||||
|
||||
constructor(connection: ConnectionHandler) {
|
||||
this.events = new Registry<SideBarManagerEvents>();
|
||||
this.connection = connection;
|
||||
this.currentType = "channel-chat";
|
||||
}
|
||||
|
||||
destroy() {}
|
||||
|
||||
getSideBarContent() : SideBarType {
|
||||
return this.currentType;
|
||||
}
|
||||
|
||||
setSideBarContent(content: SideBarType) {
|
||||
if(this.currentType === content) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentType = content;
|
||||
this.events.fire("notify_content_type_changed", { newContent: content });
|
||||
}
|
||||
|
||||
showPrivateConversations() {
|
||||
this.setSideBarContent("private-chat");
|
||||
}
|
||||
|
||||
showChannelConversations() {
|
||||
this.setSideBarContent("channel-chat");
|
||||
}
|
||||
|
||||
showClientInfo(client: ClientEntry) {
|
||||
this.connection.getSelectedClientInfo().setClient(client);
|
||||
this.setSideBarContent("client-info");
|
||||
}
|
||||
|
||||
showMusicPlayer(_client: MusicClientEntry) {
|
||||
/* FIXME: TODO! */
|
||||
this.setSideBarContent("music-manage");
|
||||
}
|
||||
|
||||
clearSideBar() {
|
||||
this.setSideBarContent("none");
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ import {
|
|||
ChatMessage,
|
||||
ChatState,
|
||||
ConversationHistoryResponse
|
||||
} from "tc-shared/ui/frames/side/ConversationDefinitions";
|
||||
} from "../ui/frames/side/AbstractConversationDefinitions";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {preprocessChatMessageForSend} from "tc-shared/text/chat";
|
||||
|
@ -16,7 +16,7 @@ import {guid} from "tc-shared/crypto/uid";
|
|||
|
||||
export const kMaxChatFrameMessageSize = 50; /* max 100 messages, since the server does not support more than 100 messages queried at once */
|
||||
|
||||
export interface AbstractChatEvents {
|
||||
export interface AbstractConversationEvents {
|
||||
notify_chat_event: {
|
||||
triggerUnread: boolean,
|
||||
event: ChatEvent
|
||||
|
@ -45,7 +45,7 @@ export interface AbstractChatEvents {
|
|||
}
|
||||
}
|
||||
|
||||
export abstract class AbstractChat<Events extends AbstractChatEvents> {
|
||||
export abstract class AbstractChat<Events extends AbstractConversationEvents> {
|
||||
readonly events: Registry<Events>;
|
||||
|
||||
protected readonly connection: ConnectionHandler;
|
||||
|
@ -341,7 +341,7 @@ export interface AbstractChatManagerEvents<ConversationType> {
|
|||
}
|
||||
}
|
||||
|
||||
export abstract class AbstractChatManager<ManagerEvents extends AbstractChatManagerEvents<ConversationType>, ConversationType extends AbstractChat<ConversationEvents>, ConversationEvents extends AbstractChatEvents> {
|
||||
export abstract class AbstractChatManager<ManagerEvents extends AbstractChatManagerEvents<ConversationType>, ConversationType extends AbstractChat<ConversationEvents>, ConversationEvents extends AbstractConversationEvents> {
|
||||
readonly events: Registry<ManagerEvents>;
|
||||
readonly connection: ConnectionHandler;
|
||||
protected readonly listenerConnection: (() => void)[];
|
||||
|
@ -351,6 +351,12 @@ export abstract class AbstractChatManager<ManagerEvents extends AbstractChatMana
|
|||
private selectedConversation: ConversationType;
|
||||
|
||||
private currentUnreadCount: number;
|
||||
/* FIXME: Access modifier */
|
||||
public historyUiStates: {[id: string]: {
|
||||
executingUIHistoryQuery: boolean,
|
||||
historyErrorMessage: string | undefined,
|
||||
historyRetryTimestamp: number
|
||||
}} = {};
|
||||
|
||||
protected constructor(connection: ConnectionHandler) {
|
||||
this.events = new Registry<ManagerEvents>();
|
||||
|
@ -418,6 +424,8 @@ export abstract class AbstractChatManager<ManagerEvents extends AbstractChatMana
|
|||
this.setSelectedConversation(undefined);
|
||||
}
|
||||
|
||||
delete this.historyUiStates[conversation.getChatId()];
|
||||
|
||||
conversation.events.off("notify_unread_state_changed", this.listenerUnreadTimestamp);
|
||||
delete this.conversations[conversation.getChatId()];
|
||||
this.events.fire("notify_conversation_destroyed", { conversation: conversation });
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {
|
||||
AbstractChat,
|
||||
AbstractChatEvents,
|
||||
AbstractConversationEvents,
|
||||
AbstractChatManager,
|
||||
AbstractChatManagerEvents,
|
||||
kMaxChatFrameMessageSize
|
||||
} from "./AbstractConversion";
|
||||
import {ChatMessage, ConversationHistoryResponse} from "tc-shared/ui/frames/side/ConversationDefinitions";
|
||||
import {ChatMessage, ConversationHistoryResponse} from "../ui/frames/side/AbstractConversationDefinitions";
|
||||
import {Settings} from "tc-shared/settings";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {ErrorCode} from "tc-shared/connection/ErrorCode";
|
||||
|
@ -17,7 +17,7 @@ import {LocalClientEntry} from "tc-shared/tree/Client";
|
|||
import {ServerCommand} from "tc-shared/connection/ConnectionBase";
|
||||
import {ChannelConversationMode} from "tc-shared/tree/Channel";
|
||||
|
||||
export interface ChannelConversationEvents extends AbstractChatEvents {
|
||||
export interface ChannelConversationEvents extends AbstractConversationEvents {
|
||||
notify_messages_deleted: { messages: string[] },
|
||||
notify_messages_loaded: {}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as loader from "tc-loader";
|
|||
import {Stage} from "tc-loader";
|
||||
import { tr } from "tc-shared/i18n/localize";
|
||||
import {LogCategory, logDebug, logError, logInfo, logWarn} from "tc-shared/log";
|
||||
import {ChatEvent} from "tc-shared/ui/frames/side/ConversationDefinitions";
|
||||
import {ChatEvent} from "../ui/frames/side/AbstractConversationDefinitions";
|
||||
|
||||
const clientUniqueId2StoreName = uniqueId => "conversation-" + uniqueId;
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {
|
||||
AbstractChat,
|
||||
AbstractChatEvents,
|
||||
AbstractConversationEvents,
|
||||
AbstractChatManager,
|
||||
AbstractChatManagerEvents
|
||||
} from "tc-shared/conversations/AbstractConversion";
|
||||
import {ClientEntry} from "tc-shared/tree/Client";
|
||||
import {ChatEvent, ChatMessage, ConversationHistoryResponse} from "tc-shared/ui/frames/side/ConversationDefinitions";
|
||||
import {ChatEvent, ChatMessage, ConversationHistoryResponse} from "../ui/frames/side/AbstractConversationDefinitions";
|
||||
import {ChannelTreeEvents} from "tc-shared/tree/ChannelTree";
|
||||
import {queryConversationEvents, registerConversationEvent} from "tc-shared/conversations/PrivateConversationHistory";
|
||||
import {LogCategory, logWarn} from "tc-shared/log";
|
||||
|
@ -19,7 +19,7 @@ export type OutOfViewClient = {
|
|||
|
||||
let receivingEventUniqueIdIndex = 0;
|
||||
|
||||
export interface PrivateConversationEvents extends AbstractChatEvents {
|
||||
export interface PrivateConversationEvents extends AbstractConversationEvents {
|
||||
notify_partner_typing: {},
|
||||
notify_partner_changed: {
|
||||
chatId: string,
|
||||
|
|
|
@ -412,7 +412,7 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
|||
callback: () => {
|
||||
const conversation = this.channelTree.client.getChannelConversations().findOrCreateConversation(this.getChannelId());
|
||||
this.channelTree.client.getChannelConversations().setSelectedConversation(conversation);
|
||||
this.channelTree.client.side_bar.showChannelConversations();
|
||||
this.channelTree.client.getSideBar().showChannelConversations();
|
||||
},
|
||||
visible: !settings.static_global(Settings.KEY_SWITCH_INSTANT_CHAT)
|
||||
}, {
|
||||
|
|
|
@ -168,23 +168,22 @@ export class ChannelTree {
|
|||
if(this.selectedEntry instanceof ClientEntry) {
|
||||
if(settings.static_global(Settings.KEY_SWITCH_INSTANT_CLIENT)) {
|
||||
if(this.selectedEntry instanceof MusicClientEntry) {
|
||||
this.client.side_bar.showMusicPlayer(this.selectedEntry);
|
||||
this.client.getSideBar().showMusicPlayer(this.selectedEntry);
|
||||
} else {
|
||||
this.client.side_bar.showClientInfo(this.selectedEntry);
|
||||
this.client.getSideBar().showClientInfo(this.selectedEntry);
|
||||
}
|
||||
}
|
||||
} else if(this.selectedEntry instanceof ChannelEntry) {
|
||||
if(settings.static_global(Settings.KEY_SWITCH_INSTANT_CHAT)) {
|
||||
const conversation = this.client.getChannelConversations().findOrCreateConversation(this.selectedEntry.channelId);
|
||||
this.client.getChannelConversations().setSelectedConversation(conversation);
|
||||
this.client.side_bar.showChannelConversations();
|
||||
this.client.getSideBar().showChannelConversations();
|
||||
}
|
||||
} else if(this.selectedEntry instanceof ServerEntry) {
|
||||
if(settings.static_global(Settings.KEY_SWITCH_INSTANT_CHAT)) {
|
||||
const sidebar = this.client.side_bar;
|
||||
const conversation = this.client.getChannelConversations().findOrCreateConversation(0);
|
||||
this.client.getChannelConversations().setSelectedConversation(conversation);
|
||||
sidebar.showChannelConversations();
|
||||
this.client.getSideBar().showChannelConversations()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -382,7 +382,7 @@ export class ClientEntry extends ChannelTreeEntry<ClientEvents> {
|
|||
type: contextmenu.MenuEntryType.ENTRY,
|
||||
name: this.properties.client_type_exact === ClientType.CLIENT_MUSIC ? tr("Show bot info") : tr("Show client info"),
|
||||
callback: () => {
|
||||
this.channelTree.client.side_bar.showClientInfo(this);
|
||||
this.channelTree.client.getSideBar().showClientInfo(this);
|
||||
},
|
||||
icon_class: "client-about",
|
||||
visible: !settings.static_global(Settings.KEY_SWITCH_INSTANT_CLIENT)
|
||||
|
@ -519,12 +519,13 @@ export class ClientEntry extends ChannelTreeEntry<ClientEvents> {
|
|||
|
||||
open_text_chat() {
|
||||
const privateConversations = this.channelTree.client.getPrivateConversations();
|
||||
const sideBar = this.channelTree.client.side_bar;
|
||||
const conversation = privateConversations.findOrCreateConversation(this);
|
||||
conversation.setActiveClientEntry(this);
|
||||
privateConversations.setSelectedConversation(conversation);
|
||||
sideBar.showPrivateConversations();
|
||||
sideBar.privateConversationsController().focusInput();
|
||||
|
||||
this.channelTree.client.getSideBar().showPrivateConversations();
|
||||
/* FIXME: Draw focus to the input box! */
|
||||
//sideBar.privateConversationsController().focusInput();
|
||||
}
|
||||
|
||||
showContextMenu(x: number, y: number, on_close: () => void = undefined) {
|
||||
|
|
|
@ -193,7 +193,7 @@ export class ServerEntry extends ChannelTreeEntry<ServerEvents> {
|
|||
name: tr("Join server text channel"),
|
||||
callback: () => {
|
||||
this.channelTree.client.getChannelConversations().setSelectedConversation(this.channelTree.client.getChannelConversations().findOrCreateConversation(0));
|
||||
this.channelTree.client.side_bar.showChannelConversations();
|
||||
this.channelTree.client.getSideBar().showChannelConversations();
|
||||
},
|
||||
visible: !settings.static_global(Settings.KEY_SWITCH_INSTANT_CHAT)
|
||||
}, {
|
||||
|
|
|
@ -1,61 +1,64 @@
|
|||
import {ClientEntry, MusicClientEntry} from "../../tree/Client";
|
||||
import {ConnectionHandler} from "../../ConnectionHandler";
|
||||
import {MusicInfo} from "../../ui/frames/side/music_info";
|
||||
import {ChannelConversationController} from "./side/ChannelConversationController";
|
||||
import {PrivateConversationController} from "./side/PrivateConversationController";
|
||||
import {ClientInfoController} from "tc-shared/ui/frames/side/ClientInfoController";
|
||||
import {SideHeader} from "tc-shared/ui/frames/side/HeaderController";
|
||||
import {SideHeaderController} from "tc-shared/ui/frames/side/HeaderController";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import {SideBarRenderer} from "tc-shared/ui/frames/SideBarRenderer";
|
||||
import * as React from "react";
|
||||
import {SideBarEvents, SideBarType} from "tc-shared/ui/frames/SideBarDefinitions";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {LogCategory, logWarn} from "tc-shared/log";
|
||||
|
||||
const cssStyle = require("./SideBar.scss");
|
||||
export class SideBarController {
|
||||
private readonly uiEvents: Registry<SideBarEvents>;
|
||||
|
||||
export class Frame {
|
||||
readonly handle: ConnectionHandler;
|
||||
private htmlTag: HTMLDivElement;
|
||||
private currentConnection: ConnectionHandler;
|
||||
private listenerConnection: (() => void)[];
|
||||
|
||||
private currentType: SideBarType;
|
||||
|
||||
private uiEvents: Registry<SideBarEvents>;
|
||||
private header: SideHeader;
|
||||
|
||||
private musicInfo: MusicInfo;
|
||||
private header: SideHeaderController;
|
||||
private clientInfo: ClientInfoController;
|
||||
private channelConversations: ChannelConversationController;
|
||||
private privateConversations: PrivateConversationController;
|
||||
|
||||
constructor(handle: ConnectionHandler) {
|
||||
this.handle = handle;
|
||||
constructor() {
|
||||
this.listenerConnection = [];
|
||||
|
||||
this.currentType = "none";
|
||||
this.uiEvents = new Registry<SideBarEvents>();
|
||||
this.uiEvents.on("query_content", () => this.uiEvents.fire_react("notify_content", { content: this.currentType }));
|
||||
this.uiEvents.on("query_content", () => this.sendContent());
|
||||
this.uiEvents.on("query_content_data", event => this.sendContentData(event.content));
|
||||
|
||||
this.privateConversations = new PrivateConversationController(handle);
|
||||
this.channelConversations = new ChannelConversationController(handle);
|
||||
this.clientInfo = new ClientInfoController(handle);
|
||||
this.musicInfo = new MusicInfo(this);
|
||||
this.header = new SideHeader();
|
||||
|
||||
this.handle.events().one("notify_handler_initialized", () => this.header.setConnectionHandler(handle));
|
||||
|
||||
this.createHtmlTag();
|
||||
this.showChannelConversations();
|
||||
this.privateConversations = new PrivateConversationController();
|
||||
this.channelConversations = new ChannelConversationController();
|
||||
this.clientInfo = new ClientInfoController();
|
||||
this.header = new SideHeaderController();
|
||||
}
|
||||
|
||||
html_tag() : HTMLDivElement { return this.htmlTag; }
|
||||
setConnection(connection: ConnectionHandler) {
|
||||
if(this.currentConnection === connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.listenerConnection.forEach(callback => callback());
|
||||
this.listenerConnection = [];
|
||||
|
||||
this.currentConnection = connection;
|
||||
this.header.setConnectionHandler(connection);
|
||||
this.clientInfo.setConnectionHandler(connection);
|
||||
this.channelConversations.setConnectionHandler(connection);
|
||||
this.privateConversations.setConnectionHandler(connection);
|
||||
|
||||
if(connection) {
|
||||
this.listenerConnection.push(connection.getSideBar().events.on("notify_content_type_changed", () => this.sendContent()));
|
||||
}
|
||||
|
||||
this.sendContent();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.header?.destroy();
|
||||
this.header = undefined;
|
||||
|
||||
this.htmlTag && this.htmlTag.remove();
|
||||
this.htmlTag = undefined;
|
||||
|
||||
this.clientInfo?.destroy();
|
||||
this.clientInfo = undefined;
|
||||
|
||||
|
@ -64,51 +67,21 @@ export class Frame {
|
|||
|
||||
this.channelConversations?.destroy();
|
||||
this.channelConversations = undefined;
|
||||
|
||||
this.musicInfo && this.musicInfo.destroy();
|
||||
this.musicInfo = undefined;
|
||||
|
||||
this.privateConversations && this.privateConversations.destroy();
|
||||
this.privateConversations = undefined;
|
||||
|
||||
this.channelConversations && this.channelConversations.destroy();
|
||||
this.channelConversations = undefined;
|
||||
}
|
||||
|
||||
renderInto(container: HTMLDivElement) {
|
||||
ReactDOM.render(React.createElement(SideBarRenderer, {
|
||||
key: this.handle.handlerId,
|
||||
handlerId: this.handle.handlerId,
|
||||
events: this.uiEvents,
|
||||
eventsHeader: this.header["uiEvents"],
|
||||
}), container);
|
||||
}
|
||||
|
||||
private createHtmlTag() {
|
||||
this.htmlTag = document.createElement("div");
|
||||
this.htmlTag.classList.add(cssStyle.container);
|
||||
private sendContent() {
|
||||
if(this.currentConnection) {
|
||||
this.uiEvents.fire("notify_content", { content: this.currentConnection.getSideBar().getSideBarContent() });
|
||||
} else {
|
||||
this.uiEvents.fire("notify_content", { content: "none" });
|
||||
}
|
||||
|
||||
|
||||
privateConversationsController() : PrivateConversationController {
|
||||
return this.privateConversations;
|
||||
}
|
||||
|
||||
getClientInfo() : ClientInfoController {
|
||||
return this.clientInfo;
|
||||
}
|
||||
|
||||
music_info() : MusicInfo {
|
||||
return this.musicInfo;
|
||||
}
|
||||
|
||||
private setCurrentContent(type: SideBarType) {
|
||||
if(this.currentType === type) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentType = type;
|
||||
this.uiEvents.fire_react("notify_content", { content: this.currentType });
|
||||
}
|
||||
|
||||
private sendContentData(content: SideBarType) {
|
||||
|
@ -121,26 +94,41 @@ export class Frame {
|
|||
break;
|
||||
|
||||
case "channel-chat":
|
||||
if(!this.currentConnection) {
|
||||
logWarn(LogCategory.GENERAL, tr("Received channel chat content data request without an active connection."));
|
||||
return;
|
||||
}
|
||||
|
||||
this.uiEvents.fire_react("notify_content_data", {
|
||||
content: "channel-chat",
|
||||
data: {
|
||||
events: this.channelConversations["uiEvents"],
|
||||
handlerId: this.handle.handlerId
|
||||
handlerId: this.currentConnection.handlerId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case "private-chat":
|
||||
if(!this.currentConnection) {
|
||||
logWarn(LogCategory.GENERAL, tr("Received private chat content data request without an active connection."));
|
||||
return;
|
||||
}
|
||||
|
||||
this.uiEvents.fire_react("notify_content_data", {
|
||||
content: "private-chat",
|
||||
data: {
|
||||
events: this.privateConversations["uiEvents"],
|
||||
handlerId: this.handle.handlerId
|
||||
handlerId: this.currentConnection.handlerId
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case "client-info":
|
||||
if(!this.currentConnection) {
|
||||
logWarn(LogCategory.GENERAL, tr("Received client info content data request without an active connection."));
|
||||
return;
|
||||
}
|
||||
|
||||
this.uiEvents.fire_react("notify_content_data", {
|
||||
content: "client-info",
|
||||
data: {
|
||||
|
@ -150,6 +138,11 @@ export class Frame {
|
|||
break;
|
||||
|
||||
case "music-manage":
|
||||
if(!this.currentConnection) {
|
||||
logWarn(LogCategory.GENERAL, tr("Received music bot content data request without an active connection."));
|
||||
return;
|
||||
}
|
||||
|
||||
this.uiEvents.fire_react("notify_content_data", {
|
||||
content: "music-manage",
|
||||
data: { }
|
||||
|
@ -157,26 +150,4 @@ export class Frame {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
showPrivateConversations() {
|
||||
this.setCurrentContent("private-chat");
|
||||
}
|
||||
|
||||
showChannelConversations() {
|
||||
this.setCurrentContent("channel-chat");
|
||||
}
|
||||
|
||||
showClientInfo(client: ClientEntry) {
|
||||
this.clientInfo.setClient(client);
|
||||
this.setCurrentContent("client-info");
|
||||
}
|
||||
|
||||
showMusicPlayer(client: MusicClientEntry) {
|
||||
this.musicInfo.set_current_bot(client);
|
||||
this.setCurrentContent("music-manage");
|
||||
}
|
||||
|
||||
clearSideBar() {
|
||||
this.setCurrentContent("none");
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import {Registry} from "tc-shared/events";
|
||||
import {PrivateConversationUIEvents} from "tc-shared/ui/frames/side/PrivateConversationDefinitions";
|
||||
import {ConversationUIEvents} from "tc-shared/ui/frames/side/ConversationDefinitions";
|
||||
import {AbstractConversationUiEvents} from "./side/AbstractConversationDefinitions";
|
||||
import {ClientInfoEvents} from "tc-shared/ui/frames/side/ClientInfoDefinitions";
|
||||
import {SideHeaderEvents} from "tc-shared/ui/frames/side/HeaderDefinitions";
|
||||
|
||||
/* TODO: Somehow outsource the event registries to IPC? */
|
||||
|
||||
|
@ -9,7 +10,7 @@ export type SideBarType = "none" | "channel-chat" | "private-chat" | "client-inf
|
|||
export interface SideBarTypeData {
|
||||
"none": {},
|
||||
"channel-chat": {
|
||||
events: Registry<ConversationUIEvents>,
|
||||
events: Registry<AbstractConversationUiEvents>,
|
||||
handlerId: string
|
||||
},
|
||||
"private-chat": {
|
||||
|
@ -32,7 +33,9 @@ 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_content_data: SideBarNotifyContentData<SideBarType>,
|
||||
notify_header_data: { events: Registry<SideHeaderEvents> }
|
||||
}
|
|
@ -8,7 +8,7 @@ import {useContext, useState} from "react";
|
|||
import {ClientInfoRenderer} from "tc-shared/ui/frames/side/ClientInfoRenderer";
|
||||
import {PrivateConversationsPanel} from "tc-shared/ui/frames/side/PrivateConversationRenderer";
|
||||
|
||||
const cssStyle = require("./SideBar.scss");
|
||||
const cssStyle = require("./SideBarRenderer.scss");
|
||||
|
||||
const EventContent = React.createContext<Registry<SideBarEvents>>(undefined);
|
||||
|
||||
|
@ -109,7 +109,6 @@ const SideBarHeader = (props: { type: SideBarType, eventsHeader: Registry<SideHe
|
|||
}
|
||||
|
||||
export const SideBarRenderer = (props: {
|
||||
handlerId: string,
|
||||
events: Registry<SideBarEvents>,
|
||||
eventsHeader: Registry<SideHeaderEvents>
|
||||
}) => {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import {
|
||||
ChatHistoryState,
|
||||
ConversationUIEvents
|
||||
} from "../../../ui/frames/side/ConversationDefinitions";
|
||||
AbstractConversationUiEvents
|
||||
} from "./AbstractConversationDefinitions";
|
||||
import {EventHandler, Registry} from "../../../events";
|
||||
import * as log from "../../../log";
|
||||
import {LogCategory} from "../../../log";
|
||||
import {tra, tr} from "../../../i18n/localize";
|
||||
import {
|
||||
AbstractChat,
|
||||
AbstractChatEvents,
|
||||
AbstractConversationEvents,
|
||||
AbstractChatManager,
|
||||
AbstractChatManagerEvents
|
||||
} from "tc-shared/conversations/AbstractConversion";
|
||||
|
@ -16,50 +16,25 @@ import {
|
|||
export const kMaxChatFrameMessageSize = 50; /* max 100 messages, since the server does not support more than 100 messages queried at once */
|
||||
|
||||
export abstract class AbstractConversationController<
|
||||
Events extends ConversationUIEvents,
|
||||
Events extends AbstractConversationUiEvents,
|
||||
Manager extends AbstractChatManager<ManagerEvents, ConversationType, ConversationEvents>,
|
||||
ManagerEvents extends AbstractChatManagerEvents<ConversationType>,
|
||||
ConversationType extends AbstractChat<ConversationEvents>,
|
||||
ConversationEvents extends AbstractChatEvents
|
||||
ConversationEvents extends AbstractConversationEvents
|
||||
> {
|
||||
protected readonly uiEvents: Registry<Events>;
|
||||
protected readonly conversationManager: Manager;
|
||||
protected readonly listenerManager: (() => void)[];
|
||||
|
||||
private historyUiStates: {[id: string]: {
|
||||
executingUIHistoryQuery: boolean,
|
||||
historyErrorMessage: string | undefined,
|
||||
historyRetryTimestamp: number
|
||||
}} = {};
|
||||
protected conversationManager: Manager | undefined;
|
||||
protected listenerManager: (() => void)[];
|
||||
|
||||
protected currentSelectedConversation: ConversationType;
|
||||
protected currentSelectedListener: (() => void)[];
|
||||
|
||||
protected crossChannelChatSupported = true;
|
||||
|
||||
protected constructor(conversationManager: Manager) {
|
||||
protected constructor() {
|
||||
this.uiEvents = new Registry<Events>();
|
||||
this.currentSelectedListener = [];
|
||||
this.conversationManager = conversationManager;
|
||||
|
||||
this.listenerManager = [];
|
||||
|
||||
this.listenerManager.push(this.conversationManager.events.on("notify_selected_changed", event => {
|
||||
this.currentSelectedListener.forEach(callback => callback());
|
||||
this.currentSelectedListener = [];
|
||||
|
||||
this.currentSelectedConversation = event.newConversation;
|
||||
this.uiEvents.fire_react("notify_selected_chat", { chatId: event.newConversation ? event.newConversation.getChatId() : "unselected" });
|
||||
|
||||
const conversation = event.newConversation;
|
||||
if(conversation) {
|
||||
this.registerConversationEvents(conversation);
|
||||
}
|
||||
}));
|
||||
|
||||
this.listenerManager.push(this.conversationManager.events.on("notify_conversation_destroyed", event => {
|
||||
delete this.historyUiStates[event.conversation.getChatId()];
|
||||
}));
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
@ -70,6 +45,31 @@ export abstract class AbstractConversationController<
|
|||
this.uiEvents.destroy();
|
||||
}
|
||||
|
||||
getUiEvents() : Registry<Events> {
|
||||
return this.uiEvents;
|
||||
}
|
||||
|
||||
protected setConversationManager(manager: Manager | undefined) {
|
||||
if(this.conversationManager === manager) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.listenerManager.forEach(callback => callback());
|
||||
this.listenerManager = [];
|
||||
this.conversationManager = manager;
|
||||
|
||||
if(manager) {
|
||||
this.registerConversationManagerEvents(manager);
|
||||
this.setCurrentlySelected(manager.getSelectedConversation());
|
||||
} else {
|
||||
this.setCurrentlySelected(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
protected registerConversationManagerEvents(manager: Manager) {
|
||||
this.listenerManager.push(manager.events.on("notify_selected_changed", event => this.setCurrentlySelected(event.newConversation)));
|
||||
}
|
||||
|
||||
protected registerConversationEvents(conversation: ConversationType) {
|
||||
this.currentSelectedListener.push(conversation.events.on("notify_unread_timestamp_changed", event =>
|
||||
this.uiEvents.fire_react("notify_unread_timestamp_changed", { chatId: conversation.getChatId(), timestamp: event.timestamp })));
|
||||
|
@ -94,13 +94,30 @@ export abstract class AbstractConversationController<
|
|||
}));
|
||||
}
|
||||
|
||||
protected setCurrentlySelected(conversation: ConversationType | undefined) {
|
||||
if(this.currentSelectedConversation === conversation) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentSelectedListener.forEach(callback => callback());
|
||||
this.currentSelectedListener = [];
|
||||
|
||||
this.currentSelectedConversation = conversation;
|
||||
this.uiEvents.fire_react("notify_selected_chat", { chatId: conversation ? conversation.getChatId() : "unselected" });
|
||||
|
||||
if(conversation) {
|
||||
this.registerConversationEvents(conversation);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Is this even a thing? */
|
||||
handlePanelShow() {
|
||||
this.uiEvents.fire_react("notify_panel_show");
|
||||
}
|
||||
|
||||
protected reportStateToUI(conversation: AbstractChat<any>) {
|
||||
let historyState: ChatHistoryState;
|
||||
const localHistoryState = this.historyUiStates[conversation.getChatId()];
|
||||
const localHistoryState = this.conversationManager.historyUiStates[conversation.getChatId()];
|
||||
if(!localHistoryState) {
|
||||
historyState = conversation.hasHistory() ? "available" : "none";
|
||||
} else {
|
||||
|
@ -171,7 +188,7 @@ export abstract class AbstractConversationController<
|
|||
}
|
||||
}
|
||||
public uiQueryHistory(conversation: AbstractChat<any>, timestamp: number, enforce?: boolean) {
|
||||
const localHistoryState = this.historyUiStates[conversation.getChatId()] || (this.historyUiStates[conversation.getChatId()] = {
|
||||
const localHistoryState = this.conversationManager.historyUiStates[conversation.getChatId()] || (this.conversationManager.historyUiStates[conversation.getChatId()] = {
|
||||
executingUIHistoryQuery: false,
|
||||
historyErrorMessage: undefined,
|
||||
historyRetryTimestamp: 0
|
||||
|
@ -242,13 +259,13 @@ export abstract class AbstractConversationController<
|
|||
this.crossChannelChatSupported = flag;
|
||||
const currentConversation = this.getCurrentConversation();
|
||||
if(currentConversation) {
|
||||
this.reportStateToUI(this.getCurrentConversation());
|
||||
this.reportStateToUI(currentConversation);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("query_conversation_state")
|
||||
protected handleQueryConversationState(event: ConversationUIEvents["query_conversation_state"]) {
|
||||
const conversation = this.conversationManager.findConversationById(event.chatId);
|
||||
@EventHandler<AbstractConversationUiEvents>("query_conversation_state")
|
||||
protected handleQueryConversationState(event: AbstractConversationUiEvents["query_conversation_state"]) {
|
||||
const conversation = this.conversationManager?.findConversationById(event.chatId);
|
||||
if(!conversation) {
|
||||
this.uiEvents.fire_react("notify_conversation_state", {
|
||||
state: "error",
|
||||
|
@ -267,9 +284,9 @@ export abstract class AbstractConversationController<
|
|||
}
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("query_conversation_history")
|
||||
protected handleQueryHistory(event: ConversationUIEvents["query_conversation_history"]) {
|
||||
const conversation = this.conversationManager.findConversationById(event.chatId);
|
||||
@EventHandler<AbstractConversationUiEvents>("query_conversation_history")
|
||||
protected handleQueryHistory(event: AbstractConversationUiEvents["query_conversation_history"]) {
|
||||
const conversation = this.conversationManager?.findConversationById(event.chatId);
|
||||
if(!conversation) {
|
||||
this.uiEvents.fire_react("notify_conversation_history", {
|
||||
state: "error",
|
||||
|
@ -286,15 +303,15 @@ export abstract class AbstractConversationController<
|
|||
this.uiQueryHistory(conversation, event.timestamp);
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>(["action_clear_unread_flag", "action_self_typing"])
|
||||
protected handleClearUnreadFlag(event: ConversationUIEvents["action_clear_unread_flag" | "action_self_typing"]) {
|
||||
const conversation = this.conversationManager.findConversationById(event.chatId);
|
||||
@EventHandler<AbstractConversationUiEvents>(["action_clear_unread_flag", "action_self_typing"])
|
||||
protected handleClearUnreadFlag(event: AbstractConversationUiEvents["action_clear_unread_flag" | "action_self_typing"]) {
|
||||
const conversation = this.conversationManager?.findConversationById(event.chatId);
|
||||
conversation?.setUnreadTimestamp(Date.now());
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("action_send_message")
|
||||
protected handleSendMessage(event: ConversationUIEvents["action_send_message"]) {
|
||||
const conversation = this.conversationManager.findConversationById(event.chatId);
|
||||
@EventHandler<AbstractConversationUiEvents>("action_send_message")
|
||||
protected handleSendMessage(event: AbstractConversationUiEvents["action_send_message"]) {
|
||||
const conversation = this.conversationManager?.findConversationById(event.chatId);
|
||||
if(!conversation) {
|
||||
log.error(LogCategory.CLIENT, tr("Tried to send a chat message to an unknown conversation with id %s"), event.chatId);
|
||||
return;
|
||||
|
@ -303,9 +320,9 @@ export abstract class AbstractConversationController<
|
|||
conversation.sendMessage(event.text);
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("action_jump_to_present")
|
||||
protected handleJumpToPresent(event: ConversationUIEvents["action_jump_to_present"]) {
|
||||
const conversation = this.conversationManager.findConversationById(event.chatId);
|
||||
@EventHandler<AbstractConversationUiEvents>("action_jump_to_present")
|
||||
protected handleJumpToPresent(event: AbstractConversationUiEvents["action_jump_to_present"]) {
|
||||
const conversation = this.conversationManager?.findConversationById(event.chatId);
|
||||
if(!conversation) {
|
||||
log.error(LogCategory.CLIENT, tr("Tried to jump to present for an unknown conversation with id %s"), event.chatId);
|
||||
return;
|
||||
|
@ -314,14 +331,14 @@ export abstract class AbstractConversationController<
|
|||
this.reportStateToUI(conversation);
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("query_selected_chat")
|
||||
@EventHandler<AbstractConversationUiEvents>("query_selected_chat")
|
||||
private handleQuerySelectedChat() {
|
||||
this.uiEvents.fire_react("notify_selected_chat", { chatId: this.currentSelectedConversation ? this.currentSelectedConversation.getChatId() : "unselected"})
|
||||
this.uiEvents.fire_react("notify_selected_chat", { chatId: this.currentSelectedConversation ? this.currentSelectedConversation.getChatId() : "unselected"});
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("action_select_chat")
|
||||
private handleActionSelectChat(event: ConversationUIEvents["action_select_chat"]) {
|
||||
const conversation = this.conversationManager.findConversationById(event.chatId);
|
||||
@EventHandler<AbstractConversationUiEvents>("action_select_chat")
|
||||
private handleActionSelectChat(event: AbstractConversationUiEvents["action_select_chat"]) {
|
||||
const conversation = this.conversationManager?.findConversationById(event.chatId);
|
||||
this.conversationManager.setSelectedConversation(conversation);
|
||||
}
|
||||
}
|
|
@ -111,7 +111,7 @@ export interface ChatStatePrivate {
|
|||
|
||||
export type ChatStateData = ChatStateNormal | ChatStateNoPermissions | ChatStateError | ChatStateLoading | ChatStatePrivate;
|
||||
|
||||
export interface ConversationUIEvents {
|
||||
export interface AbstractConversationUiEvents {
|
||||
action_select_chat: { chatId: "unselected" | string },
|
||||
action_clear_unread_flag: { chatId: string },
|
||||
action_self_typing: { chatId: string },
|
|
@ -15,8 +15,8 @@ import {
|
|||
ChatEventPartnerAction,
|
||||
ChatHistoryState,
|
||||
ChatMessage,
|
||||
ConversationUIEvents, ChatEventModeChanged
|
||||
} from "tc-shared/ui/frames/side/ConversationDefinitions";
|
||||
AbstractConversationUiEvents, ChatEventModeChanged
|
||||
} from "./AbstractConversationDefinitions";
|
||||
import {TimestampRenderer} from "tc-shared/ui/react-elements/TimestampRenderer";
|
||||
import {BBCodeRenderer} from "tc-shared/text/bbcode";
|
||||
import {getGlobalAvatarManagerFactory} from "tc-shared/file/Avatars";
|
||||
|
@ -34,7 +34,7 @@ const ChatMessageTextRenderer = React.memo((props: { text: string }) => {
|
|||
const ChatEventMessageRenderer = React.memo((props: {
|
||||
message: ChatMessage,
|
||||
callbackDelete?: () => void,
|
||||
events: Registry<ConversationUIEvents>,
|
||||
events: Registry<AbstractConversationUiEvents>,
|
||||
handlerId: string,
|
||||
|
||||
refHTMLElement?: Ref<HTMLDivElement>
|
||||
|
@ -126,7 +126,7 @@ const UnreadEntry = (props: { refDiv: React.Ref<HTMLDivElement> }) => (
|
|||
</div>
|
||||
);
|
||||
|
||||
const LoadOderMessages = (props: { events: Registry<ConversationUIEvents>, chatId: string, state: ChatHistoryState | "error", errorMessage?: string, retryTimestamp?: number, timestamp: number | undefined }) => {
|
||||
const LoadOderMessages = (props: { events: Registry<AbstractConversationUiEvents>, chatId: string, state: ChatHistoryState | "error", errorMessage?: string, retryTimestamp?: number, timestamp: number | undefined }) => {
|
||||
if(props.state === "none")
|
||||
return null;
|
||||
|
||||
|
@ -172,7 +172,7 @@ const LoadOderMessages = (props: { events: Registry<ConversationUIEvents>, chatI
|
|||
)
|
||||
};
|
||||
|
||||
const JumpToPresent = (props: { events: Registry<ConversationUIEvents>, chatId: string }) => (
|
||||
const JumpToPresent = (props: { events: Registry<AbstractConversationUiEvents>, chatId: string }) => (
|
||||
<div
|
||||
className={cssStyle.containerLoadMessages + " " + cssStyle.present}
|
||||
onClick={() => props.events.fire("action_jump_to_present", { chatId: props.chatId })}
|
||||
|
@ -305,7 +305,7 @@ const ChatEventModeChangedRenderer = (props: { event: ChatEventModeChanged, refH
|
|||
}
|
||||
}
|
||||
|
||||
const PartnerTypingIndicator = (props: { events: Registry<ConversationUIEvents>, chatId: string, timeout?: number }) => {
|
||||
const PartnerTypingIndicator = (props: { events: Registry<AbstractConversationUiEvents>, chatId: string, timeout?: number }) => {
|
||||
const kTypingTimeout = props.timeout || 5000;
|
||||
|
||||
|
||||
|
@ -349,7 +349,7 @@ const PartnerTypingIndicator = (props: { events: Registry<ConversationUIEvents>,
|
|||
};
|
||||
|
||||
interface ConversationMessagesProperties {
|
||||
events: Registry<ConversationUIEvents>;
|
||||
events: Registry<AbstractConversationUiEvents>;
|
||||
handlerId: string;
|
||||
|
||||
noFirstMessageOverlay?: boolean
|
||||
|
@ -698,8 +698,8 @@ class ConversationMessages extends React.PureComponent<ConversationMessagesPrope
|
|||
}
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("notify_selected_chat")
|
||||
private handleNotifySelectedChat(event: ConversationUIEvents["notify_selected_chat"]) {
|
||||
@EventHandler<AbstractConversationUiEvents>("notify_selected_chat")
|
||||
private handleNotifySelectedChat(event: AbstractConversationUiEvents["notify_selected_chat"]) {
|
||||
if(this.currentChatId === event.chatId) {
|
||||
return;
|
||||
}
|
||||
|
@ -718,8 +718,8 @@ class ConversationMessages extends React.PureComponent<ConversationMessagesPrope
|
|||
}
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("notify_conversation_state")
|
||||
private handleConversationStateUpdate(event: ConversationUIEvents["notify_conversation_state"]) {
|
||||
@EventHandler<AbstractConversationUiEvents>("notify_conversation_state")
|
||||
private handleConversationStateUpdate(event: AbstractConversationUiEvents["notify_conversation_state"]) {
|
||||
if(event.chatId !== this.currentChatId)
|
||||
return;
|
||||
|
||||
|
@ -771,8 +771,8 @@ class ConversationMessages extends React.PureComponent<ConversationMessagesPrope
|
|||
}
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("notify_chat_event")
|
||||
private handleChatEvent(event: ConversationUIEvents["notify_chat_event"]) {
|
||||
@EventHandler<AbstractConversationUiEvents>("notify_chat_event")
|
||||
private handleChatEvent(event: AbstractConversationUiEvents["notify_chat_event"]) {
|
||||
if(event.chatId !== this.currentChatId || this.state.isBrowsingHistory)
|
||||
return;
|
||||
|
||||
|
@ -793,8 +793,8 @@ class ConversationMessages extends React.PureComponent<ConversationMessagesPrope
|
|||
this.forceUpdate(() => this.scrollToBottom());
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("notify_chat_message_delete")
|
||||
private handleMessageDeleted(event: ConversationUIEvents["notify_chat_message_delete"]) {
|
||||
@EventHandler<AbstractConversationUiEvents>("notify_chat_message_delete")
|
||||
private handleMessageDeleted(event: AbstractConversationUiEvents["notify_chat_message_delete"]) {
|
||||
if(event.chatId !== this.currentChatId) {
|
||||
return;
|
||||
}
|
||||
|
@ -805,8 +805,8 @@ class ConversationMessages extends React.PureComponent<ConversationMessagesPrope
|
|||
this.forceUpdate(() => this.scrollToBottom());
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("notify_unread_timestamp_changed")
|
||||
private handleUnreadTimestampChanged(event: ConversationUIEvents["notify_unread_timestamp_changed"]) {
|
||||
@EventHandler<AbstractConversationUiEvents>("notify_unread_timestamp_changed")
|
||||
private handleUnreadTimestampChanged(event: AbstractConversationUiEvents["notify_unread_timestamp_changed"]) {
|
||||
if (event.chatId !== this.currentChatId)
|
||||
return;
|
||||
|
||||
|
@ -823,13 +823,13 @@ class ConversationMessages extends React.PureComponent<ConversationMessagesPrope
|
|||
}
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("notify_panel_show")
|
||||
@EventHandler<AbstractConversationUiEvents>("notify_panel_show")
|
||||
private handlePanelShow() {
|
||||
this.fixScroll();
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("query_conversation_history")
|
||||
private handleQueryConversationHistory(event: ConversationUIEvents["query_conversation_history"]) {
|
||||
@EventHandler<AbstractConversationUiEvents>("query_conversation_history")
|
||||
private handleQueryConversationHistory(event: AbstractConversationUiEvents["query_conversation_history"]) {
|
||||
if (event.chatId !== this.currentChatId)
|
||||
return;
|
||||
|
||||
|
@ -838,8 +838,8 @@ class ConversationMessages extends React.PureComponent<ConversationMessagesPrope
|
|||
});
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("notify_conversation_history")
|
||||
private handleNotifyConversationHistory(event: ConversationUIEvents["notify_conversation_history"]) {
|
||||
@EventHandler<AbstractConversationUiEvents>("notify_conversation_history")
|
||||
private handleNotifyConversationHistory(event: AbstractConversationUiEvents["notify_conversation_history"]) {
|
||||
if (event.chatId !== this.currentChatId)
|
||||
return;
|
||||
|
||||
|
@ -881,7 +881,7 @@ class ConversationMessages extends React.PureComponent<ConversationMessagesPrope
|
|||
}
|
||||
}
|
||||
|
||||
export const ConversationPanel = React.memo((props: { events: Registry<ConversationUIEvents>, handlerId: string, messagesDeletable: boolean, noFirstMessageOverlay: boolean }) => {
|
||||
export const ConversationPanel = React.memo((props: { events: Registry<AbstractConversationUiEvents>, handlerId: string, messagesDeletable: boolean, noFirstMessageOverlay: boolean }) => {
|
||||
const currentChat = useRef({ id: "unselected" });
|
||||
const chatEnabled = useRef(false);
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import * as React from "react";
|
||||
import {ConnectionHandler, ConnectionState} from "../../../ConnectionHandler";
|
||||
import {EventHandler} from "../../../events";
|
||||
import * as log from "../../../log";
|
||||
import {LogCategory} from "../../../log";
|
||||
import {tr} from "../../../i18n/localize";
|
||||
import {ConversationUIEvents} from "../../../ui/frames/side/ConversationDefinitions";
|
||||
import {ConversationPanel} from "./AbstractConversationRenderer";
|
||||
import {AbstractConversationUiEvents} from "./AbstractConversationDefinitions";
|
||||
import {AbstractConversationController} from "./AbstractConversationController";
|
||||
import {
|
||||
ChannelConversation,
|
||||
|
@ -14,34 +12,22 @@ import {
|
|||
ChannelConversationManagerEvents
|
||||
} from "tc-shared/conversations/ChannelConversationManager";
|
||||
import {ServerFeature} from "tc-shared/connection/ServerFeatures";
|
||||
import ReactDOM = require("react-dom");
|
||||
import {ChannelConversationUiEvents} from "tc-shared/ui/frames/side/ChannelConversationDefinitions";
|
||||
|
||||
export class ChannelConversationController extends AbstractConversationController<
|
||||
ConversationUIEvents,
|
||||
ChannelConversationUiEvents,
|
||||
ChannelConversationManager,
|
||||
ChannelConversationManagerEvents,
|
||||
ChannelConversation,
|
||||
ChannelConversationEvents
|
||||
> {
|
||||
readonly connection: ConnectionHandler;
|
||||
readonly htmlTag: HTMLDivElement;
|
||||
private connection: ConnectionHandler;
|
||||
private connectionListener: (() => void)[];
|
||||
|
||||
constructor(connection: ConnectionHandler) {
|
||||
super(connection.getChannelConversations() as any);
|
||||
this.connection = connection;
|
||||
constructor() {
|
||||
super();
|
||||
this.connectionListener = [];
|
||||
|
||||
this.htmlTag = document.createElement("div");
|
||||
this.htmlTag.style.display = "flex";
|
||||
this.htmlTag.style.flexDirection = "column";
|
||||
this.htmlTag.style.justifyContent = "stretch";
|
||||
this.htmlTag.style.height = "100%";
|
||||
|
||||
ReactDOM.render(React.createElement(ConversationPanel, {
|
||||
events: this.uiEvents,
|
||||
handlerId: this.connection.handlerId,
|
||||
noFirstMessageOverlay: false,
|
||||
messagesDeletable: true
|
||||
}), this.htmlTag);
|
||||
/*
|
||||
spawnExternalModal("conversation", this.uiEvents, {
|
||||
handlerId: this.connection.handlerId,
|
||||
|
@ -52,7 +38,37 @@ export class ChannelConversationController extends AbstractConversationControlle
|
|||
});
|
||||
*/
|
||||
|
||||
this.uiEvents.on("notify_destroy", connection.events().on("notify_visibility_changed", event => {
|
||||
this.uiEvents.register_handler(this, true);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.connectionListener.forEach(callback => callback());
|
||||
this.connectionListener = [];
|
||||
|
||||
this.uiEvents.unregister_handler(this);
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
setConnectionHandler(connection: ConnectionHandler) {
|
||||
if(this.connection === connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.connectionListener.forEach(callback => callback());
|
||||
this.connectionListener = [];
|
||||
|
||||
this.connection = connection;
|
||||
if(connection) {
|
||||
this.initializeConnectionListener(connection);
|
||||
/* FIXME: Update cross channel talk state! */
|
||||
this.setConversationManager(connection.getChannelConversations());
|
||||
} else {
|
||||
this.setConversationManager(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
private initializeConnectionListener(connection: ConnectionHandler) {
|
||||
this.connectionListener.push(connection.events().on("notify_visibility_changed", event => {
|
||||
if(!event.visible) {
|
||||
return;
|
||||
}
|
||||
|
@ -60,9 +76,7 @@ export class ChannelConversationController extends AbstractConversationControlle
|
|||
this.handlePanelShow();
|
||||
}));
|
||||
|
||||
this.uiEvents.register_handler(this, true);
|
||||
|
||||
this.listenerManager.push(connection.events().on("notify_connection_state_changed", event => {
|
||||
this.connectionListener.push(connection.events().on("notify_connection_state_changed", event => {
|
||||
if(event.newState === ConnectionState.CONNECTED) {
|
||||
connection.serverFeatures.awaitFeatures().then(success => {
|
||||
if(!success) { return; }
|
||||
|
@ -75,17 +89,9 @@ export class ChannelConversationController extends AbstractConversationControlle
|
|||
}));
|
||||
}
|
||||
|
||||
destroy() {
|
||||
ReactDOM.unmountComponentAtNode(this.htmlTag);
|
||||
this.htmlTag.remove();
|
||||
|
||||
this.uiEvents.unregister_handler(this);
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("action_delete_message")
|
||||
private handleMessageDelete(event: ConversationUIEvents["action_delete_message"]) {
|
||||
const conversation = this.conversationManager.findConversationById(event.chatId);
|
||||
@EventHandler<AbstractConversationUiEvents>("action_delete_message")
|
||||
private handleMessageDelete(event: AbstractConversationUiEvents["action_delete_message"]) {
|
||||
const conversation = this.conversationManager?.findConversationById(event.chatId);
|
||||
if(!conversation) {
|
||||
log.error(LogCategory.CLIENT, tr("Tried to delete a chat message from an unknown conversation with id %s"), event.chatId);
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import {AbstractConversationUiEvents} from "tc-shared/ui/frames/side/AbstractConversationDefinitions";
|
||||
|
||||
export interface ChannelConversationUiEvents extends AbstractConversationUiEvents {}
|
|
@ -1,107 +1,23 @@
|
|||
import {ConnectionHandler, ConnectionState} from "tc-shared/ConnectionHandler";
|
||||
import {ClientEntry, ClientType, LocalClientEntry} from "tc-shared/tree/Client";
|
||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {
|
||||
ClientForumInfo,
|
||||
ClientGroupInfo,
|
||||
ClientInfoEvents, ClientInfoType,
|
||||
ClientStatusInfo,
|
||||
ClientVersionInfo
|
||||
ClientInfoEvents,
|
||||
} from "tc-shared/ui/frames/side/ClientInfoDefinitions";
|
||||
|
||||
import {Registry} from "tc-shared/events";
|
||||
import * as i18nc from "../../../i18n/country";
|
||||
import {openClientInfo} from "tc-shared/ui/modal/ModalClientInfo";
|
||||
|
||||
type CurrentClientInfo = {
|
||||
type: ClientInfoType;
|
||||
name: string,
|
||||
uniqueId: string,
|
||||
databaseId: number,
|
||||
clientId: number,
|
||||
|
||||
description: string,
|
||||
joinTimestamp: number,
|
||||
leaveTimestamp: number,
|
||||
country: { name: string, flag: string },
|
||||
volume: { volume: number, muted: boolean },
|
||||
status: ClientStatusInfo,
|
||||
forumAccount: ClientForumInfo | undefined,
|
||||
channelGroup: number,
|
||||
serverGroups: number[],
|
||||
version: ClientVersionInfo
|
||||
}
|
||||
|
||||
export interface ClientInfoControllerEvents {
|
||||
notify_client_changed: {
|
||||
newClient: ClientEntry | undefined
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientInfoController {
|
||||
readonly events: Registry<ClientInfoControllerEvents>;
|
||||
|
||||
private readonly connection: ConnectionHandler;
|
||||
private readonly listenerConnection: (() => void)[];
|
||||
|
||||
private readonly uiEvents: Registry<ClientInfoEvents>;
|
||||
|
||||
private listenerClient: (() => void)[];
|
||||
private currentClient: ClientEntry | undefined;
|
||||
private currentClientStatus: CurrentClientInfo | undefined;
|
||||
private connection: ConnectionHandler;
|
||||
private listenerConnection: (() => void)[];
|
||||
|
||||
constructor(connection: ConnectionHandler) {
|
||||
this.connection = connection;
|
||||
this.events = new Registry<ClientInfoControllerEvents>();
|
||||
constructor() {
|
||||
this.uiEvents = new Registry<ClientInfoEvents>();
|
||||
this.uiEvents.enableDebug("client-info");
|
||||
|
||||
this.listenerConnection = [];
|
||||
this.listenerClient = [];
|
||||
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
private initialize() {
|
||||
this.listenerConnection.push(this.connection.groups.events.on("notify_groups_updated", event => {
|
||||
if(!this.currentClientStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(const update of event.updates) {
|
||||
if(update.group.id === this.currentClientStatus.channelGroup) {
|
||||
this.sendChannelGroup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(const update of event.updates) {
|
||||
if(this.currentClientStatus.serverGroups.indexOf(update.group.id) !== -1) {
|
||||
this.sendServerGroups();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this.listenerConnection.push(this.connection.channelTree.events.on("notify_client_leave_view", event => {
|
||||
if(event.client !== this.currentClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentClientStatus.leaveTimestamp = Date.now() / 1000;
|
||||
this.currentClientStatus.clientId = 0;
|
||||
this.currentClient = undefined;
|
||||
this.unregisterClientEvents();
|
||||
this.sendOnline();
|
||||
}));
|
||||
|
||||
this.listenerConnection.push(this.connection.events().on("notify_connection_state_changed", event => {
|
||||
if(event.newState !== ConnectionState.CONNECTED && this.currentClientStatus) {
|
||||
this.currentClient = undefined;
|
||||
this.currentClientStatus.leaveTimestamp = Date.now() / 1000;
|
||||
this.sendOnline();
|
||||
}
|
||||
}))
|
||||
|
||||
this.uiEvents.on("query_client", () => this.sendClient());
|
||||
this.uiEvents.on("query_client_name", () => this.sendClientName());
|
||||
this.uiEvents.on("query_client_description", () => this.sendClientDescription());
|
||||
|
@ -114,179 +30,105 @@ export class ClientInfoController {
|
|||
this.uiEvents.on("query_version", () => this.sendVersion());
|
||||
this.uiEvents.on("query_forum", () => this.sendForum());
|
||||
|
||||
this.uiEvents.on("action_edit_avatar", () => this.connection.update_avatar());
|
||||
this.uiEvents.on("action_show_full_info", () => this.currentClient && openClientInfo(this.currentClient));
|
||||
this.uiEvents.on("action_edit_avatar", () => this.connection?.update_avatar());
|
||||
this.uiEvents.on("action_show_full_info", () => {
|
||||
const client = this.connection?.getSelectedClientInfo().getClient();
|
||||
if(client) {
|
||||
openClientInfo(client);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private unregisterClientEvents() {
|
||||
this.listenerClient.forEach(callback => callback());
|
||||
this.listenerClient = [];
|
||||
destroy() {
|
||||
this.listenerConnection.forEach(callback => callback());
|
||||
this.listenerConnection = [];
|
||||
}
|
||||
|
||||
private registerClientEvents(client: ClientEntry) {
|
||||
const events = this.listenerClient;
|
||||
|
||||
events.push(client.events.on("notify_properties_updated", event => {
|
||||
if('client_nickname' in event.updated_properties) {
|
||||
this.currentClientStatus.name = event.client_properties.client_nickname;
|
||||
this.sendClientName();
|
||||
setConnectionHandler(connection: ConnectionHandler) {
|
||||
if(this.connection === connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
if('client_description' in event.updated_properties) {
|
||||
this.currentClientStatus.description = event.client_properties.client_description;
|
||||
this.sendClientDescription();
|
||||
this.listenerConnection.forEach(callback => callback());
|
||||
this.listenerConnection = [];
|
||||
|
||||
this.connection = connection;
|
||||
if(connection) {
|
||||
this.initializeConnection(connection);
|
||||
}
|
||||
this.sendClient();
|
||||
}
|
||||
|
||||
if('client_channel_group_id' in event.updated_properties) {
|
||||
this.currentClientStatus.channelGroup = event.client_properties.client_channel_group_id;
|
||||
private initializeConnection(connection: ConnectionHandler) {
|
||||
this.listenerConnection.push(connection.groups.events.on("notify_groups_updated", event => {
|
||||
const info = this.connection?.getSelectedClientInfo().getInfo();
|
||||
if(!info) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(const update of event.updates) {
|
||||
if(update.group.id === info.channelGroup) {
|
||||
this.sendChannelGroup();
|
||||
}
|
||||
|
||||
if('client_servergroups' in event.updated_properties) {
|
||||
this.currentClientStatus.serverGroups = client.assignedServerGroupIds();
|
||||
this.sendServerGroups();
|
||||
}
|
||||
|
||||
/* Can happen since that variable isn't in view on client appearance */
|
||||
if('client_lastconnected' in event.updated_properties) {
|
||||
this.currentClientStatus.joinTimestamp = event.client_properties.client_lastconnected;
|
||||
this.sendOnline();
|
||||
}
|
||||
|
||||
if('client_country' in event.updated_properties) {
|
||||
this.updateCachedCountry(client);
|
||||
this.sendCountry();
|
||||
}
|
||||
|
||||
for(const key of ["client_away", "client_away_message", "client_input_muted", "client_input_hardware", "client_output_muted", "client_output_hardware"]) {
|
||||
if(key in event.updated_properties) {
|
||||
this.updateCachedClientStatus(client);
|
||||
this.sendClientStatus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if('client_platform' in event.updated_properties || 'client_version' in event.updated_properties) {
|
||||
this.currentClientStatus.version = {
|
||||
platform: client.properties.client_platform,
|
||||
version: client.properties.client_version
|
||||
};
|
||||
this.sendVersion();
|
||||
for(const update of event.updates) {
|
||||
if(info.serverGroups.indexOf(update.group.id) !== -1) {
|
||||
this.sendServerGroups();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
if('client_teaforo_flags' in event.updated_properties || 'client_teaforo_name' in event.updated_properties || 'client_teaforo_id' in event.updated_properties) {
|
||||
this.updateForumAccount(client);
|
||||
this.listenerConnection.push(connection.getSelectedClientInfo().events.on("notify_cache_changed", event => {
|
||||
switch (event.category) {
|
||||
case "name":
|
||||
this.sendClientName();
|
||||
break;
|
||||
|
||||
case "country":
|
||||
this.sendCountry();
|
||||
break;
|
||||
|
||||
case "description":
|
||||
this.sendClientDescription();
|
||||
break;
|
||||
|
||||
case "forum-account":
|
||||
this.sendForum();
|
||||
}
|
||||
}));
|
||||
break;
|
||||
|
||||
events.push(client.events.on("notify_audio_level_changed", () => {
|
||||
this.updateCachedVolume(client);
|
||||
case "group-channel":
|
||||
this.sendChannelGroup();
|
||||
break;
|
||||
|
||||
case "groups-server":
|
||||
this.sendServerGroups();
|
||||
break;
|
||||
|
||||
case "online-state":
|
||||
this.sendOnline();
|
||||
break;
|
||||
|
||||
case "status":
|
||||
this.sendClientStatus();
|
||||
break;
|
||||
|
||||
case "version":
|
||||
this.sendVolume();
|
||||
}));
|
||||
break;
|
||||
|
||||
events.push(client.events.on("notify_mute_state_change", () => {
|
||||
this.updateCachedVolume(client);
|
||||
case "volume":
|
||||
this.sendVolume();
|
||||
break;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private updateCachedClientStatus(client: ClientEntry) {
|
||||
this.currentClientStatus.status = {
|
||||
away: client.properties.client_away ? client.properties.client_away_message ? client.properties.client_away_message : true : false,
|
||||
microphoneMuted: client.properties.client_input_muted,
|
||||
microphoneDisabled: !client.properties.client_input_hardware,
|
||||
speakerMuted: client.properties.client_output_muted,
|
||||
speakerDisabled: !client.properties.client_output_hardware
|
||||
};
|
||||
}
|
||||
|
||||
private updateCachedCountry(client: ClientEntry) {
|
||||
this.currentClientStatus.country = {
|
||||
flag: client.properties.client_country,
|
||||
name: i18nc.country_name(client.properties.client_country.toUpperCase()),
|
||||
};
|
||||
}
|
||||
|
||||
private updateCachedVolume(client: ClientEntry) {
|
||||
this.currentClientStatus.volume = {
|
||||
volume: client.getAudioVolume(),
|
||||
muted: client.isMuted()
|
||||
}
|
||||
}
|
||||
|
||||
private updateForumAccount(client: ClientEntry) {
|
||||
if(client.properties.client_teaforo_id) {
|
||||
this.currentClientStatus.forumAccount = {
|
||||
flags: client.properties.client_teaforo_flags,
|
||||
nickname: client.properties.client_teaforo_name,
|
||||
userId: client.properties.client_teaforo_id
|
||||
};
|
||||
} else {
|
||||
this.currentClientStatus.forumAccount = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private initializeClientInfo(client: ClientEntry) {
|
||||
this.currentClientStatus = {
|
||||
type: client instanceof LocalClientEntry ? "self" : client.properties.client_type === ClientType.CLIENT_QUERY ? "query" : "voice",
|
||||
name: client.properties.client_nickname,
|
||||
databaseId: client.properties.client_database_id,
|
||||
uniqueId: client.properties.client_unique_identifier,
|
||||
clientId: client.clientId(),
|
||||
|
||||
description: client.properties.client_description,
|
||||
channelGroup: client.properties.client_channel_group_id,
|
||||
serverGroups: client.assignedServerGroupIds(),
|
||||
country: undefined,
|
||||
forumAccount: undefined,
|
||||
joinTimestamp: client.properties.client_lastconnected,
|
||||
leaveTimestamp: 0,
|
||||
status: undefined,
|
||||
volume: undefined,
|
||||
version: {
|
||||
platform: client.properties.client_platform,
|
||||
version: client.properties.client_version
|
||||
}
|
||||
};
|
||||
this.updateCachedClientStatus(client);
|
||||
this.updateCachedCountry(client);
|
||||
this.updateCachedVolume(client);
|
||||
this.updateForumAccount(client);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.listenerClient.forEach(callback => callback());
|
||||
this.listenerClient = [];
|
||||
|
||||
this.listenerConnection.forEach(callback => callback());
|
||||
this.listenerConnection.splice(0, this.listenerConnection.length);
|
||||
}
|
||||
|
||||
setClient(client: ClientEntry | undefined) {
|
||||
if(this.currentClient === client) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.unregisterClientEvents();
|
||||
this.currentClient = client;
|
||||
this.currentClientStatus = undefined;
|
||||
if(this.currentClient) {
|
||||
this.currentClient.updateClientVariables().then(undefined);
|
||||
this.registerClientEvents(this.currentClient);
|
||||
this.initializeClientInfo(this.currentClient);
|
||||
}
|
||||
this.sendClient();
|
||||
this.events.fire("notify_client_changed", { newClient: client });
|
||||
}
|
||||
|
||||
getClient() : ClientEntry | undefined {
|
||||
return this.currentClient;
|
||||
}
|
||||
|
||||
private generateGroupInfo(groupId: number, type: "channel" | "server") : ClientGroupInfo {
|
||||
const uniqueServerId = this.connection.channelTree.server.properties.virtualserver_unique_identifier;
|
||||
const group = type === "channel" ? this.connection.groups.findChannelGroup(groupId) : this.connection.groups.findServerGroup(groupId);
|
||||
const uniqueServerId = this.connection?.channelTree.server.properties.virtualserver_unique_identifier;
|
||||
const group = type === "channel" ? this.connection?.groups.findChannelGroup(groupId) : this.connection?.groups.findServerGroup(groupId);
|
||||
|
||||
if(!group) {
|
||||
return {
|
||||
|
@ -310,14 +152,15 @@ export class ClientInfoController {
|
|||
}
|
||||
|
||||
private sendClient() {
|
||||
if(this.currentClientStatus) {
|
||||
const info = this.connection?.getSelectedClientInfo().getInfo();
|
||||
if(info) {
|
||||
this.uiEvents.fire_react("notify_client", {
|
||||
info: {
|
||||
handlerId: this.connection.handlerId,
|
||||
type: this.currentClientStatus.type,
|
||||
clientDatabaseId: this.currentClientStatus.databaseId,
|
||||
clientId: this.currentClientStatus.clientId,
|
||||
clientUniqueId: this.currentClientStatus.uniqueId
|
||||
type: info.type,
|
||||
clientDatabaseId: info.databaseId,
|
||||
clientId: info.clientId,
|
||||
clientUniqueId: info.uniqueId
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
@ -328,19 +171,21 @@ export class ClientInfoController {
|
|||
}
|
||||
|
||||
private sendChannelGroup() {
|
||||
if(typeof this.currentClientStatus === "undefined") {
|
||||
const info = this.connection?.getSelectedClientInfo().getInfo();
|
||||
if(typeof info === "undefined") {
|
||||
this.uiEvents.fire_react("notify_channel_group", { group: undefined });
|
||||
} else {
|
||||
this.uiEvents.fire_react("notify_channel_group", { group: this.generateGroupInfo(this.currentClientStatus.channelGroup, "channel") });
|
||||
this.uiEvents.fire_react("notify_channel_group", { group: this.generateGroupInfo(info.channelGroup, "channel") });
|
||||
}
|
||||
}
|
||||
|
||||
private sendServerGroups() {
|
||||
if(this.currentClientStatus === undefined) {
|
||||
const info = this.connection?.getSelectedClientInfo().getInfo();
|
||||
if(info === undefined) {
|
||||
this.uiEvents.fire_react("notify_server_groups", { groups: [] });
|
||||
} else {
|
||||
this.uiEvents.fire_react("notify_server_groups", {
|
||||
groups: this.currentClientStatus.serverGroups.map(group => this.generateGroupInfo(group, "server"))
|
||||
groups: info.serverGroups.map(group => this.generateGroupInfo(group, "server"))
|
||||
.sort((a, b) => {
|
||||
if (a.groupSortOrder < b.groupSortOrder)
|
||||
return 1;
|
||||
|
@ -361,8 +206,9 @@ export class ClientInfoController {
|
|||
}
|
||||
|
||||
private sendClientStatus() {
|
||||
const info = this.connection?.getSelectedClientInfo().getInfo();
|
||||
this.uiEvents.fire_react("notify_status", {
|
||||
status: this.currentClientStatus?.status || {
|
||||
status: info?.status || {
|
||||
away: false,
|
||||
speakerDisabled: false,
|
||||
speakerMuted: false,
|
||||
|
@ -373,27 +219,31 @@ export class ClientInfoController {
|
|||
}
|
||||
|
||||
private sendClientName() {
|
||||
this.uiEvents.fire_react("notify_client_name", { name: this.currentClientStatus?.name });
|
||||
const info = this.connection?.getSelectedClientInfo().getInfo();
|
||||
this.uiEvents.fire_react("notify_client_name", { name: info?.name });
|
||||
}
|
||||
|
||||
private sendClientDescription() {
|
||||
this.uiEvents.fire_react("notify_client_description", { description: this.currentClientStatus?.description });
|
||||
const info = this.connection?.getSelectedClientInfo().getInfo();
|
||||
this.uiEvents.fire_react("notify_client_description", { description: info?.description });
|
||||
}
|
||||
|
||||
private sendOnline() {
|
||||
const info = this.connection?.getSelectedClientInfo().getInfo();
|
||||
this.uiEvents.fire_react("notify_online", {
|
||||
status: {
|
||||
leaveTimestamp: this.currentClientStatus ? this.currentClientStatus.leaveTimestamp : 0,
|
||||
joinTimestamp: this.currentClientStatus ? this.currentClientStatus.joinTimestamp : 0
|
||||
leaveTimestamp: info ? info.leaveTimestamp : 0,
|
||||
joinTimestamp: info ? info.joinTimestamp : 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private sendCountry() {
|
||||
const info = this.connection?.getSelectedClientInfo().getInfo();
|
||||
this.uiEvents.fire_react("notify_country", {
|
||||
country: this.currentClientStatus ? {
|
||||
name: this.currentClientStatus.country.name,
|
||||
flag: this.currentClientStatus.country.flag
|
||||
country: info ? {
|
||||
name: info.country.name,
|
||||
flag: info.country.flag
|
||||
} : {
|
||||
name: tr("Unknown"),
|
||||
flag: "xx"
|
||||
|
@ -402,10 +252,11 @@ export class ClientInfoController {
|
|||
}
|
||||
|
||||
private sendVolume() {
|
||||
const info = this.connection?.getSelectedClientInfo().getInfo();
|
||||
this.uiEvents.fire_react("notify_volume", {
|
||||
volume: this.currentClientStatus ? {
|
||||
volume: this.currentClientStatus.volume.volume,
|
||||
muted: this.currentClientStatus.volume.muted
|
||||
volume: info ? {
|
||||
volume: info.volume.volume,
|
||||
muted: info.volume.muted
|
||||
} : {
|
||||
volume: -1,
|
||||
muted: false
|
||||
|
@ -414,10 +265,11 @@ export class ClientInfoController {
|
|||
}
|
||||
|
||||
private sendVersion() {
|
||||
const info = this.connection?.getSelectedClientInfo().getInfo();
|
||||
this.uiEvents.fire_react("notify_version", {
|
||||
version: this.currentClientStatus ? {
|
||||
platform: this.currentClientStatus.version.platform,
|
||||
version: this.currentClientStatus.version.version
|
||||
version: info ? {
|
||||
platform: info.version.platform,
|
||||
version: info.version.version
|
||||
} : {
|
||||
platform: tr("Unknown"),
|
||||
version: tr("Unknown")
|
||||
|
@ -426,6 +278,7 @@ export class ClientInfoController {
|
|||
}
|
||||
|
||||
private sendForum() {
|
||||
this.uiEvents.fire_react("notify_forum", { forum: this.currentClientStatus?.forumAccount })
|
||||
const info = this.connection?.getSelectedClientInfo().getInfo();
|
||||
this.uiEvents.fire_react("notify_forum", { forum: info?.forumAccount })
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ const ChannelInfoUpdateProperties: (keyof ChannelProperties)[] = [
|
|||
];
|
||||
|
||||
/* TODO: Remove the ping interval handler. It's currently still there since the clients are not emitting the event yet */
|
||||
export class SideHeader {
|
||||
export class SideHeaderController {
|
||||
private readonly uiEvents: Registry<SideHeaderEvents>;
|
||||
|
||||
private connection: ConnectionHandler;
|
||||
|
@ -43,26 +43,32 @@ export class SideHeader {
|
|||
|
||||
private initialize() {
|
||||
this.uiEvents.on("action_open_conversation", () => {
|
||||
const selectedClient = this.connection.side_bar.getClientInfo().getClient()
|
||||
const selectedClient = this.connection.getSelectedClientInfo().getClient()
|
||||
if(selectedClient) {
|
||||
const conversations = this.connection.getPrivateConversations();
|
||||
conversations.setSelectedConversation(conversations.findOrCreateConversation(selectedClient));
|
||||
}
|
||||
this.connection.side_bar.showPrivateConversations();
|
||||
this.connection.getSideBar().showPrivateConversations();
|
||||
});
|
||||
|
||||
this.uiEvents.on("action_switch_channel_chat", () => {
|
||||
this.connection.side_bar.showChannelConversations();
|
||||
this.connection.getSideBar().showChannelConversations();
|
||||
});
|
||||
|
||||
this.uiEvents.on("action_bot_manage", () => {
|
||||
const bot = this.connection.side_bar.music_info().current_bot();
|
||||
/* FIXME: TODO! */
|
||||
/*
|
||||
const bot = this.connection.getSideBar().music_info().current_bot();
|
||||
if(!bot) return;
|
||||
|
||||
openMusicManage(this.connection, bot);
|
||||
*/
|
||||
});
|
||||
|
||||
this.uiEvents.on("action_bot_add_song", () => this.connection.side_bar.music_info().events.fire("action_song_add"));
|
||||
this.uiEvents.on("action_bot_add_song", () => {
|
||||
/* FIXME: TODO! */
|
||||
//this.connection.side_bar.music_info().events.fire("action_song_add")
|
||||
});
|
||||
|
||||
this.uiEvents.on("query_client_info_own_client", () => this.sendClientInfoOwnClient());
|
||||
this.uiEvents.on("query_current_channel_state", event => this.sendChannelState(event.mode));
|
||||
|
@ -98,7 +104,7 @@ export class SideHeader {
|
|||
this.listenerConnection.push(this.connection.serverConnection.events.on("notify_ping_updated", () => this.sendPing()));
|
||||
this.listenerConnection.push(this.connection.getPrivateConversations().events.on("notify_unread_count_changed", () => this.sendPrivateConversationInfo()));
|
||||
this.listenerConnection.push(this.connection.getPrivateConversations().events.on(["notify_conversation_destroyed", "notify_conversation_destroyed"], () => this.sendPrivateConversationInfo()));
|
||||
this.listenerConnection.push(this.connection.side_bar.getClientInfo().events.on("notify_client_changed", () => this.sendClientInfoOwnClient()));
|
||||
this.listenerConnection.push(this.connection.getSelectedClientInfo().events.on("notify_client_changed", () => this.sendClientInfoOwnClient()));
|
||||
}
|
||||
|
||||
setConnectionHandler(connection: ConnectionHandler) {
|
||||
|
@ -253,7 +259,7 @@ export class SideHeader {
|
|||
|
||||
private sendClientInfoOwnClient() {
|
||||
if(this.connection) {
|
||||
this.uiEvents.fire_react("notify_client_info_own_client", { isOwnClient: this.connection.side_bar.getClientInfo().getClient() instanceof LocalClientEntry });
|
||||
this.uiEvents.fire_react("notify_client_info_own_client", { isOwnClient: this.connection.getSelectedClientInfo().getClient() instanceof LocalClientEntry });
|
||||
} else {
|
||||
this.uiEvents.fire_react("notify_client_info_own_client", { isOwnClient: false });
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {Registry, RegistryMap} from "tc-shared/events";
|
||||
import {ConversationUIEvents} from "tc-shared/ui/frames/side/ConversationDefinitions";
|
||||
import {AbstractConversationUiEvents} from "./AbstractConversationDefinitions";
|
||||
import {ConversationPanel} from "./AbstractConversationRenderer";
|
||||
import * as React from "react";
|
||||
import {AbstractModal} from "tc-shared/ui/react-elements/ModalDefinitions";
|
||||
|
||||
class PopoutConversationRenderer extends AbstractModal {
|
||||
private readonly events: Registry<ConversationUIEvents>;
|
||||
private readonly events: Registry<AbstractConversationUiEvents>;
|
||||
private readonly userData: any;
|
||||
|
||||
constructor(registryMap: RegistryMap, userData: any) {
|
||||
|
|
|
@ -4,12 +4,9 @@ import {
|
|||
PrivateConversationInfo,
|
||||
PrivateConversationUIEvents
|
||||
} from "../../../ui/frames/side/PrivateConversationDefinitions";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import * as React from "react";
|
||||
import {PrivateConversationsPanel} from "./PrivateConversationRenderer";
|
||||
import {
|
||||
ConversationUIEvents
|
||||
} from "../../../ui/frames/side/ConversationDefinitions";
|
||||
AbstractConversationUiEvents
|
||||
} from "./AbstractConversationDefinitions";
|
||||
import * as log from "../../../log";
|
||||
import {LogCategory} from "../../../log";
|
||||
import {AbstractConversationController} from "./AbstractConversationController";
|
||||
|
@ -48,35 +45,57 @@ export class PrivateConversationController extends AbstractConversationControlle
|
|||
PrivateConversation,
|
||||
PrivateConversationEvents
|
||||
> {
|
||||
public readonly htmlTag: HTMLDivElement;
|
||||
public readonly connection: ConnectionHandler;
|
||||
private connection: ConnectionHandler;
|
||||
private connectionListener: (() => void)[];
|
||||
|
||||
private listenerConversation: {[key: string]:(() => void)[]};
|
||||
|
||||
constructor(connection: ConnectionHandler) {
|
||||
super(connection.getPrivateConversations());
|
||||
this.connection = connection;
|
||||
constructor() {
|
||||
super();
|
||||
this.connectionListener = [];
|
||||
this.listenerConversation = {};
|
||||
|
||||
this.htmlTag = document.createElement("div");
|
||||
this.htmlTag.style.display = "flex";
|
||||
this.htmlTag.style.flexDirection = "row";
|
||||
this.htmlTag.style.justifyContent = "stretch";
|
||||
this.htmlTag.style.height = "100%";
|
||||
|
||||
this.uiEvents.register_handler(this, true);
|
||||
this.uiEvents.enableDebug("private-conversations");
|
||||
}
|
||||
|
||||
ReactDOM.render(React.createElement(PrivateConversationsPanel, { events: this.uiEvents, handler: this.connection }), this.htmlTag);
|
||||
destroy() {
|
||||
/* listenerConversation will be cleaned up via the listenerManager callbacks */
|
||||
|
||||
this.uiEvents.on("notify_destroy", connection.events().on("notify_visibility_changed", event => {
|
||||
this.uiEvents.unregister_handler(this);
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
setConnectionHandler(connection: ConnectionHandler) {
|
||||
if(this.connection === connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.connectionListener.forEach(callback => callback());
|
||||
this.connectionListener = [];
|
||||
|
||||
this.connection = connection;
|
||||
if(connection) {
|
||||
this.initializeConnectionListener(connection);
|
||||
this.setConversationManager(connection.getPrivateConversations());
|
||||
} else {
|
||||
this.setConversationManager(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
private initializeConnectionListener(connection: ConnectionHandler) {
|
||||
this.connectionListener.push(connection.events().on("notify_visibility_changed", event => {
|
||||
if(!event.visible)
|
||||
return;
|
||||
|
||||
this.handlePanelShow();
|
||||
}));
|
||||
}
|
||||
|
||||
this.listenerManager.push(this.conversationManager.events.on("notify_conversation_created", event => {
|
||||
protected registerConversationManagerEvents(manager: PrivateConversationManager) {
|
||||
super.registerConversationManagerEvents(manager);
|
||||
|
||||
this.listenerManager.push(manager.events.on("notify_conversation_created", event => {
|
||||
const conversation = event.conversation;
|
||||
const events = this.listenerConversation[conversation.getChatId()] = [];
|
||||
events.push(conversation.events.on("notify_partner_changed", event => {
|
||||
|
@ -94,21 +113,17 @@ export class PrivateConversationController extends AbstractConversationControlle
|
|||
|
||||
this.reportConversationList();
|
||||
}));
|
||||
this.listenerManager.push(this.conversationManager.events.on("notify_conversation_destroyed", event => {
|
||||
this.listenerManager.push(manager.events.on("notify_conversation_destroyed", event => {
|
||||
this.listenerConversation[event.conversation.getChatId()]?.forEach(callback => callback());
|
||||
delete this.listenerConversation[event.conversation.getChatId()];
|
||||
|
||||
this.reportConversationList();
|
||||
}));
|
||||
this.listenerManager.push(this.conversationManager.events.on("notify_selected_changed", () => this.reportConversationList()));
|
||||
}
|
||||
|
||||
destroy() {
|
||||
ReactDOM.unmountComponentAtNode(this.htmlTag);
|
||||
this.htmlTag.remove();
|
||||
|
||||
this.uiEvents.unregister_handler(this);
|
||||
super.destroy();
|
||||
this.listenerManager.push(manager.events.on("notify_selected_changed", () => this.reportConversationList()));
|
||||
this.listenerManager.push(() => {
|
||||
Object.values(this.listenerConversation).forEach(callbacks => callbacks.forEach(callback => callback()));
|
||||
this.listenerConversation = {};
|
||||
});
|
||||
}
|
||||
|
||||
focusInput() {
|
||||
|
@ -117,8 +132,8 @@ export class PrivateConversationController extends AbstractConversationControlle
|
|||
|
||||
private reportConversationList() {
|
||||
this.uiEvents.fire_react("notify_private_conversations", {
|
||||
conversations: this.conversationManager.getConversations().map(generateConversationUiInfo),
|
||||
selected: this.conversationManager.getSelectedConversation()?.clientUniqueId || "unselected"
|
||||
conversations: this.conversationManager ? this.conversationManager.getConversations().map(generateConversationUiInfo) : [],
|
||||
selected: this.conversationManager?.getSelectedConversation()?.clientUniqueId || "unselected"
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -129,7 +144,7 @@ export class PrivateConversationController extends AbstractConversationControlle
|
|||
|
||||
@EventHandler<PrivateConversationUIEvents>("action_close_chat")
|
||||
private handleConversationClose(event: PrivateConversationUIEvents["action_close_chat"]) {
|
||||
const conversation = this.conversationManager.findConversation(event.chatId);
|
||||
const conversation = this.conversationManager?.findConversation(event.chatId);
|
||||
if(!conversation) {
|
||||
log.error(LogCategory.CLIENT, tr("Tried to close a not existing private conversation with id %s"), event.chatId);
|
||||
return;
|
||||
|
@ -138,13 +153,8 @@ export class PrivateConversationController extends AbstractConversationControlle
|
|||
this.conversationManager.closeConversation(conversation);
|
||||
}
|
||||
|
||||
@EventHandler<PrivateConversationUIEvents>("notify_partner_typing")
|
||||
private handleNotifySelectChat(event: PrivateConversationUIEvents["notify_partner_typing"]) {
|
||||
/* TODO, set active chat? MH 9/12/20: What?? */
|
||||
}
|
||||
|
||||
@EventHandler<ConversationUIEvents>("action_self_typing")
|
||||
protected handleActionSelfTyping1(_event: ConversationUIEvents["action_self_typing"]) {
|
||||
@EventHandler<AbstractConversationUiEvents>("action_self_typing")
|
||||
protected handleActionSelfTyping1(_event: AbstractConversationUiEvents["action_self_typing"]) {
|
||||
const conversation = this.getCurrentConversation();
|
||||
if(!conversation) {
|
||||
return;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {ConversationUIEvents} from "../../../ui/frames/side/ConversationDefinitions";
|
||||
import {AbstractConversationUiEvents} from "./AbstractConversationDefinitions";
|
||||
|
||||
export type PrivateConversationInfo = {
|
||||
nickname: string;
|
||||
|
@ -11,7 +11,7 @@ export type PrivateConversationInfo = {
|
|||
unreadMessages: boolean;
|
||||
};
|
||||
|
||||
export interface PrivateConversationUIEvents extends ConversationUIEvents {
|
||||
export interface PrivateConversationUIEvents extends AbstractConversationUiEvents {
|
||||
action_close_chat: { chatId: string },
|
||||
|
||||
query_private_conversations: {},
|
||||
|
|
|
@ -17,6 +17,13 @@ html:root {
|
|||
--chat-private-selected-background: #2c2c2c;
|
||||
}
|
||||
|
||||
.dividerContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: stretch;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 2px!important;
|
||||
min-width: 2px!important;
|
||||
|
|
|
@ -218,10 +218,11 @@ const OpenConversationsPanel = React.memo(() => {
|
|||
export const PrivateConversationsPanel = (props: { events: Registry<PrivateConversationUIEvents>, handlerId: string }) => (
|
||||
<HandlerIdContext.Provider value={props.handlerId}>
|
||||
<EventContext.Provider value={props.events}>
|
||||
<ContextDivider id={"seperator-conversation-list-messages"} direction={"horizontal"} defaultValue={25} separatorClassName={cssStyle.divider}>
|
||||
<div className={cssStyle.dividerContainer}>
|
||||
<OpenConversationsPanel />
|
||||
<ContextDivider id={"seperator-conversation-list-messages"} direction={"horizontal"} defaultValue={25} separatorClassName={cssStyle.divider} />
|
||||
<ConversationPanel events={props.events as any} handlerId={props.handlerId} noFirstMessageOverlay={true} messagesDeletable={false} />
|
||||
</ContextDivider>
|
||||
</div>
|
||||
</EventContext.Provider>
|
||||
</HandlerIdContext.Provider>
|
||||
);
|
|
@ -1,4 +1,4 @@
|
|||
import {Frame, FrameContent} from "../../../ui/frames/chat_frame";
|
||||
import {SideBarController, FrameContent} from "../SideBarController";
|
||||
import {LogCategory} from "../../../log";
|
||||
import {CommandResult, PlaylistSong} from "../../../connection/ServerConnectionDeclaration";
|
||||
import {createErrorModal, createInputModal} from "../../../ui/elements/Modal";
|
||||
|
@ -67,7 +67,7 @@ interface LoadedSongData {
|
|||
|
||||
export class MusicInfo {
|
||||
readonly events: Registry<MusicSidebarEvents>;
|
||||
readonly handle: Frame;
|
||||
readonly handle: SideBarController;
|
||||
|
||||
private _html_tag: JQuery;
|
||||
private _container_playlist: JQuery;
|
||||
|
@ -91,7 +91,7 @@ export class MusicInfo {
|
|||
|
||||
previous_frame_content: FrameContent;
|
||||
|
||||
constructor(handle: Frame) {
|
||||
constructor(handle: SideBarController) {
|
||||
this.events = new Registry<MusicSidebarEvents>();
|
||||
this.handle = handle;
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ export interface ContextDividerProperties {
|
|||
separatorClassName?: string;
|
||||
separatorActiveClassName?: string;
|
||||
|
||||
children: [React.ReactElement, React.ReactElement];
|
||||
children?: never;
|
||||
}
|
||||
|
||||
export interface ContextDividerState {
|
||||
|
@ -99,11 +99,9 @@ export class ContextDivider extends React.Component<ContextDividerProperties, Co
|
|||
if(this.state.active && this.props.separatorClassName)
|
||||
separatorClassNames += " " + this.props.separatorClassName;
|
||||
|
||||
return [
|
||||
this.props.children[0],
|
||||
<div key={"context-separator"} ref={this.refSeparator} className={separatorClassNames} onMouseDown={e => this.startMovement(e)} onTouchStart={e => this.startMovement(e)} />,
|
||||
this.props.children[1]
|
||||
];
|
||||
return (
|
||||
<div key={"context-separator"} ref={this.refSeparator} className={separatorClassNames} onMouseDown={e => this.startMovement(e)} onTouchStart={e => this.startMovement(e)} />
|
||||
)
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
|
|
Loading…
Reference in New Issue