UNPKG

@azure/communication-react

Version:

React library for building modern communication user experiences utilizing Azure Communication Services

187 lines • 10.7 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __asyncValues = (this && this.__asyncValues) || function (o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } }; import { toFlatCommunicationIdentifier } from "../../acs-ui-common/src"; import { convertChatMessage } from './convertChatMessage'; // TODO: When we can get messageId of event from SDK, remove this // Maximum time to look back message list when we receive a system event const maxSyncTimeInMs = 10 * 1000; /** * @private */ export class EventSubscriber { constructor(chatClient, chatContext) { this.convertEventToChatMessage = (event) => { return convertChatMessage({ id: event.id, version: event.version, content: { message: event.message, attachments: event.attachments }, type: this.convertEventType(event.type), sender: event.sender, senderDisplayName: event.senderDisplayName, sequenceId: '', // Note: there is a bug in chatMessageReceived event that it is missing sequenceId createdOn: new Date(event.createdOn), editedOn: 'editedOn' in event ? event.editedOn : undefined, metadata: event.metadata }); }; // convert event type to chatMessage type, only possible type is 'html' and 'text' in chat event this.convertEventType = (type) => { const lowerCaseType = type.toLowerCase(); if (lowerCaseType === 'richtext/html' || lowerCaseType === 'html') { return 'html'; } else { return 'text'; } }; this.onChatMessageReceived = (event) => { var _a; // Today we are avoiding how to render these messages. In the future we can // remove this condition and handle this message appropriately. const messageEventType = event.type.toLowerCase(); if (messageEventType !== 'text' && messageEventType !== 'richtext/html' && messageEventType !== 'html') { return; } const newMessage = this.convertEventToChatMessage(event); // Because of bug in chatMessageReceived event, if we already have that particular message in context, we want to // make sure to not overwrite the sequenceId when calling setChatMessage. const existingMessage = (_a = this.chatContext.getState().threads[event.threadId]) === null || _a === void 0 ? void 0 : _a.chatMessages[event.id]; if (existingMessage) { newMessage.sequenceId = existingMessage.sequenceId; } this.chatContext.batch(() => { this.chatContext.createThreadIfNotExist(event.threadId); this.chatContext.setChatMessage(event.threadId, newMessage); }); }; this.onChatMessageDeleted = (event) => { this.chatContext.deleteMessage(event.threadId, event.id); }; this.onChatMessageEdited = (event) => { const editedMessage = this.convertEventToChatMessage(event); this.chatContext.setChatMessage(event.threadId, convertChatMessage(editedMessage)); }; this.onParticipantsAdded = (event) => { const participantsToAdd = event.participantsAdded.map(participant => (Object.assign(Object.assign({}, participant), { shareHistoryTime: participant.shareHistoryTime ? new Date(participant.shareHistoryTime) : undefined }))); this.chatContext.batch(() => { this.chatContext.createThreadIfNotExist(event.threadId); this.chatContext.setParticipants(event.threadId, participantsToAdd); }); this.fetchLastParticipantMessage(event.threadId, 'participantAdded'); }; // This is a temporary fix that no participant message is received for onChatMessageReceived event, which should be handled by JS SDK. // Without the temporary fix, there are missing 'participant joined' and 'participant left' system messages in the chat thread. this.fetchLastParticipantMessage = (threadId, actionType) => __awaiter(this, void 0, void 0, function* () { var _a, e_1, _b, _c; try { for (var _d = true, _e = __asyncValues(this.chatClient.getChatThreadClient(threadId).listMessages({ startTime: new Date(Date.now() - maxSyncTimeInMs) })), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) { _c = _f.value; _d = false; const message = _c; if (message.type === actionType) { this.chatContext.setChatMessage(threadId, Object.assign(Object.assign({}, message), { status: 'delivered' })); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (!_d && !_a && (_b = _e.return)) yield _b.call(_e); } finally { if (e_1) throw e_1.error; } } }); this.onParticipantsRemoved = (event) => { const participantIds = event.participantsRemoved.map(participant => { return participant.id; }); this.chatContext.deleteParticipants(event.threadId, participantIds); // If the current user is removed from the thread, do not fetch the last participant message // as they no longer have access to the thread. const currentUserId = toFlatCommunicationIdentifier(this.chatContext.getState().userId); const wasCurrentUserRemoved = participantIds.find(id => toFlatCommunicationIdentifier(id) === currentUserId); if (!wasCurrentUserRemoved) { this.fetchLastParticipantMessage(event.threadId, 'participantRemoved'); } }; this.onReadReceiptReceived = (event) => { const readReceipt = Object.assign(Object.assign({}, event), { sender: event.sender, readOn: new Date(event.readOn) }); this.chatContext.batch(() => { this.chatContext.createThreadIfNotExist(event.threadId); this.chatContext.addReadReceipt(event.threadId, readReceipt); }); }; this.onTypingIndicatorReceived = (typingIndicator) => { this.chatContext.batch(() => { this.chatContext.createThreadIfNotExist(typingIndicator.threadId); this.chatContext.addTypingIndicator(typingIndicator.threadId, typingIndicator); }); }; this.onChatThreadCreated = (event) => { const properties = { topic: event.properties.topic }; if (!this.chatContext.createThreadIfNotExist(event.threadId, properties)) { this.chatContext.updateThread(event.threadId, properties); } }; this.onChatThreadDeleted = (event) => { this.chatContext.deleteThread(event.threadId); }; this.onChatThreadPropertiesUpdated = (event) => { this.chatContext.updateThread(event.threadId, { topic: event.properties.topic }); }; this.subscribe = () => { this.chatClient.on('chatMessageReceived', this.onChatMessageReceived); this.chatClient.on('chatMessageDeleted', this.onChatMessageDeleted); this.chatClient.on('chatMessageEdited', this.onChatMessageEdited); this.chatClient.on('participantsAdded', this.onParticipantsAdded); this.chatClient.on('participantsRemoved', this.onParticipantsRemoved); this.chatClient.on('readReceiptReceived', this.onReadReceiptReceived); this.chatClient.on('typingIndicatorReceived', this.onTypingIndicatorReceived); this.chatClient.on('chatThreadCreated', this.onChatThreadCreated); this.chatClient.on('chatThreadDeleted', this.onChatThreadDeleted); this.chatClient.on('chatThreadPropertiesUpdated', this.onChatThreadPropertiesUpdated); }; this.unsubscribe = () => { this.chatClient.off('chatMessageReceived', this.onChatMessageReceived); this.chatClient.off('chatMessageDeleted', this.onChatMessageDeleted); this.chatClient.off('chatMessageEdited', this.onChatMessageEdited); this.chatClient.off('participantsAdded', this.onParticipantsAdded); this.chatClient.off('participantsRemoved', this.onParticipantsRemoved); this.chatClient.off('readReceiptReceived', this.onReadReceiptReceived); this.chatClient.off('typingIndicatorReceived', this.onTypingIndicatorReceived); this.chatClient.off('chatThreadCreated', this.onChatThreadCreated); this.chatClient.off('chatThreadDeleted', this.onChatThreadDeleted); this.chatClient.off('chatThreadPropertiesUpdated', this.onChatThreadPropertiesUpdated); }; this.chatClient = chatClient; this.chatContext = chatContext; this.subscribe(); } } //# sourceMappingURL=EventSubscriber.js.map