Improved channel private conversation mode behaviour
parent
a05b795859
commit
e6bbb883e4
|
@ -2,6 +2,7 @@
|
||||||
* **09.12.20**
|
* **09.12.20**
|
||||||
- Fixed the private messages unread indicator
|
- Fixed the private messages unread indicator
|
||||||
- Properly updating the private message unread count
|
- Properly updating the private message unread count
|
||||||
|
- Improved channel conversation mode detection support
|
||||||
|
|
||||||
* **08.12.20**
|
* **08.12.20**
|
||||||
- Fixed the permission editor not resolving unique ids
|
- Fixed the permission editor not resolving unique ids
|
||||||
|
|
|
@ -2,15 +2,17 @@ import {
|
||||||
ChatEvent,
|
ChatEvent,
|
||||||
ChatEventMessage,
|
ChatEventMessage,
|
||||||
ChatMessage,
|
ChatMessage,
|
||||||
ChatState, ConversationHistoryResponse
|
ChatState,
|
||||||
|
ConversationHistoryResponse
|
||||||
} from "tc-shared/ui/frames/side/ConversationDefinitions";
|
} from "tc-shared/ui/frames/side/ConversationDefinitions";
|
||||||
import {Registry} from "tc-shared/events";
|
import {Registry} from "tc-shared/events";
|
||||||
import {ConnectionHandler, ConnectionState} from "tc-shared/ConnectionHandler";
|
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||||
import {preprocessChatMessageForSend} from "tc-shared/text/chat";
|
import {preprocessChatMessageForSend} from "tc-shared/text/chat";
|
||||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||||
import {ErrorCode} from "tc-shared/connection/ErrorCode";
|
import {ErrorCode} from "tc-shared/connection/ErrorCode";
|
||||||
import {LogCategory, logWarn} from "tc-shared/log";
|
import {LogCategory, logWarn} from "tc-shared/log";
|
||||||
import {ChannelConversation} from "tc-shared/conversations/ChannelConversationManager";
|
import {ChannelConversationMode} from "tc-shared/tree/Channel";
|
||||||
|
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 const kMaxChatFrameMessageSize = 50; /* max 100 messages, since the server does not support more than 100 messages queried at once */
|
||||||
|
|
||||||
|
@ -34,6 +36,12 @@ export interface AbstractChatEvents {
|
||||||
},
|
},
|
||||||
notify_history_state_changed: {
|
notify_history_state_changed: {
|
||||||
hasHistory: boolean
|
hasHistory: boolean
|
||||||
|
},
|
||||||
|
notify_conversation_mode_changed: {
|
||||||
|
newMode: ChannelConversationMode
|
||||||
|
},
|
||||||
|
notify_read_state_changed: {
|
||||||
|
readable: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,13 +58,14 @@ export abstract class AbstractChat<Events extends AbstractChatEvents> {
|
||||||
protected failedPermission: string;
|
protected failedPermission: string;
|
||||||
protected errorMessage: string;
|
protected errorMessage: string;
|
||||||
|
|
||||||
protected conversationPrivate: boolean = false;
|
private conversationMode: ChannelConversationMode;
|
||||||
protected crossChannelChatSupported: boolean = true;
|
protected crossChannelChatSupported: boolean = true;
|
||||||
|
|
||||||
protected unreadTimestamp: number;
|
protected unreadTimestamp: number;
|
||||||
protected unreadState: boolean = false;
|
protected unreadState: boolean = false;
|
||||||
|
|
||||||
protected messageSendEnabled: boolean = true;
|
protected messageSendEnabled: boolean = true;
|
||||||
|
private conversationReadable = true;
|
||||||
|
|
||||||
private history = false;
|
private history = false;
|
||||||
|
|
||||||
|
@ -65,6 +74,7 @@ export abstract class AbstractChat<Events extends AbstractChatEvents> {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.chatId = chatId;
|
this.chatId = chatId;
|
||||||
this.unreadTimestamp = Date.now();
|
this.unreadTimestamp = Date.now();
|
||||||
|
this.conversationMode = ChannelConversationMode.Public;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
|
@ -103,7 +113,7 @@ export abstract class AbstractChat<Events extends AbstractChatEvents> {
|
||||||
this.setUnreadTimestamp(Date.now());
|
this.setUnreadTimestamp(Date.now());
|
||||||
} else if(!this.isUnread() && triggerUnread) {
|
} else if(!this.isUnread() && triggerUnread) {
|
||||||
this.setUnreadTimestamp(event.message.timestamp - 1);
|
this.setUnreadTimestamp(event.message.timestamp - 1);
|
||||||
} else {
|
} else if(!this.isUnread()) {
|
||||||
/* mark the last message as read */
|
/* mark the last message as read */
|
||||||
this.setUnreadTimestamp(event.message.timestamp);
|
this.setUnreadTimestamp(event.message.timestamp);
|
||||||
}
|
}
|
||||||
|
@ -119,7 +129,7 @@ export abstract class AbstractChat<Events extends AbstractChatEvents> {
|
||||||
|
|
||||||
if(!this.isUnread() && triggerUnread) {
|
if(!this.isUnread() && triggerUnread) {
|
||||||
this.setUnreadTimestamp(event.timestamp - 1);
|
this.setUnreadTimestamp(event.timestamp - 1);
|
||||||
} else {
|
} else if(!this.isUnread()) {
|
||||||
/* mark the last message as read */
|
/* mark the last message as read */
|
||||||
this.setUnreadTimestamp(event.timestamp);
|
this.setUnreadTimestamp(event.timestamp);
|
||||||
}
|
}
|
||||||
|
@ -197,8 +207,41 @@ export abstract class AbstractChat<Events extends AbstractChatEvents> {
|
||||||
return this.unreadState;
|
return this.unreadState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getConversationMode() : ChannelConversationMode {
|
||||||
|
return this.conversationMode;
|
||||||
|
}
|
||||||
|
|
||||||
public isPrivate() : boolean {
|
public isPrivate() : boolean {
|
||||||
return this.conversationPrivate;
|
return this.conversationMode === ChannelConversationMode.Private;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setConversationMode(mode: ChannelConversationMode) {
|
||||||
|
if(this.conversationMode === mode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.registerChatEvent({
|
||||||
|
type: "mode-changed",
|
||||||
|
uniqueId: guid() + "-mode-change",
|
||||||
|
timestamp: Date.now(),
|
||||||
|
newMode: mode === ChannelConversationMode.Public ? "normal" : mode === ChannelConversationMode.Private ? "private" : "none"
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
this.conversationMode = mode;
|
||||||
|
this.events.fire("notify_conversation_mode_changed", { newMode: mode });
|
||||||
|
}
|
||||||
|
|
||||||
|
public isReadable() {
|
||||||
|
return this.conversationReadable;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setReadable(flag: boolean) {
|
||||||
|
if(this.conversationReadable === flag) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.conversationReadable = flag;
|
||||||
|
this.events.fire("notify_read_state_changed", { readable: flag });
|
||||||
}
|
}
|
||||||
|
|
||||||
public isSendEnabled() : boolean {
|
public isSendEnabled() : boolean {
|
||||||
|
@ -275,7 +318,6 @@ export abstract class AbstractChat<Events extends AbstractChatEvents> {
|
||||||
this.events.fire("notify_send_toggle", { enabled: enabled });
|
this.events.fire("notify_send_toggle", { enabled: enabled });
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract canClientAccessChat() : boolean;
|
|
||||||
public abstract queryHistory(criteria: { begin?: number, end?: number, limit?: number }) : Promise<ConversationHistoryResponse>;
|
public abstract queryHistory(criteria: { begin?: number, end?: number, limit?: number }) : Promise<ConversationHistoryResponse>;
|
||||||
public abstract queryCurrentMessages();
|
public abstract queryCurrentMessages();
|
||||||
public abstract sendMessage(text: string);
|
public abstract sendMessage(text: string);
|
||||||
|
|
|
@ -15,6 +15,7 @@ import {traj} from "tc-shared/i18n/localize";
|
||||||
import {ConnectionHandler, ConnectionState} from "tc-shared/ConnectionHandler";
|
import {ConnectionHandler, ConnectionState} from "tc-shared/ConnectionHandler";
|
||||||
import {LocalClientEntry} from "tc-shared/tree/Client";
|
import {LocalClientEntry} from "tc-shared/tree/Client";
|
||||||
import {ServerCommand} from "tc-shared/connection/ConnectionBase";
|
import {ServerCommand} from "tc-shared/connection/ConnectionBase";
|
||||||
|
import {ChannelConversationMode} from "tc-shared/tree/Channel";
|
||||||
|
|
||||||
export interface ChannelConversationEvents extends AbstractChatEvents {
|
export interface ChannelConversationEvents extends AbstractChatEvents {
|
||||||
notify_messages_deleted: { messages: string[] },
|
notify_messages_deleted: { messages: string[] },
|
||||||
|
@ -40,13 +41,12 @@ export class ChannelConversation extends AbstractChat<ChannelConversationEvents>
|
||||||
this.conversationId = id;
|
this.conversationId = id;
|
||||||
|
|
||||||
this.preventUnreadUpdate = true;
|
this.preventUnreadUpdate = true;
|
||||||
const dateNow = Date.now();
|
|
||||||
const unreadTimestamp = handle.connection.settings.server(Settings.FN_CHANNEL_CHAT_READ(id), Date.now());
|
const unreadTimestamp = handle.connection.settings.server(Settings.FN_CHANNEL_CHAT_READ(id), Date.now());
|
||||||
this.setUnreadTimestamp(unreadTimestamp);
|
this.setUnreadTimestamp(unreadTimestamp);
|
||||||
this.preventUnreadUpdate = false;
|
this.preventUnreadUpdate = false;
|
||||||
|
|
||||||
this.events.on("notify_unread_state_changed", event => {
|
this.events.on(["notify_unread_state_changed", "notify_read_state_changed"], event => {
|
||||||
this.handle.connection.channelTree.findChannel(this.conversationId)?.setUnread(event.unread);
|
this.handle.connection.channelTree.findChannel(this.conversationId)?.setUnread(this.isReadable() && this.isUnread());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +142,6 @@ export class ChannelConversation extends AbstractChat<ChannelConversationEvents>
|
||||||
this.setCurrentMode("loading");
|
this.setCurrentMode("loading");
|
||||||
|
|
||||||
this.queryHistory({ end: 1, limit: kMaxChatFrameMessageSize }).then(history => {
|
this.queryHistory({ end: 1, limit: kMaxChatFrameMessageSize }).then(history => {
|
||||||
this.conversationPrivate = false;
|
|
||||||
this.conversationVolatile = false;
|
this.conversationVolatile = false;
|
||||||
this.failedPermission = undefined;
|
this.failedPermission = undefined;
|
||||||
this.errorMessage = undefined;
|
this.errorMessage = undefined;
|
||||||
|
@ -166,17 +165,18 @@ export class ChannelConversation extends AbstractChat<ChannelConversationEvents>
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "private":
|
case "private":
|
||||||
this.conversationPrivate = true;
|
this.setConversationMode(ChannelConversationMode.Private);
|
||||||
this.setCurrentMode("normal");
|
this.setCurrentMode("normal");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "success":
|
case "success":
|
||||||
|
this.setConversationMode(ChannelConversationMode.Public);
|
||||||
this.setCurrentMode("normal");
|
this.setCurrentMode("normal");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "unsupported":
|
case "unsupported":
|
||||||
this.crossChannelChatSupported = false;
|
this.crossChannelChatSupported = false;
|
||||||
this.conversationPrivate = true;
|
this.setConversationMode(ChannelConversationMode.Private);
|
||||||
this.setCurrentMode("normal");
|
this.setCurrentMode("normal");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -295,6 +295,10 @@ export class ChannelConversation extends AbstractChat<ChannelConversationEvents>
|
||||||
this.handle.connection.settings.changeServer(Settings.FN_CHANNEL_CHAT_READ(this.conversationId), timestamp);
|
this.handle.connection.settings.changeServer(Settings.FN_CHANNEL_CHAT_READ(this.conversationId), timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setConversationMode(mode: ChannelConversationMode) {
|
||||||
|
super.setConversationMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
public localClientSwitchedChannel(type: "join" | "leave") {
|
public localClientSwitchedChannel(type: "join" | "leave") {
|
||||||
this.registerChatEvent({
|
this.registerChatEvent({
|
||||||
type: "local-user-switch",
|
type: "local-user-switch",
|
||||||
|
@ -309,6 +313,14 @@ export class ChannelConversation extends AbstractChat<ChannelConversationEvents>
|
||||||
sendMessage(text: string) {
|
sendMessage(text: string) {
|
||||||
this.doSendMessage(text, this.conversationId ? 2 : 3, this.conversationId).then(() => {});
|
this.doSendMessage(text, this.conversationId ? 2 : 3, this.conversationId).then(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateAccessState() {
|
||||||
|
if(this.isPrivate()) {
|
||||||
|
this.setReadable(this.connection.getClient().currentChannel()?.getChannelId() === this.conversationId);
|
||||||
|
} else {
|
||||||
|
this.setReadable(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChannelConversationManagerEvents extends AbstractChatManagerEvents<ChannelConversation> { }
|
export interface ChannelConversationManagerEvents extends AbstractChatManagerEvents<ChannelConversation> { }
|
||||||
|
@ -337,6 +349,37 @@ export class ChannelConversationManager extends AbstractChatManager<ChannelConve
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this.listenerConnection.push(connection.channelTree.events.on("notify_channel_updated", event => {
|
||||||
|
const conversation = this.findConversation(event.channel.channelId);
|
||||||
|
if(!conversation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if("channel_conversation_mode" in event.updatedProperties) {
|
||||||
|
conversation.setConversationMode(event.channel.properties.channel_conversation_mode);
|
||||||
|
conversation.updateAccessState();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.listenerConnection.push(connection.channelTree.events.on("notify_client_moved", event => {
|
||||||
|
if(event.client instanceof LocalClientEntry) {
|
||||||
|
const fromConversation = this.findConversation(event.oldChannel.channelId);
|
||||||
|
const targetConversation = this.findConversation(event.newChannel.channelId);
|
||||||
|
|
||||||
|
fromConversation?.updateAccessState();
|
||||||
|
targetConversation?.updateAccessState();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.listenerConnection.push(connection.channelTree.events.on("notify_client_enter_view", event => {
|
||||||
|
if(event.client instanceof LocalClientEntry) {
|
||||||
|
const targetConversation = this.findConversation(event.targetChannel.channelId);
|
||||||
|
targetConversation?.updateAccessState();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
/* TODO: Permission listener for text send power! */
|
||||||
|
|
||||||
this.listenerConnection.push(connection.serverConnection.command_handler_boss().register_explicit_handler("notifyconversationhistory", this.handleConversationHistory.bind(this)));
|
this.listenerConnection.push(connection.serverConnection.command_handler_boss().register_explicit_handler("notifyconversationhistory", this.handleConversationHistory.bind(this)));
|
||||||
this.listenerConnection.push(connection.serverConnection.command_handler_boss().register_explicit_handler("notifyconversationindex", this.handleConversationIndex.bind(this)));
|
this.listenerConnection.push(connection.serverConnection.command_handler_boss().register_explicit_handler("notifyconversationindex", this.handleConversationIndex.bind(this)));
|
||||||
this.listenerConnection.push(connection.serverConnection.command_handler_boss().register_explicit_handler("notifyconversationmessagedelete", this.handleConversationMessageDelete.bind(this)));
|
this.listenerConnection.push(connection.serverConnection.command_handler_boss().register_explicit_handler("notifyconversationmessagedelete", this.handleConversationMessageDelete.bind(this)));
|
||||||
|
|
|
@ -42,6 +42,12 @@ export enum ChannelSubscribeMode {
|
||||||
INHERITED
|
INHERITED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ChannelConversationMode {
|
||||||
|
Public = 0,
|
||||||
|
Private = 1,
|
||||||
|
None = 2
|
||||||
|
}
|
||||||
|
|
||||||
export class ChannelProperties {
|
export class ChannelProperties {
|
||||||
channel_order: number = 0;
|
channel_order: number = 0;
|
||||||
channel_name: string = "";
|
channel_name: string = "";
|
||||||
|
@ -73,7 +79,7 @@ export class ChannelProperties {
|
||||||
//Only after request
|
//Only after request
|
||||||
channel_description: string = "";
|
channel_description: string = "";
|
||||||
|
|
||||||
channel_conversation_mode: number = 0; /* 0 := Private, the default */
|
channel_conversation_mode: ChannelConversationMode = 0;
|
||||||
channel_conversation_history_length: number = -1;
|
channel_conversation_history_length: number = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,6 +570,8 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
|
||||||
}
|
}
|
||||||
/* devel-block-end */
|
/* devel-block-end */
|
||||||
|
|
||||||
|
/* TODO: Validate values. Example: channel_conversation_mode */
|
||||||
|
|
||||||
for(let variable of variables) {
|
for(let variable of variables) {
|
||||||
let key = variable.key;
|
let key = variable.key;
|
||||||
let value = variable.value;
|
let value = variable.value;
|
||||||
|
|
|
@ -35,6 +35,8 @@ export abstract class AbstractConversationController<
|
||||||
protected currentSelectedConversation: ConversationType;
|
protected currentSelectedConversation: ConversationType;
|
||||||
protected currentSelectedListener: (() => void)[];
|
protected currentSelectedListener: (() => void)[];
|
||||||
|
|
||||||
|
protected crossChannelChatSupported = true;
|
||||||
|
|
||||||
protected constructor(conversationManager: Manager) {
|
protected constructor(conversationManager: Manager) {
|
||||||
this.uiEvents = new Registry<Events>();
|
this.uiEvents = new Registry<Events>();
|
||||||
this.currentSelectedListener = [];
|
this.currentSelectedListener = [];
|
||||||
|
@ -86,6 +88,10 @@ export abstract class AbstractConversationController<
|
||||||
this.currentSelectedListener.push(conversation.events.on("notify_history_state_changed", () => {
|
this.currentSelectedListener.push(conversation.events.on("notify_history_state_changed", () => {
|
||||||
this.reportStateToUI(conversation);
|
this.reportStateToUI(conversation);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this.currentSelectedListener.push(conversation.events.on("notify_read_state_changed", () => {
|
||||||
|
this.reportStateToUI(conversation);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePanelShow() {
|
handlePanelShow() {
|
||||||
|
@ -93,8 +99,6 @@ export abstract class AbstractConversationController<
|
||||||
}
|
}
|
||||||
|
|
||||||
protected reportStateToUI(conversation: AbstractChat<any>) {
|
protected reportStateToUI(conversation: AbstractChat<any>) {
|
||||||
const crossChannelChatSupported = true; /* FIXME: Determine this form the server! */
|
|
||||||
|
|
||||||
let historyState: ChatHistoryState;
|
let historyState: ChatHistoryState;
|
||||||
const localHistoryState = this.historyUiStates[conversation.getChatId()];
|
const localHistoryState = this.historyUiStates[conversation.getChatId()];
|
||||||
if(!localHistoryState) {
|
if(!localHistoryState) {
|
||||||
|
@ -113,11 +117,11 @@ export abstract class AbstractConversationController<
|
||||||
|
|
||||||
switch (conversation.getCurrentMode()) {
|
switch (conversation.getCurrentMode()) {
|
||||||
case "normal":
|
case "normal":
|
||||||
if(conversation.isPrivate() && !conversation.canClientAccessChat()) {
|
if(conversation.isPrivate() && !conversation.isReadable()) {
|
||||||
this.uiEvents.fire_react("notify_conversation_state", {
|
this.uiEvents.fire_react("notify_conversation_state", {
|
||||||
chatId: conversation.getChatId(),
|
chatId: conversation.getChatId(),
|
||||||
state: "private",
|
state: "private",
|
||||||
crossChannelChatSupported: crossChannelChatSupported
|
crossChannelChatSupported: this.crossChannelChatSupported
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -133,7 +137,7 @@ export abstract class AbstractConversationController<
|
||||||
chatFrameMaxMessageCount: kMaxChatFrameMessageSize,
|
chatFrameMaxMessageCount: kMaxChatFrameMessageSize,
|
||||||
unreadTimestamp: conversation.getUnreadTimestamp(),
|
unreadTimestamp: conversation.getUnreadTimestamp(),
|
||||||
|
|
||||||
showUserSwitchEvents: conversation.isPrivate() || !crossChannelChatSupported,
|
showUserSwitchEvents: conversation.isPrivate() || !this.crossChannelChatSupported,
|
||||||
sendEnabled: conversation.isSendEnabled(),
|
sendEnabled: conversation.isSendEnabled(),
|
||||||
|
|
||||||
events: [...conversation.getPresentEvents(), ...conversation.getPresentMessages()]
|
events: [...conversation.getPresentEvents(), ...conversation.getPresentMessages()]
|
||||||
|
@ -230,6 +234,18 @@ export abstract class AbstractConversationController<
|
||||||
return this.currentSelectedConversation;
|
return this.currentSelectedConversation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected setCrossChannelChatSupport(flag: boolean) {
|
||||||
|
if(this.crossChannelChatSupported === flag) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.crossChannelChatSupported = flag;
|
||||||
|
const currentConversation = this.getCurrentConversation();
|
||||||
|
if(currentConversation) {
|
||||||
|
this.reportStateToUI(this.getCurrentConversation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler<ConversationUIEvents>("query_conversation_state")
|
@EventHandler<ConversationUIEvents>("query_conversation_state")
|
||||||
protected handleQueryConversationState(event: ConversationUIEvents["query_conversation_state"]) {
|
protected handleQueryConversationState(event: ConversationUIEvents["query_conversation_state"]) {
|
||||||
const conversation = this.conversationManager.findConversationById(event.chatId);
|
const conversation = this.conversationManager.findConversationById(event.chatId);
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
ChatEventPartnerAction,
|
ChatEventPartnerAction,
|
||||||
ChatHistoryState,
|
ChatHistoryState,
|
||||||
ChatMessage,
|
ChatMessage,
|
||||||
ConversationUIEvents
|
ConversationUIEvents, ChatEventModeChanged
|
||||||
} from "tc-shared/ui/frames/side/ConversationDefinitions";
|
} from "tc-shared/ui/frames/side/ConversationDefinitions";
|
||||||
import {TimestampRenderer} from "tc-shared/ui/react-elements/TimestampRenderer";
|
import {TimestampRenderer} from "tc-shared/ui/react-elements/TimestampRenderer";
|
||||||
import {BBCodeRenderer} from "tc-shared/text/bbcode";
|
import {BBCodeRenderer} from "tc-shared/text/bbcode";
|
||||||
|
@ -277,6 +277,34 @@ const ChatEventPartnerActionRenderer = (props: { event: ChatEventPartnerAction,
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ChatEventModeChangedRenderer = (props: { event: ChatEventModeChanged, refHTMLElement: Ref<HTMLDivElement> }) => {
|
||||||
|
switch (props.event.newMode) {
|
||||||
|
case "none":
|
||||||
|
return (
|
||||||
|
<div className={cssStyle.containerSwitch + " " + cssStyle.actionClose} ref={props.refHTMLElement}>
|
||||||
|
<a><Translatable>The conversation has been disabled</Translatable></a>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "private":
|
||||||
|
return (
|
||||||
|
<div className={cssStyle.containerSwitch + " " + cssStyle.actionClose} ref={props.refHTMLElement}>
|
||||||
|
<a><Translatable>The conversation has been made private</Translatable></a>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "normal":
|
||||||
|
return (
|
||||||
|
<div className={cssStyle.containerSwitch + " " + cssStyle.actionClose} ref={props.refHTMLElement}>
|
||||||
|
<a><Translatable>The conversation has been made public</Translatable></a>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const PartnerTypingIndicator = (props: { events: Registry<ConversationUIEvents>, chatId: string, timeout?: number }) => {
|
const PartnerTypingIndicator = (props: { events: Registry<ConversationUIEvents>, chatId: string, timeout?: number }) => {
|
||||||
const kTypingTimeout = props.timeout || 5000;
|
const kTypingTimeout = props.timeout || 5000;
|
||||||
|
|
||||||
|
@ -658,6 +686,14 @@ class ConversationMessages extends React.PureComponent<ConversationMessagesPrope
|
||||||
refHTMLElement={reference}
|
refHTMLElement={reference}
|
||||||
/>);
|
/>);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "mode-changed":
|
||||||
|
this.viewEntries.push(<ChatEventModeChangedRenderer
|
||||||
|
key={event.uniqueId}
|
||||||
|
event={event}
|
||||||
|
refHTMLElement={reference}
|
||||||
|
/>);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {ConnectionHandler} from "../../../ConnectionHandler";
|
import {ConnectionHandler, ConnectionState} from "../../../ConnectionHandler";
|
||||||
import {EventHandler} from "../../../events";
|
import {EventHandler} from "../../../events";
|
||||||
import * as log from "../../../log";
|
import * as log from "../../../log";
|
||||||
import {LogCategory} from "../../../log";
|
import {LogCategory} from "../../../log";
|
||||||
import {tr} from "../../../i18n/localize";
|
import {tr} from "../../../i18n/localize";
|
||||||
import ReactDOM = require("react-dom");
|
import {ConversationUIEvents} from "../../../ui/frames/side/ConversationDefinitions";
|
||||||
import {
|
|
||||||
ConversationUIEvents
|
|
||||||
} from "../../../ui/frames/side/ConversationDefinitions";
|
|
||||||
import {ConversationPanel} from "./AbstractConversationRenderer";
|
import {ConversationPanel} from "./AbstractConversationRenderer";
|
||||||
import {AbstractConversationController} from "./AbstractConversationController";
|
import {AbstractConversationController} from "./AbstractConversationController";
|
||||||
import {
|
import {
|
||||||
ChannelConversation, ChannelConversationEvents,
|
ChannelConversation,
|
||||||
|
ChannelConversationEvents,
|
||||||
ChannelConversationManager,
|
ChannelConversationManager,
|
||||||
ChannelConversationManagerEvents
|
ChannelConversationManagerEvents
|
||||||
} from "tc-shared/conversations/ChannelConversationManager";
|
} from "tc-shared/conversations/ChannelConversationManager";
|
||||||
|
import {ServerFeature} from "tc-shared/connection/ServerFeatures";
|
||||||
|
import ReactDOM = require("react-dom");
|
||||||
|
|
||||||
export class ChannelConversationController extends AbstractConversationController<
|
export class ChannelConversationController extends AbstractConversationController<
|
||||||
ConversationUIEvents,
|
ConversationUIEvents,
|
||||||
|
@ -61,6 +61,18 @@ export class ChannelConversationController extends AbstractConversationControlle
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.uiEvents.register_handler(this, true);
|
this.uiEvents.register_handler(this, true);
|
||||||
|
|
||||||
|
this.listenerManager.push(connection.events().on("notify_connection_state_changed", event => {
|
||||||
|
if(event.newState === ConnectionState.CONNECTED) {
|
||||||
|
connection.serverFeatures.awaitFeatures().then(success => {
|
||||||
|
if(!success) { return; }
|
||||||
|
|
||||||
|
this.setCrossChannelChatSupport(connection.serverFeatures.supportsFeature(ServerFeature.ADVANCED_CHANNEL_CHAT));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setCrossChannelChatSupport(true);
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
|
@ -84,8 +96,13 @@ export class ChannelConversationController extends AbstractConversationControlle
|
||||||
|
|
||||||
protected registerConversationEvents(conversation: ChannelConversation) {
|
protected registerConversationEvents(conversation: ChannelConversation) {
|
||||||
super.registerConversationEvents(conversation);
|
super.registerConversationEvents(conversation);
|
||||||
|
|
||||||
this.currentSelectedListener.push(conversation.events.on("notify_messages_deleted", event => {
|
this.currentSelectedListener.push(conversation.events.on("notify_messages_deleted", event => {
|
||||||
this.uiEvents.fire_react("notify_chat_message_delete", { messageIds: event.messages, chatId: conversation.getChatId() });
|
this.uiEvents.fire_react("notify_chat_message_delete", { messageIds: event.messages, chatId: conversation.getChatId() });
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this.currentSelectedListener.push(conversation.events.on("notify_conversation_mode_changed", () => {
|
||||||
|
this.reportStateToUI(conversation);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,7 +16,8 @@ export type ChatEvent = { timestamp: number; uniqueId: string; } & (
|
||||||
ChatEventQueryFailed |
|
ChatEventQueryFailed |
|
||||||
ChatEventPartnerInstanceChanged |
|
ChatEventPartnerInstanceChanged |
|
||||||
ChatEventLocalAction |
|
ChatEventLocalAction |
|
||||||
ChatEventPartnerAction
|
ChatEventPartnerAction |
|
||||||
|
ChatEventModeChanged
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface ChatEventUnreadTrigger {
|
export interface ChatEventUnreadTrigger {
|
||||||
|
@ -64,6 +65,11 @@ export interface ChatEventPartnerAction {
|
||||||
action: "disconnect" | "close" | "reconnect";
|
action: "disconnect" | "close" | "reconnect";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ChatEventModeChanged {
|
||||||
|
type: "mode-changed";
|
||||||
|
newMode: "normal" | "private" | "none"
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- Chat States ---------- */
|
/* ---------- Chat States ---------- */
|
||||||
export type ChatState = "normal" | "loading" | "no-permissions" | "error" | "unloaded";
|
export type ChatState = "normal" | "loading" | "no-permissions" | "error" | "unloaded";
|
||||||
export type ChatHistoryState = "none" | "loading" | "available" | "error";
|
export type ChatHistoryState = "none" | "loading" | "available" | "error";
|
||||||
|
|
|
@ -105,9 +105,9 @@ const BlockPing = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pingInfo.javaScript === undefined) {
|
if(pingInfo.javaScript === undefined) {
|
||||||
title = tr("Ping: " + pingInfo.native.toFixed(3) + "ms");
|
title = tra("Ping: {}ms", pingInfo.native.toFixed(3));
|
||||||
} else {
|
} else {
|
||||||
title = tr("Native: " + pingInfo.native.toFixed(3) + "ms\nJavascript: " + pingInfo.javaScript.toFixed(3) + "ms");
|
title = tra("Native: {}ms\nJavascript: {}ms", pingInfo.native.toFixed(3), pingInfo.javaScript.toFixed(3));
|
||||||
}
|
}
|
||||||
value = <div className={cssStyle.value + " " + cssStyle.ping + " " + pingClass} key={"ping"} title={title}>{pingInfo.native.toFixed(0)}ms</div>;
|
value = <div className={cssStyle.value + " " + cssStyle.ping + " " + pingClass} key={"ping"} title={title}>{pingInfo.native.toFixed(0)}ms</div>;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue