2021-01-10 17:36:57 +01:00
|
|
|
import {AbstractConversationUiEvents, ChatHistoryState} from "./AbstractConversationDefinitions";
|
2020-12-09 13:36:56 +01:00
|
|
|
import {EventHandler, Registry} from "../../../events";
|
2021-01-10 17:36:57 +01:00
|
|
|
import {LogCategory, logError} from "../../../log";
|
|
|
|
import {tr, tra} from "../../../i18n/localize";
|
2020-12-09 13:36:56 +01:00
|
|
|
import {
|
|
|
|
AbstractChat,
|
|
|
|
AbstractChatManager,
|
2021-01-10 17:36:57 +01:00
|
|
|
AbstractChatManagerEvents,
|
|
|
|
AbstractConversationEvents
|
2020-12-09 13:36:56 +01:00
|
|
|
} from "tc-shared/conversations/AbstractConversion";
|
|
|
|
|
|
|
|
export const kMaxChatFrameMessageSize = 50; /* max 100 messages, since the server does not support more than 100 messages queried at once */
|
|
|
|
|
|
|
|
export abstract class AbstractConversationController<
|
2020-12-09 20:44:33 +01:00
|
|
|
Events extends AbstractConversationUiEvents,
|
2020-12-09 13:36:56 +01:00
|
|
|
Manager extends AbstractChatManager<ManagerEvents, ConversationType, ConversationEvents>,
|
|
|
|
ManagerEvents extends AbstractChatManagerEvents<ConversationType>,
|
|
|
|
ConversationType extends AbstractChat<ConversationEvents>,
|
2020-12-09 20:44:33 +01:00
|
|
|
ConversationEvents extends AbstractConversationEvents
|
2020-12-09 13:36:56 +01:00
|
|
|
> {
|
|
|
|
protected readonly uiEvents: Registry<Events>;
|
2020-12-09 20:44:33 +01:00
|
|
|
protected conversationManager: Manager | undefined;
|
|
|
|
protected listenerManager: (() => void)[];
|
2020-12-09 13:36:56 +01:00
|
|
|
|
|
|
|
protected currentSelectedConversation: ConversationType;
|
|
|
|
protected currentSelectedListener: (() => void)[];
|
|
|
|
|
2020-12-09 20:44:33 +01:00
|
|
|
protected constructor() {
|
2020-12-09 13:36:56 +01:00
|
|
|
this.uiEvents = new Registry<Events>();
|
|
|
|
this.currentSelectedListener = [];
|
|
|
|
this.listenerManager = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
destroy() {
|
|
|
|
this.listenerManager.forEach(callback => callback());
|
|
|
|
this.listenerManager.splice(0, this.listenerManager.length);
|
|
|
|
|
|
|
|
this.uiEvents.fire("notify_destroy");
|
|
|
|
this.uiEvents.destroy();
|
|
|
|
}
|
|
|
|
|
2020-12-09 20:44:33 +01:00
|
|
|
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)));
|
2020-12-18 17:06:38 +01:00
|
|
|
this.listenerManager.push(manager.events.on("notify_cross_conversation_support_changed", () => {
|
|
|
|
const currentConversation = this.getCurrentConversation();
|
|
|
|
if(currentConversation) {
|
|
|
|
this.reportStateToUI(currentConversation);
|
|
|
|
}
|
|
|
|
}));
|
2020-12-09 20:44:33 +01:00
|
|
|
}
|
|
|
|
|
2020-12-09 13:36:56 +01:00
|
|
|
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 })));
|
|
|
|
|
|
|
|
this.currentSelectedListener.push(conversation.events.on("notify_send_toggle", event =>
|
|
|
|
this.uiEvents.fire_react("notify_send_enabled", { chatId: conversation.getChatId(), enabled: event.enabled })));
|
|
|
|
|
|
|
|
this.currentSelectedListener.push(conversation.events.on("notify_chat_event", event => {
|
|
|
|
this.uiEvents.fire_react("notify_chat_event", { chatId: conversation.getChatId(), event: event.event, triggerUnread: event.triggerUnread });
|
|
|
|
}));
|
|
|
|
|
|
|
|
this.currentSelectedListener.push(conversation.events.on("notify_state_changed", () => {
|
|
|
|
this.reportStateToUI(conversation);
|
|
|
|
}));
|
|
|
|
|
|
|
|
this.currentSelectedListener.push(conversation.events.on("notify_history_state_changed", () => {
|
|
|
|
this.reportStateToUI(conversation);
|
|
|
|
}));
|
2020-12-09 14:22:22 +01:00
|
|
|
|
|
|
|
this.currentSelectedListener.push(conversation.events.on("notify_read_state_changed", () => {
|
|
|
|
this.reportStateToUI(conversation);
|
|
|
|
}));
|
2020-12-09 13:36:56 +01:00
|
|
|
}
|
|
|
|
|
2020-12-09 20:44:33 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-09 13:36:56 +01:00
|
|
|
protected reportStateToUI(conversation: AbstractChat<any>) {
|
|
|
|
let historyState: ChatHistoryState;
|
2020-12-09 20:44:33 +01:00
|
|
|
const localHistoryState = this.conversationManager.historyUiStates[conversation.getChatId()];
|
2020-12-09 13:36:56 +01:00
|
|
|
if(!localHistoryState) {
|
|
|
|
historyState = conversation.hasHistory() ? "available" : "none";
|
|
|
|
} else {
|
|
|
|
if(Date.now() < localHistoryState.historyRetryTimestamp && localHistoryState.historyErrorMessage) {
|
|
|
|
historyState = "error";
|
|
|
|
} else if(localHistoryState.executingUIHistoryQuery) {
|
|
|
|
historyState = "loading";
|
|
|
|
} else if(conversation.hasHistory()) {
|
|
|
|
historyState = "available";
|
|
|
|
} else {
|
|
|
|
historyState = "none";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (conversation.getCurrentMode()) {
|
|
|
|
case "normal":
|
2020-12-09 14:22:22 +01:00
|
|
|
if(conversation.isPrivate() && !conversation.isReadable()) {
|
2020-12-09 13:36:56 +01:00
|
|
|
this.uiEvents.fire_react("notify_conversation_state", {
|
|
|
|
chatId: conversation.getChatId(),
|
|
|
|
state: "private",
|
2020-12-18 17:06:38 +01:00
|
|
|
crossChannelChatSupported: this.conversationManager.hasCrossConversationSupport()
|
2020-12-09 13:36:56 +01:00
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.uiEvents.fire_react("notify_conversation_state", {
|
|
|
|
chatId: conversation.getChatId(),
|
|
|
|
state: "normal",
|
|
|
|
|
|
|
|
historyState: historyState,
|
|
|
|
historyErrorMessage: localHistoryState?.historyErrorMessage,
|
|
|
|
historyRetryTimestamp: localHistoryState ? localHistoryState.historyRetryTimestamp : 0,
|
|
|
|
|
|
|
|
chatFrameMaxMessageCount: kMaxChatFrameMessageSize,
|
|
|
|
unreadTimestamp: conversation.getUnreadTimestamp(),
|
|
|
|
|
2020-12-18 17:06:38 +01:00
|
|
|
showUserSwitchEvents: conversation.isPrivate() || !this.conversationManager.hasCrossConversationSupport(),
|
2020-12-09 13:36:56 +01:00
|
|
|
sendEnabled: conversation.isSendEnabled(),
|
|
|
|
|
|
|
|
events: [...conversation.getPresentEvents(), ...conversation.getPresentMessages()]
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "loading":
|
|
|
|
case "unloaded":
|
|
|
|
this.uiEvents.fire_react("notify_conversation_state", {
|
|
|
|
chatId: conversation.getChatId(),
|
|
|
|
state: "loading"
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "error":
|
|
|
|
this.uiEvents.fire_react("notify_conversation_state", {
|
|
|
|
chatId: conversation.getChatId(),
|
|
|
|
state: "error",
|
|
|
|
errorMessage: conversation.getErrorMessage()
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "no-permissions":
|
|
|
|
this.uiEvents.fire_react("notify_conversation_state", {
|
|
|
|
chatId: conversation.getChatId(),
|
|
|
|
state: "no-permissions",
|
|
|
|
failedPermission: conversation.getFailedPermission()
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public uiQueryHistory(conversation: AbstractChat<any>, timestamp: number, enforce?: boolean) {
|
2020-12-09 20:44:33 +01:00
|
|
|
const localHistoryState = this.conversationManager.historyUiStates[conversation.getChatId()] || (this.conversationManager.historyUiStates[conversation.getChatId()] = {
|
2020-12-09 13:36:56 +01:00
|
|
|
executingUIHistoryQuery: false,
|
|
|
|
historyErrorMessage: undefined,
|
|
|
|
historyRetryTimestamp: 0
|
|
|
|
});
|
|
|
|
|
|
|
|
if(localHistoryState.executingUIHistoryQuery && !enforce) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
localHistoryState.executingUIHistoryQuery = true;
|
|
|
|
conversation.queryHistory({ end: 1, begin: timestamp, limit: kMaxChatFrameMessageSize }).then(result => {
|
|
|
|
localHistoryState.executingUIHistoryQuery = false;
|
|
|
|
localHistoryState.historyErrorMessage = undefined;
|
|
|
|
localHistoryState.historyRetryTimestamp = result.nextAllowedQuery;
|
|
|
|
|
|
|
|
switch (result.status) {
|
|
|
|
case "success":
|
|
|
|
this.uiEvents.fire_react("notify_conversation_history", {
|
|
|
|
chatId: conversation.getChatId(),
|
|
|
|
state: "success",
|
|
|
|
|
|
|
|
hasMoreMessages: result.moreEvents,
|
|
|
|
retryTimestamp: localHistoryState.historyRetryTimestamp,
|
|
|
|
|
|
|
|
events: result.events
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "private":
|
|
|
|
this.uiEvents.fire_react("notify_conversation_history", {
|
|
|
|
chatId: conversation.getChatId(),
|
|
|
|
state: "error",
|
|
|
|
errorMessage: localHistoryState.historyErrorMessage = tr("chat is private"),
|
|
|
|
retryTimestamp: localHistoryState.historyRetryTimestamp
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "no-permission":
|
|
|
|
this.uiEvents.fire_react("notify_conversation_history", {
|
|
|
|
chatId: conversation.getChatId(),
|
|
|
|
state: "error",
|
|
|
|
errorMessage: localHistoryState.historyErrorMessage = tra("failed on {}", result.failedPermission || tr("unknown permission")),
|
|
|
|
retryTimestamp: localHistoryState.historyRetryTimestamp
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "error":
|
|
|
|
this.uiEvents.fire_react("notify_conversation_history", {
|
|
|
|
chatId: conversation.getChatId(),
|
|
|
|
state: "error",
|
|
|
|
errorMessage: localHistoryState.historyErrorMessage = result.errorMessage,
|
|
|
|
retryTimestamp: localHistoryState.historyRetryTimestamp
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
protected getCurrentConversation() : ConversationType | undefined {
|
|
|
|
return this.currentSelectedConversation;
|
|
|
|
}
|
|
|
|
|
2020-12-09 20:44:33 +01:00
|
|
|
@EventHandler<AbstractConversationUiEvents>("query_conversation_state")
|
|
|
|
protected handleQueryConversationState(event: AbstractConversationUiEvents["query_conversation_state"]) {
|
|
|
|
const conversation = this.conversationManager?.findConversationById(event.chatId);
|
2020-12-09 13:36:56 +01:00
|
|
|
if(!conversation) {
|
|
|
|
this.uiEvents.fire_react("notify_conversation_state", {
|
|
|
|
state: "error",
|
|
|
|
errorMessage: tr("Unknown conversation"),
|
|
|
|
|
|
|
|
chatId: event.chatId
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(conversation.getCurrentMode() === "unloaded") {
|
|
|
|
/* will switch the state to "loading" and already reports the state to the ui */
|
|
|
|
conversation.queryCurrentMessages();
|
|
|
|
} else {
|
|
|
|
this.reportStateToUI(conversation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-09 20:44:33 +01:00
|
|
|
@EventHandler<AbstractConversationUiEvents>("query_conversation_history")
|
|
|
|
protected handleQueryHistory(event: AbstractConversationUiEvents["query_conversation_history"]) {
|
|
|
|
const conversation = this.conversationManager?.findConversationById(event.chatId);
|
2020-12-09 13:36:56 +01:00
|
|
|
if(!conversation) {
|
|
|
|
this.uiEvents.fire_react("notify_conversation_history", {
|
|
|
|
state: "error",
|
|
|
|
errorMessage: tr("Unknown conversation"),
|
|
|
|
retryTimestamp: Date.now() + 10 * 1000,
|
|
|
|
|
|
|
|
chatId: event.chatId
|
|
|
|
});
|
|
|
|
|
2021-01-10 17:36:57 +01:00
|
|
|
logError(LogCategory.CLIENT, tr("Tried to query history for an unknown conversation with id %s"), event.chatId);
|
2020-12-09 13:36:56 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.uiQueryHistory(conversation, event.timestamp);
|
|
|
|
}
|
|
|
|
|
2020-12-09 20:44:33 +01:00
|
|
|
@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);
|
2020-12-09 13:36:56 +01:00
|
|
|
conversation?.setUnreadTimestamp(Date.now());
|
|
|
|
}
|
|
|
|
|
2020-12-09 20:44:33 +01:00
|
|
|
@EventHandler<AbstractConversationUiEvents>("action_send_message")
|
|
|
|
protected handleSendMessage(event: AbstractConversationUiEvents["action_send_message"]) {
|
|
|
|
const conversation = this.conversationManager?.findConversationById(event.chatId);
|
2020-12-09 13:36:56 +01:00
|
|
|
if(!conversation) {
|
2021-01-10 17:36:57 +01:00
|
|
|
logError(LogCategory.CLIENT, tr("Tried to send a chat message to an unknown conversation with id %s"), event.chatId);
|
2020-12-09 13:36:56 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
conversation.sendMessage(event.text);
|
|
|
|
}
|
|
|
|
|
2020-12-09 20:44:33 +01:00
|
|
|
@EventHandler<AbstractConversationUiEvents>("action_jump_to_present")
|
|
|
|
protected handleJumpToPresent(event: AbstractConversationUiEvents["action_jump_to_present"]) {
|
|
|
|
const conversation = this.conversationManager?.findConversationById(event.chatId);
|
2020-12-09 13:36:56 +01:00
|
|
|
if(!conversation) {
|
2021-01-10 17:36:57 +01:00
|
|
|
logError(LogCategory.CLIENT, tr("Tried to jump to present for an unknown conversation with id %s"), event.chatId);
|
2020-12-09 13:36:56 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.reportStateToUI(conversation);
|
|
|
|
}
|
|
|
|
|
2020-12-09 20:44:33 +01:00
|
|
|
@EventHandler<AbstractConversationUiEvents>("query_selected_chat")
|
2020-12-09 13:36:56 +01:00
|
|
|
private handleQuerySelectedChat() {
|
2020-12-09 20:44:33 +01:00
|
|
|
this.uiEvents.fire_react("notify_selected_chat", { chatId: this.currentSelectedConversation ? this.currentSelectedConversation.getChatId() : "unselected"});
|
2020-12-09 13:36:56 +01:00
|
|
|
}
|
|
|
|
|
2020-12-09 20:44:33 +01:00
|
|
|
@EventHandler<AbstractConversationUiEvents>("action_select_chat")
|
|
|
|
private handleActionSelectChat(event: AbstractConversationUiEvents["action_select_chat"]) {
|
|
|
|
const conversation = this.conversationManager?.findConversationById(event.chatId);
|
2020-12-09 13:36:56 +01:00
|
|
|
this.conversationManager.setSelectedConversation(conversation);
|
|
|
|
}
|
|
|
|
}
|