4623 lines
244 KiB
JavaScript
4623 lines
244 KiB
JavaScript
"use strict";
|
||
(self["webpackChunktinode_webapp"] = self["webpackChunktinode_webapp"] || []).push([["src_views_messages-view_jsx"],{
|
||
|
||
/***/ "./src/views/logo-view.jsx":
|
||
/*!*********************************!*\
|
||
!*** ./src/views/logo-view.jsx ***!
|
||
\*********************************/
|
||
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||
|
||
__webpack_require__.r(__webpack_exports__);
|
||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||
/* harmony export */ "default": function() { return /* binding */ LogoView; }
|
||
/* harmony export */ });
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
||
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
||
/* harmony import */ var react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! react/jsx-dev-runtime */ "./node_modules/react/jsx-dev-runtime.js");
|
||
|
||
|
||
|
||
function Icon({
|
||
children
|
||
}) {
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
"aria-hidden": "true",
|
||
children: children
|
||
}, void 0, false);
|
||
}
|
||
class LogoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||
render() {
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)("div", {
|
||
id: "dummy-view",
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)("div", {
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)("span", {
|
||
id: "dummy-view-icon",
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)(Icon, {
|
||
children: "forum"
|
||
}, void 0, false)
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)("h2", {
|
||
children: _config_js__WEBPACK_IMPORTED_MODULE_1__.BASE_APP_NAME
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)("p", {
|
||
children: "选择一个会话开始聊天"
|
||
}, void 0, false)]
|
||
}, void 0, true)
|
||
}, void 0, false);
|
||
}
|
||
}
|
||
;
|
||
|
||
/***/ }),
|
||
|
||
/***/ "./src/views/messages-view.jsx":
|
||
/*!*************************************!*\
|
||
!*** ./src/views/messages-view.jsx ***!
|
||
\*************************************/
|
||
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||
|
||
__webpack_require__.r(__webpack_exports__);
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ "react-intl");
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
|
||
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! tinode-sdk */ "tinode-sdk");
|
||
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__);
|
||
/* harmony import */ var _widgets_chat_message_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../widgets/chat-message.jsx */ "./src/widgets/chat-message.jsx");
|
||
/* harmony import */ var _widgets_contact_badges_jsx__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../widgets/contact-badges.jsx */ "./src/widgets/contact-badges.jsx");
|
||
/* harmony import */ var _widgets_error_panel_jsx__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../widgets/error-panel.jsx */ "./src/widgets/error-panel.jsx");
|
||
/* harmony import */ var _widgets_group_subs_jsx__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../widgets/group-subs.jsx */ "./src/widgets/group-subs.jsx");
|
||
/* harmony import */ var _widgets_invitation_jsx__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../widgets/invitation.jsx */ "./src/widgets/invitation.jsx");
|
||
/* harmony import */ var _widgets_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../widgets/letter-tile.jsx */ "./src/widgets/letter-tile.jsx");
|
||
/* harmony import */ var _widgets_load_spinner_jsx__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../widgets/load-spinner.jsx */ "./src/widgets/load-spinner.jsx");
|
||
/* harmony import */ var _logo_view_jsx__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./logo-view.jsx */ "./src/views/logo-view.jsx");
|
||
/* harmony import */ var _widgets_meta_message_jsx__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../widgets/meta-message.jsx */ "./src/widgets/meta-message.jsx");
|
||
/* harmony import */ var _widgets_pinned_messages_jsx__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../widgets/pinned-messages.jsx */ "./src/widgets/pinned-messages.jsx");
|
||
/* harmony import */ var _security_support_view_jsx__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./security-support-view.jsx */ "./src/views/security-support-view.jsx");
|
||
/* harmony import */ var _widgets_send_message_jsx__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../widgets/send-message.jsx */ "./src/widgets/send-message.jsx");
|
||
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
||
/* harmony import */ var _constants_js__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../constants.js */ "./src/constants.js");
|
||
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
|
||
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
|
||
/* harmony import */ var _lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ../lib/im-api.js */ "./src/lib/im-api.js");
|
||
/* harmony import */ var _lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ../lib/navigation.js */ "./src/lib/navigation.js");
|
||
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
|
||
/* harmony import */ var react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! react/jsx-dev-runtime */ "./node_modules/react/jsx-dev-runtime.js");
|
||
|
||
|
||
|
||
const LOCAL_SEQID = 0xFFFFFFF;
|
||
const CallPanel = react__WEBPACK_IMPORTED_MODULE_0___default().lazy(_ => __webpack_require__.e(/*! import() */ "src_widgets_call-panel_jsx").then(__webpack_require__.bind(__webpack_require__, /*! ../widgets/call-panel.jsx */ "./src/widgets/call-panel.jsx")));
|
||
|
||
|
||
const DocPreview = react__WEBPACK_IMPORTED_MODULE_0___default().lazy(_ => __webpack_require__.e(/*! import() */ "src_widgets_doc-preview_jsx").then(__webpack_require__.bind(__webpack_require__, /*! ../widgets/doc-preview.jsx */ "./src/widgets/doc-preview.jsx")));
|
||
|
||
|
||
const ImagePreview = react__WEBPACK_IMPORTED_MODULE_0___default().lazy(_ => __webpack_require__.e(/*! import() */ "src_widgets_image-preview_jsx").then(__webpack_require__.bind(__webpack_require__, /*! ../widgets/image-preview.jsx */ "./src/widgets/image-preview.jsx")));
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
const TheCardPreview = react__WEBPACK_IMPORTED_MODULE_0___default().lazy(_ => Promise.all(/*! import() */[__webpack_require__.e("vendors-node_modules_libphonenumber-js_mobile_exports_parsePhoneNumberWithError_js"), __webpack_require__.e("src_widgets_the-card-preview_jsx")]).then(__webpack_require__.bind(__webpack_require__, /*! ../widgets/the-card-preview.jsx */ "./src/widgets/the-card-preview.jsx")));
|
||
const VideoPreview = react__WEBPACK_IMPORTED_MODULE_0___default().lazy(_ => __webpack_require__.e(/*! import() */ "src_widgets_video-preview_jsx").then(__webpack_require__.bind(__webpack_require__, /*! ../widgets/video-preview.jsx */ "./src/widgets/video-preview.jsx")));
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
const NOTIFICATION_EXEC_INTERVAL = 300;
|
||
const SHOW_GO_TO_LAST_DIST = 100;
|
||
const FETCH_PAGE_TRIGGER = 40;
|
||
const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
|
||
message_deleted: {
|
||
id: 'message_deleted',
|
||
defaultMessage: 'message deleted',
|
||
description: 'Placeholder shown in the timeline where a message was recalled or deleted'
|
||
},
|
||
online_now: {
|
||
id: 'online_now',
|
||
defaultMessage: 'online now',
|
||
description: 'Indicator that the user or topic is currently online'
|
||
},
|
||
last_seen: {
|
||
id: 'last_seen_timestamp',
|
||
defaultMessage: 'Last seen: {timestamp}',
|
||
description: 'Label for the timestamp of when the user or topic was last online'
|
||
},
|
||
not_found: {
|
||
id: 'title_not_found',
|
||
defaultMessage: 'Not found',
|
||
description: 'Title shown when topic is not found'
|
||
},
|
||
channel: {
|
||
id: 'channel',
|
||
defaultMessage: 'channel',
|
||
description: 'Subtitle shown for channels in MessagesView instead of last seen'
|
||
},
|
||
file_attachment_too_large: {
|
||
id: 'file_attachment_too_large',
|
||
defaultMessage: 'The file size {size} exceeds the {limit} limit.',
|
||
description: 'Error message when attachment is too large'
|
||
},
|
||
invalid_content: {
|
||
id: 'invalid_content',
|
||
defaultMessage: 'invalid content',
|
||
description: 'Shown when the message is unreadable'
|
||
},
|
||
editing_message: {
|
||
id: 'editing_message',
|
||
defaultMessage: 'Editing',
|
||
description: 'Title over message editing preview'
|
||
},
|
||
drag_file: {
|
||
id: 'drag_file',
|
||
defaultMessage: 'Drag file here',
|
||
description: 'Prompt on the file drag-n-drop overlay banner'
|
||
},
|
||
self_topic_name: {
|
||
id: 'self_topic_name',
|
||
defaultMessage: 'Saved messages',
|
||
description: 'Name of self topic for UI'
|
||
},
|
||
subscriber_count: {
|
||
id: 'subscriber_count',
|
||
defaultMessage: '{count, plural, ' + 'one {{count, number} subscriber} ' + 'two {{count, number} subscribers} ' + 'three {{count, number} subscribers} ' + 'few {{count, number} subscribers} ' + 'many {{count, number} subscribers} ' + 'other {{count, number} subscribers}}',
|
||
description: 'Count of channel subscribers'
|
||
},
|
||
member_count: {
|
||
id: 'member_count',
|
||
defaultMessage: '{count, plural, ' + 'one {{count, number} member} ' + 'two {{count, number} members} ' + 'three {{count, number} members} ' + 'few {{count, number} members} ' + 'many {{count, number} members} ' + 'other {{count, number} members}}',
|
||
description: 'Count of group topic members'
|
||
},
|
||
cannot_parse_vcard: {
|
||
id: 'cannot_parse_vcard',
|
||
defaultMessage: 'Cannot parse vCard file.',
|
||
description: 'Error message when vCard file cannot be parsed'
|
||
}
|
||
});
|
||
function isUnconfirmed(acs) {
|
||
if (acs) {
|
||
const ex = acs.getExcessive() || '';
|
||
return acs.isJoiner('given') && (ex.includes('R') || ex.includes('W'));
|
||
}
|
||
return false;
|
||
}
|
||
function isPeerRestricted(acs) {
|
||
if (acs) {
|
||
const ms = acs.getMissing() || '';
|
||
return acs.isJoiner('want') && (ms.includes('R') || ms.includes('W'));
|
||
}
|
||
return false;
|
||
}
|
||
const MENTION_ALL_UID = '__mention_all__';
|
||
function shouldPresentCallPanel(callState) {
|
||
return callState == _constants_js__WEBPACK_IMPORTED_MODULE_16__.CALL_STATE_OUTGOING_INITATED || callState == _constants_js__WEBPACK_IMPORTED_MODULE_16__.CALL_STATE_IN_PROGRESS;
|
||
}
|
||
class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
|
||
constructor(props) {
|
||
super(props);
|
||
this.state = MessagesView.getDerivedStateFromProps(props, {});
|
||
this.state.messagePolicy = null;
|
||
this.state.toast = null;
|
||
this.state.riskDialog = null;
|
||
this.mentionedUsers = [];
|
||
this.mentionAllPending = false;
|
||
this.componentSetup = this.componentSetup.bind(this);
|
||
this.leave = this.leave.bind(this);
|
||
this.sendMessage = this.sendMessage.bind(this);
|
||
this.handleRiskConfirm = this.handleRiskConfirm.bind(this);
|
||
this.handleRiskCancel = this.handleRiskCancel.bind(this);
|
||
this.retrySend = this.retrySend.bind(this);
|
||
this.sendImageAttachment = this.sendImageAttachment.bind(this);
|
||
this.sendVideoAttachment = this.sendVideoAttachment.bind(this);
|
||
this.sendFileAttachment = this.sendFileAttachment.bind(this);
|
||
this.sendAudioAttachment = this.sendAudioAttachment.bind(this);
|
||
this.sendTheCardAttachment = this.sendTheCardAttachment.bind(this);
|
||
this.sendKeyPress = this.sendKeyPress.bind(this);
|
||
this.subscribe = this.subscribe.bind(this);
|
||
this.handleScrollReference = this.handleScrollReference.bind(this);
|
||
this.mountDnDEvents = this.mountDnDEvents.bind(this);
|
||
this.handleScrollEvent = this.handleScrollEvent.bind(this);
|
||
this.handleDescChange = this.handleDescChange.bind(this);
|
||
this.handleSubsUpdated = this.handleSubsUpdated.bind(this);
|
||
this.handleMessageUpdate = this.handleMessageUpdate.bind(this);
|
||
this.handleAuxUpdate = this.handleAuxUpdate.bind(this);
|
||
this.handleAllMessagesReceived = this.handleAllMessagesReceived.bind(this);
|
||
this.handleInfoReceipt = this.handleInfoReceipt.bind(this);
|
||
this.handleExpandMedia = this.handleExpandMedia.bind(this);
|
||
this.handleClosePreview = this.handleClosePreview.bind(this);
|
||
this.handleCallPanelToggle = this.handleCallPanelToggle.bind(this);
|
||
this.handleFormResponse = this.handleFormResponse.bind(this);
|
||
this.handleContextClick = this.handleContextClick.bind(this);
|
||
this.handleShowMessageContextMenu = this.handleShowMessageContextMenu.bind(this);
|
||
this.handleNewChatAcceptance = this.handleNewChatAcceptance.bind(this);
|
||
this.handleEnablePeer = this.handleEnablePeer.bind(this);
|
||
this.loadRelationshipContext = this.loadRelationshipContext.bind(this);
|
||
this.loadGroupContext = this.loadGroupContext.bind(this);
|
||
this.loadDraft = this.loadDraft.bind(this);
|
||
this.handleComposerChange = this.handleComposerChange.bind(this);
|
||
this.clearDraft = this.clearDraft.bind(this);
|
||
this.mentionSearch = this.mentionSearch.bind(this);
|
||
this.mentionSelected = this.mentionSelected.bind(this);
|
||
this.handleShowAnnouncement = this.handleShowAnnouncement.bind(this);
|
||
this.handleShowJoinRequests = this.handleShowJoinRequests.bind(this);
|
||
this.handleSendFriendVerification = this.handleSendFriendVerification.bind(this);
|
||
this.handleAcceptGroupInvitation = this.handleAcceptGroupInvitation.bind(this);
|
||
this.handleBatchDelete = this.handleBatchDelete.bind(this);
|
||
this.handleBatchFavorite = this.handleBatchFavorite.bind(this);
|
||
this.handleAttachFile = this.handleAttachFile.bind(this);
|
||
this.handleAttachImageOrVideo = this.handleAttachImageOrVideo.bind(this);
|
||
this.handleCancelUpload = this.handleCancelUpload.bind(this);
|
||
this.postReadNotification = this.postReadNotification.bind(this);
|
||
this.clearNotificationQueue = this.clearNotificationQueue.bind(this);
|
||
this.goToLatestMessage = this.goToLatestMessage.bind(this);
|
||
this.handleFileDrop = this.handleFileDrop.bind(this);
|
||
this.handlePickReply = this.handlePickReply.bind(this);
|
||
this.handleEditMessage = this.handleEditMessage.bind(this);
|
||
this.handleCancelReply = this.handleCancelReply.bind(this);
|
||
this.handleQuoteClick = this.handleQuoteClick.bind(this);
|
||
this.jumpToMessage = this.jumpToMessage.bind(this);
|
||
this.handleUnpinMessage = this.handleUnpinMessage.bind(this);
|
||
this.handleCallHangup = this.handleCallHangup.bind(this);
|
||
this.isDragEnabled = this.isDragEnabled.bind(this);
|
||
this.handleDragStart = this.handleDragStart.bind(this);
|
||
this.handleDragIn = this.handleDragIn.bind(this);
|
||
this.handleDragOut = this.handleDragOut.bind(this);
|
||
this.handleDrag = this.handleDrag.bind(this);
|
||
this.handleDrop = this.handleDrop.bind(this);
|
||
this.chatMessageRefs = [];
|
||
this.getOrCreateMessageRef = this.getOrCreateMessageRef.bind(this);
|
||
this.getVisibleMessageRange = this.getVisibleMessageRange.bind(this);
|
||
this.dragCounter = 0;
|
||
this.dndRef = null;
|
||
this.readNotificationQueue = [];
|
||
this.readNotificationTimer = null;
|
||
this.keyPressTimer = null;
|
||
}
|
||
getOrCreateMessageRef(seqId) {
|
||
if (this.chatMessageRefs[seqId]) {
|
||
return this.chatMessageRefs[seqId];
|
||
}
|
||
const ref = react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
|
||
this.chatMessageRefs[seqId] = ref;
|
||
return ref;
|
||
}
|
||
getVisibleMessageRange(holderRect) {
|
||
let min = Number.MAX_SAFE_INTEGER,
|
||
max = -1;
|
||
let visibilityStatus = false;
|
||
this.chatMessageRefs.every((ref, seq) => {
|
||
if (ref.current) {
|
||
const {
|
||
top,
|
||
bottom,
|
||
height
|
||
} = ref.current.getBoundingClientRect();
|
||
const visible = top <= holderRect.top ? holderRect.top - top <= height : bottom - holderRect.bottom <= height;
|
||
if (visible) {
|
||
visibilityStatus = true;
|
||
min = Math.min(min, seq);
|
||
max = Math.max(max, seq);
|
||
} else if (visibilityStatus) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
});
|
||
return max >= min ? {
|
||
min: min,
|
||
max: max
|
||
} : {
|
||
min: 0,
|
||
max: 0
|
||
};
|
||
}
|
||
componentDidMount() {
|
||
if (this.messagesScroller) {
|
||
this.messagesScroller.addEventListener('scroll', this.handleScrollEvent);
|
||
}
|
||
this.mountDnDEvents(this.dndRef);
|
||
this.componentSetup({}, {});
|
||
if (this.props.jumpToSeq) {
|
||
const seq = parseInt(this.props.jumpToSeq, 10);
|
||
const topicObj = this.props.tinode && this.props.topic ? this.props.tinode.getTopic(this.props.topic) : null;
|
||
const attemptJump = () => this.jumpToMessage(seq);
|
||
if (topicObj && !topicObj.isSubscribed()) {
|
||
let tries = 0;
|
||
const waitForSubscription = () => {
|
||
if (topicObj.isSubscribed() || ++tries > 25) {
|
||
attemptJump();
|
||
} else {
|
||
window.setTimeout(waitForSubscription, 200);
|
||
}
|
||
};
|
||
waitForSubscription();
|
||
} else {
|
||
attemptJump();
|
||
}
|
||
}
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.getBootstrap)().then(data => {
|
||
this.setState({
|
||
messagePolicy: data.message_policy || null
|
||
});
|
||
}).catch(_ => {});
|
||
}
|
||
componentWillUnmount() {
|
||
if (this.state.rtcPanel) {
|
||
this.handleCallHangup(this.state.topic, this.props.callSeq);
|
||
}
|
||
if (this.messagesScroller) {
|
||
this.messagesScroller.removeEventListener('scroll', this.handleScrollEvent);
|
||
}
|
||
window.clearTimeout(this.toastTimer);
|
||
this.clearNotificationQueue();
|
||
if (this.dndRef) {
|
||
this.dndRef.removeEventListener('dragstart', this.handleDragStart);
|
||
this.dndRef.removeEventListener('dragenter', this.handleDragIn);
|
||
this.dndRef.removeEventListener('dragleave', this.handleDragOut);
|
||
this.dndRef.removeEventListener('dragover', this.handleDrag);
|
||
this.dndRef.removeEventListener('drop', this.handleDrop);
|
||
}
|
||
}
|
||
componentDidUpdate(prevProps, prevState) {
|
||
if (this.messagesScroller && (prevState.topic != this.state.topic || prevState.maxSeqId != this.state.maxSeqId || prevState.minSeqId != this.state.minSeqId)) {
|
||
if (this.state.scrollPosition < SHOW_GO_TO_LAST_DIST) {
|
||
this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition - this.messagesScroller.offsetHeight;
|
||
}
|
||
}
|
||
if (!this.props.applicationVisible) {
|
||
this.clearNotificationQueue();
|
||
} else {
|
||
this.postReadNotification(0);
|
||
}
|
||
if (this.props.jumpToSeq && this.props.jumpToSeq != prevProps.jumpToSeq) {
|
||
this.jumpToMessage(parseInt(this.props.jumpToSeq, 10));
|
||
}
|
||
this.componentSetup(prevProps, prevState);
|
||
}
|
||
componentSetup(prevProps, prevState) {
|
||
const topic = this.props.tinode ? this.props.tinode.getTopic(this.state.topic) : undefined;
|
||
if (this.state.topic != prevState.topic) {
|
||
if (prevState.topic && !tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.isNewGroupTopicName(prevState.topic)) {
|
||
this.leave(prevState.topic);
|
||
if (prevState.rtcPanel) {
|
||
this.handleCallHangup(prevState.topic, prevProps.callSeq);
|
||
}
|
||
}
|
||
if (topic) {
|
||
topic.onData = this.handleMessageUpdate;
|
||
topic.onAllMessagesReceived = this.handleAllMessagesReceived;
|
||
topic.onInfo = this.handleInfoReceipt;
|
||
topic.onMetaDesc = this.handleDescChange;
|
||
topic.onSubsUpdated = this.handleSubsUpdated;
|
||
topic.onPres = this.handleSubsUpdated;
|
||
topic.onAuxUpdated = this.handleAuxUpdate;
|
||
}
|
||
this.mentionedUsers = [];
|
||
this.mentionAllPending = false;
|
||
this.loadRelationshipContext(topic);
|
||
this.loadGroupContext(topic);
|
||
this.loadDraft(topic);
|
||
}
|
||
if (topic) {
|
||
if (this.state.topic != prevState.topic || this.props.myUserId && !prevProps.myUserId) {
|
||
const newTopic = this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic;
|
||
if (topic.isP2PType() && newTopic && !_config_js__WEBPACK_IMPORTED_MODULE_15__.IMMEDIATE_P2P_SUBSCRIPTION) {
|
||
topic.getMeta(topic.startMetaQuery().withDesc().build());
|
||
} else if (this.props.myUserId) {
|
||
this.subscribe(topic);
|
||
}
|
||
} else if (topic.isSubscribed() && this.state.isReader && !prevState.isReader) {
|
||
topic.getMeta(topic.startMetaQuery().withLaterData().build());
|
||
} else if (!topic.isSubscribed() && this.props.ready && !prevProps.ready) {
|
||
this.subscribe(topic);
|
||
}
|
||
}
|
||
}
|
||
static getDerivedStateFromProps(nextProps, prevState) {
|
||
let nextState = {};
|
||
if (!nextProps.topic) {
|
||
nextState = {
|
||
minSeqId: -1,
|
||
maxSeqId: -1,
|
||
latestClearId: -1,
|
||
onlineSubs: [],
|
||
topic: null,
|
||
title: '',
|
||
avatar: null,
|
||
isVerified: false,
|
||
isStaff: false,
|
||
isDangerous: false,
|
||
deleted: false,
|
||
docPreview: null,
|
||
imagePreview: null,
|
||
imagePostview: null,
|
||
videoPreview: null,
|
||
videoPostview: null,
|
||
rtcPanel: null,
|
||
minimizedCallPanel: false,
|
||
typingIndicator: false,
|
||
scrollPosition: 0,
|
||
fetchingMessages: false,
|
||
peerMessagingDisabled: false,
|
||
channel: false,
|
||
reply: null,
|
||
contentToEdit: null,
|
||
showGoToLastButton: false,
|
||
dragging: false,
|
||
pins: [],
|
||
pinsLoaded: false,
|
||
selectedPin: 0,
|
||
subsVersion: 0,
|
||
relationshipContext: null,
|
||
groupRoles: null,
|
||
myGroupRole: null,
|
||
groupAnnouncement: null,
|
||
pendingJoinRequests: 0,
|
||
draftText: null,
|
||
draftVersion: 0
|
||
};
|
||
} else if (nextProps.topic != prevState.topic) {
|
||
const topic = nextProps.tinode.getTopic(nextProps.topic);
|
||
nextState = {
|
||
topic: nextProps.topic,
|
||
deleted: topic._deleted,
|
||
docPreview: null,
|
||
imagePreview: null,
|
||
imagePostview: null,
|
||
videoPreview: null,
|
||
videoPostview: null,
|
||
rtcPanel: null,
|
||
minimizedCallPanel: false,
|
||
typingIndicator: false,
|
||
scrollPosition: 0,
|
||
fetchingMessages: false,
|
||
showGoToLastButton: false,
|
||
contentToEdit: null,
|
||
dragging: false,
|
||
selectedPin: 0,
|
||
relationshipContext: null,
|
||
groupRoles: null,
|
||
myGroupRole: null,
|
||
groupAnnouncement: null,
|
||
pendingJoinRequests: 0,
|
||
draftText: null,
|
||
draftVersion: 0,
|
||
riskDialog: null
|
||
};
|
||
if (nextProps.forwardMessage) {
|
||
if (nextProps.forwardMessage.content) {
|
||
const content = nextProps.forwardMessage.content;
|
||
const size = content?.length || content?.size || 0;
|
||
nextState.docPreview = {
|
||
object: content,
|
||
type: nextProps.forwardMessage.type,
|
||
size: size
|
||
};
|
||
} else {
|
||
nextState.reply = {
|
||
content: nextProps.forwardMessage.preview,
|
||
seq: null
|
||
};
|
||
}
|
||
} else {
|
||
nextState.reply = null;
|
||
}
|
||
if (topic) {
|
||
const subs = [];
|
||
if (nextProps.connected) {
|
||
topic.subscribers(sub => {
|
||
if (sub.online && sub.user != nextProps.myUserId) {
|
||
subs.push(sub);
|
||
}
|
||
});
|
||
}
|
||
Object.assign(nextState, {
|
||
onlineSubs: subs
|
||
});
|
||
if (topic.isSelfType()) {
|
||
Object.assign(nextState, {
|
||
title: nextProps.intl.formatMessage(messages.self_topic_name),
|
||
avatar: true
|
||
});
|
||
} else if (topic.public) {
|
||
Object.assign(nextState, {
|
||
title: (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_18__.topicDisplayName)(topic.name, topic.public, ''),
|
||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.makeImageUrl)(topic.public.photo)
|
||
});
|
||
} else {
|
||
Object.assign(nextState, {
|
||
title: '',
|
||
avatar: null
|
||
});
|
||
}
|
||
const peer = topic.p2pPeerDesc();
|
||
if (peer) {
|
||
Object.assign(nextState, {
|
||
peerMessagingDisabled: isPeerRestricted(peer.acs)
|
||
});
|
||
} else if (prevState.peerMessagingDisabled) {
|
||
Object.assign(nextState, {
|
||
peerMessagingDisabled: false
|
||
});
|
||
}
|
||
Object.assign(nextState, {
|
||
minSeqId: topic.minMsgSeq(),
|
||
maxSeqId: topic.maxMsgSeq(),
|
||
latestClearId: topic.maxClearId(),
|
||
channel: topic.isChannelType(),
|
||
pins: (topic.aux('pins') || []).slice(),
|
||
pinsLoaded: false
|
||
});
|
||
if (nextProps.callTopic == topic.name && shouldPresentCallPanel(nextProps.callState)) {
|
||
nextState.rtcPanel = nextProps.callTopic;
|
||
}
|
||
} else {
|
||
Object.assign(nextState, {
|
||
minSeqId: -1,
|
||
maxSeqId: -1,
|
||
latestClearId: -1,
|
||
onlineSubs: [],
|
||
title: '',
|
||
avatar: null,
|
||
peerMessagingDisabled: false,
|
||
channel: false,
|
||
pins: [],
|
||
pinsLoaded: false
|
||
});
|
||
}
|
||
} else {
|
||
if (nextProps.callTopic == prevState.topic && !prevState.rtcPanel && shouldPresentCallPanel(nextProps.callState)) {
|
||
nextState.rtcPanel = nextProps.callTopic;
|
||
}
|
||
}
|
||
if (nextProps.acs) {
|
||
if (nextProps.acs.isWriter() != prevState.isWriter) {
|
||
nextState.isWriter = !prevState.isWriter;
|
||
}
|
||
if (nextProps.acs.isReader() != prevState.isReader) {
|
||
nextState.isReader = !prevState.isReader;
|
||
}
|
||
if (nextProps.acs.isAdmin() != prevState.isAdmin) {
|
||
nextState.isAdmin = !prevState.isAdmin;
|
||
}
|
||
if (!nextProps.acs.isReader('given') != prevState.readingBlocked) {
|
||
nextState.readingBlocked = !prevState.readingBlocked;
|
||
}
|
||
if (nextProps.acs.isSharer() != prevState.isSharer) {
|
||
nextState.isSharer = !prevState.isSharer;
|
||
}
|
||
} else {
|
||
if (prevState.isWriter) {
|
||
nextState.isWriter = false;
|
||
}
|
||
if (prevState.isReader) {
|
||
nextState.isReader = false;
|
||
}
|
||
if (prevState.isAdmin) {
|
||
nextState.isAdmin = false;
|
||
}
|
||
if (!prevState.readingBlocked) {
|
||
nextState.readingBlocked = true;
|
||
}
|
||
if (prevState.isSharer) {
|
||
nextState.isSharer = false;
|
||
}
|
||
}
|
||
if (isUnconfirmed(nextProps.acs) == !prevState.unconformed) {
|
||
nextState.unconfirmed = !prevState.unconformed;
|
||
}
|
||
if (!nextProps.connected && prevState.onlineSubs && prevState.onlineSubs.length > 0) {
|
||
nextState.onlineSubs = [];
|
||
}
|
||
return nextState;
|
||
}
|
||
subscribe(topic) {
|
||
if (!this.props.ready) {
|
||
return;
|
||
}
|
||
const newTopic = this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic;
|
||
let getQuery = topic.startMetaQuery().withLaterDesc().withLaterSub().withAux();
|
||
if (this.state.isReader || newTopic) {
|
||
getQuery = getQuery.withData(undefined, undefined, _config_js__WEBPACK_IMPORTED_MODULE_15__.MESSAGES_PAGE);
|
||
if (this.state.isReader) {
|
||
getQuery = getQuery.withLaterDel();
|
||
}
|
||
this.setState({
|
||
fetchingMessages: true
|
||
});
|
||
}
|
||
const setQuery = newTopic ? this.props.newTopicParams : undefined;
|
||
const doSubscribe = () => topic.subscribe(getQuery.build(), setQuery).then(ctrl => {
|
||
if (ctrl.code == 303) {
|
||
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].setUrlTopic('', ctrl.params.topic));
|
||
return;
|
||
}
|
||
if (this.state.topic != ctrl.topic) {
|
||
this.setState({
|
||
topic: ctrl.topic
|
||
});
|
||
}
|
||
if (this.state.deleted) {
|
||
this.setState({
|
||
deleted: false
|
||
});
|
||
}
|
||
this.props.onNewTopicCreated(this.props.topic, ctrl.topic);
|
||
let discard = [];
|
||
topic.queuedMessages(pub => {
|
||
if (pub._sending) {
|
||
return;
|
||
}
|
||
if (pub._fatal || pub.head && pub.head.webrtc) {
|
||
discard.push(pub.seq);
|
||
return;
|
||
}
|
||
if (topic.isSubscribed()) {
|
||
this.retrySend(pub);
|
||
}
|
||
});
|
||
if (discard.length > 0) {
|
||
topic.delMessagesList(discard, true);
|
||
}
|
||
}).catch(err => {
|
||
console.error("Failed subscription to", this.state.topic, err);
|
||
this.props.onError(err.message, 'err');
|
||
const blankState = MessagesView.getDerivedStateFromProps({}, {});
|
||
blankState.title = this.props.intl.formatMessage(messages.not_found);
|
||
this.setState(blankState);
|
||
});
|
||
if (topic.isSubscribed()) {
|
||
topic.leave(false).catch(_ => {}).then(doSubscribe);
|
||
return;
|
||
}
|
||
doSubscribe();
|
||
}
|
||
leave(oldTopicName) {
|
||
if (!oldTopicName || !this.props.tinode.isTopicCached(oldTopicName)) {
|
||
return;
|
||
}
|
||
const oldTopic = this.props.tinode.getTopic(oldTopicName);
|
||
if (oldTopic && oldTopic.isSubscribed()) {
|
||
oldTopic.leave(false).catch(_ => {}).finally(_ => {
|
||
this.setState({
|
||
fetchingMessages: false
|
||
});
|
||
oldTopic.onData = undefined;
|
||
oldTopic.onAllMessagesReceived = undefined;
|
||
oldTopic.onInfo = undefined;
|
||
oldTopic.onMetaDesc = undefined;
|
||
oldTopic.onSubsUpdated = undefined;
|
||
oldTopic.onPres = undefined;
|
||
oldTopic.onAuxUpdated = undefined;
|
||
});
|
||
}
|
||
}
|
||
handleScrollReference(node) {
|
||
if (node) {
|
||
node.addEventListener('scroll', this.handleScrollEvent);
|
||
this.messagesScroller = node;
|
||
this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition - this.messagesScroller.offsetHeight;
|
||
}
|
||
}
|
||
handleScrollEvent(event) {
|
||
const pos = event.target.scrollHeight - event.target.scrollTop - event.target.offsetHeight;
|
||
this.setState({
|
||
scrollPosition: pos,
|
||
showGoToLastButton: pos > SHOW_GO_TO_LAST_DIST && pos < this.state.scrollPosition
|
||
});
|
||
if (this.state.fetchingMessages || this.processingScrollEvent) {
|
||
return;
|
||
}
|
||
if (event.target.scrollTop <= FETCH_PAGE_TRIGGER) {
|
||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||
if (topic && topic.isSubscribed()) {
|
||
this.processingScrollEvent = true;
|
||
const {
|
||
min,
|
||
max
|
||
} = this.getVisibleMessageRange(event.target.getBoundingClientRect());
|
||
const gaps = topic.msgHasMoreMessages(min, max, false);
|
||
if (gaps.length > 0) {
|
||
this.setState({
|
||
fetchingMessages: true
|
||
}, _ => {
|
||
topic.getMessagesPage(_config_js__WEBPACK_IMPORTED_MODULE_15__.MESSAGES_PAGE, gaps, min, max).catch(err => this.props.onError(err.message, 'err')).finally(_ => this.setState({
|
||
fetchingMessages: false
|
||
}));
|
||
});
|
||
}
|
||
}
|
||
}
|
||
this.processingScrollEvent = false;
|
||
}
|
||
mountDnDEvents(dnd) {
|
||
if (dnd) {
|
||
dnd.addEventListener('dragstart', this.handleDragStart);
|
||
dnd.addEventListener('dragenter', this.handleDragIn);
|
||
dnd.addEventListener('dragleave', this.handleDragOut);
|
||
dnd.addEventListener('dragover', this.handleDrag);
|
||
dnd.addEventListener('drop', this.handleDrop);
|
||
this.dndRef = dnd;
|
||
}
|
||
}
|
||
goToLatestMessage() {
|
||
this.setState({
|
||
scrollPosition: 0
|
||
});
|
||
if (this.messagesScroller) {
|
||
this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.messagesScroller.offsetHeight;
|
||
}
|
||
}
|
||
handleDescChange(desc) {
|
||
if (tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.isSelfTopicName(this.props.topic)) {
|
||
this.setState({
|
||
title: this.props.intl.formatMessage(messages.self_topic_name),
|
||
avatar: true
|
||
});
|
||
} else if (desc.public) {
|
||
this.setState({
|
||
title: (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_18__.topicDisplayName)(this.props.topic, desc.public, ''),
|
||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.makeImageUrl)(desc.public.photo)
|
||
});
|
||
} else {
|
||
this.setState({
|
||
title: '',
|
||
avatar: null
|
||
});
|
||
}
|
||
if (desc.acs) {
|
||
this.setState({
|
||
isWriter: desc.acs.isWriter(),
|
||
isReader: desc.acs.isReader(),
|
||
isAdmin: desc.acs.isAdmin(),
|
||
readingBlocked: !desc.acs.isReader('given'),
|
||
unconfirmed: isUnconfirmed(desc.acs)
|
||
});
|
||
}
|
||
}
|
||
postReadNotification(seq) {
|
||
if (!this.props.applicationVisible || !this.state.topic) {
|
||
return;
|
||
}
|
||
if (!this.readNotificationTimer) {
|
||
this.readNotificationTimer = setInterval(_ => {
|
||
if (this.readNotificationQueue.length == 0) {
|
||
clearInterval(this.readNotificationTimer);
|
||
this.readNotificationTimer = null;
|
||
return;
|
||
}
|
||
let seq = -1;
|
||
while (this.readNotificationQueue.length > 0) {
|
||
const n = this.readNotificationQueue[0];
|
||
if (n.topicName != this.state.topic) {
|
||
this.readNotificationQueue.shift();
|
||
continue;
|
||
}
|
||
const now = new Date();
|
||
if (n.sendAt <= now) {
|
||
this.readNotificationQueue.shift();
|
||
if (n.seq == 0 || tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.isServerAssignedSeq(n.seq)) {
|
||
seq = Math.max(seq, n.seq);
|
||
}
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
if (seq >= 0) {
|
||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||
if (topic) {
|
||
try {
|
||
topic.noteRead(seq);
|
||
} catch (err) {
|
||
console.error("Failed to send read notification", err);
|
||
}
|
||
}
|
||
}
|
||
}, NOTIFICATION_EXEC_INTERVAL);
|
||
}
|
||
const now = new Date();
|
||
this.readNotificationQueue.push({
|
||
topicName: this.state.topic,
|
||
seq: seq,
|
||
sendAt: now.setMilliseconds(now.getMilliseconds() + _config_js__WEBPACK_IMPORTED_MODULE_15__.READ_DELAY)
|
||
});
|
||
}
|
||
clearNotificationQueue() {
|
||
this.readNotificationQueue = [];
|
||
if (this.readNotificationTimer) {
|
||
clearInterval(this.readNotificationTimer);
|
||
this.readNotificationTimer = null;
|
||
}
|
||
}
|
||
handleSubsUpdated() {
|
||
if (this.state.topic) {
|
||
const subs = [];
|
||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||
topic.subscribers(sub => {
|
||
if (sub.online && sub.user != this.props.myUserId) {
|
||
subs.push(sub);
|
||
}
|
||
});
|
||
const newState = {
|
||
onlineSubs: subs,
|
||
subsVersion: this.state.subsVersion + 1
|
||
};
|
||
const peer = topic.p2pPeerDesc();
|
||
if (peer) {
|
||
Object.assign(newState, {
|
||
peerMessagingDisabled: isPeerRestricted(peer.acs)
|
||
});
|
||
} else if (this.state.peerMessagingDisabled) {
|
||
Object.assign(newState, {
|
||
peerMessagingDisabled: false
|
||
});
|
||
}
|
||
this.setState(newState);
|
||
}
|
||
}
|
||
handleMessageUpdate(msg) {
|
||
if (!this.state.topic) {
|
||
return;
|
||
}
|
||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||
if (!msg) {
|
||
this.setState({
|
||
latestClearId: topic.maxClearId()
|
||
});
|
||
return;
|
||
}
|
||
clearTimeout(this.keyPressTimer);
|
||
this.setState({
|
||
maxSeqId: topic.maxMsgSeq(),
|
||
minSeqId: topic.minMsgSeq(),
|
||
typingIndicator: false
|
||
}, _ => {
|
||
if (topic.isNewMessage(msg.seq)) {
|
||
if (this.state.scrollPosition > SHOW_GO_TO_LAST_DIST) {
|
||
this.setState({
|
||
showGoToLastButton: true
|
||
});
|
||
} else {
|
||
this.goToLatestMessage();
|
||
}
|
||
} else {
|
||
if (this.messagesScroller) {
|
||
this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition - this.messagesScroller.offsetHeight;
|
||
}
|
||
}
|
||
});
|
||
const status = topic.msgStatus(msg, true);
|
||
if (status >= tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.MESSAGE_STATUS_SENT && msg.from != this.props.myUserId) {
|
||
this.postReadNotification(msg.seq);
|
||
}
|
||
}
|
||
handleAllMessagesReceived(count) {
|
||
this.setState({
|
||
fetchingMessages: false
|
||
});
|
||
if (!count) {
|
||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||
if (topic) {
|
||
this.postReadNotification(topic.seq);
|
||
}
|
||
return;
|
||
}
|
||
if (!this.state.pinsLoaded) {
|
||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||
this.setState({
|
||
pinsLoaded: true
|
||
}, _ => {
|
||
topic.getPinnedMessages();
|
||
});
|
||
} else {
|
||
this.setState({
|
||
pinsLoaded: false
|
||
});
|
||
}
|
||
}
|
||
handleAuxUpdate(aux) {
|
||
const pins = (aux['pins'] || []).slice();
|
||
let selectedPin = this.state.selectedPin;
|
||
if (pins.length > this.state.pins.length) {
|
||
selectedPin = 0;
|
||
} else if (selectedPin >= pins.length) {
|
||
selectedPin = Math.max(0, pins.length - 1);
|
||
}
|
||
this.setState({
|
||
pins: pins,
|
||
selectedPin: selectedPin
|
||
});
|
||
}
|
||
handleInfoReceipt(info) {
|
||
switch (info.what) {
|
||
case 'kp':
|
||
{
|
||
clearTimeout(this.keyPressTimer);
|
||
this.keyPressTimer = setTimeout(_ => this.setState({
|
||
typingIndicator: false
|
||
}), _config_js__WEBPACK_IMPORTED_MODULE_15__.KEYPRESS_DELAY + 1000);
|
||
if (!this.state.typingIndicator) {
|
||
this.setState({
|
||
typingIndicator: true
|
||
});
|
||
}
|
||
break;
|
||
}
|
||
case 'read':
|
||
case 'recv':
|
||
this.forceUpdate();
|
||
break;
|
||
default:
|
||
console.info("Other change in topic: ", info.what);
|
||
}
|
||
}
|
||
handleExpandMedia(content) {
|
||
if (!content) {
|
||
return;
|
||
}
|
||
if (content.video) {
|
||
this.setState({
|
||
videoPostview: content
|
||
});
|
||
} else {
|
||
this.setState({
|
||
imagePostview: content
|
||
});
|
||
}
|
||
}
|
||
handleClosePreview() {
|
||
if (this.state.imagePreview && this.state.imagePreview.url) {
|
||
URL.revokeObjectURL(this.state.imagePreview.url);
|
||
}
|
||
if (this.state.videoPreview && this.state.videoPreview.url) {
|
||
URL.revokeObjectURL(this.state.videoPreview.url);
|
||
}
|
||
this.setState({
|
||
imagePostview: null,
|
||
imagePreview: null,
|
||
docPreview: null,
|
||
videoPreview: null,
|
||
videoPostview: null
|
||
});
|
||
this.props.onCancelForwardMessage();
|
||
}
|
||
handleFormResponse(action, text, data) {
|
||
if (action == 'pub') {
|
||
this.sendMessage(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.attachJSON(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.parse(text), data));
|
||
} else if (action == 'url') {
|
||
const url = new URL(data.ref);
|
||
const params = url.searchParams;
|
||
for (let key in data.resp) {
|
||
if (data.resp.hasOwnProperty(key)) {
|
||
params.set(key, data.resp[key]);
|
||
}
|
||
}
|
||
['name', 'seq'].forEach(key => {
|
||
if (data[key]) {
|
||
params.set(key, data[key]);
|
||
}
|
||
});
|
||
params.set('uid', this.props.myUserId);
|
||
params.set('topic', this.state.topic);
|
||
url.search = params;
|
||
window.open(url, '_blank');
|
||
} else {
|
||
console.info("Unknown action in form", action);
|
||
}
|
||
}
|
||
handleContextClick(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
this.props.showContextMenu({
|
||
topicName: this.state.topic,
|
||
y: e.pageY,
|
||
x: e.pageX
|
||
});
|
||
}
|
||
handleShowMessageContextMenu(params, messageSpecificMenuItems) {
|
||
if (params.userFrom == 'chan') {
|
||
params.userFrom = this.state.topic;
|
||
params.userName = this.state.title;
|
||
}
|
||
params.topicName = this.state.topic;
|
||
let menuItems = messageSpecificMenuItems || [];
|
||
if (this.state.reply && this.state.reply.editing) {
|
||
menuItems = menuItems.filter(item => item !== 'menu_item_reply');
|
||
}
|
||
const topic = this.props.tinode.getTopic(params.topicName);
|
||
if (topic) {
|
||
const editWindow = this.state.messagePolicy ? this.state.messagePolicy.edit_window_seconds : 0;
|
||
if (menuItems.includes('menu_item_edit') && !(editWindow > 0 && params.timestamp && params.timestamp.getTime() > new Date().getTime() - editWindow * 1000)) {
|
||
menuItems = menuItems.filter(item => item !== 'menu_item_edit');
|
||
}
|
||
menuItems.push('menu_item_copy', 'menu_item_favorite');
|
||
const recallWindow = this.state.messagePolicy ? this.state.messagePolicy.recall_window_seconds : 0;
|
||
const canRecallAsAdmin = topic.isGroupType() && topic.getAccessMode() && topic.getAccessMode().isAdmin();
|
||
if ((params.userFrom == this.props.myUserId || canRecallAsAdmin) && params.seq > 0 && params.seq < LOCAL_SEQID && recallWindow > 0 && params.timestamp && params.timestamp.getTime() > new Date().getTime() - recallWindow * 1000) {
|
||
menuItems.push('menu_item_recall');
|
||
}
|
||
if (topic.isSelfType()) {
|
||
menuItems.push('message_delete_generic');
|
||
} else {
|
||
if (!topic.isChannelType()) {
|
||
menuItems.push('message_delete');
|
||
}
|
||
const acs = topic.getAccessMode();
|
||
if (acs && acs.isDeleter()) {
|
||
let canDelete = acs.isOwner();
|
||
if (!canDelete) {
|
||
const maxDelAge = this.props.tinode.getServerParam(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.MSG_DELETE_AGE, 0) | 0;
|
||
canDelete = maxDelAge == 0 || (maxDelAge > 0 && params.timestamp ? params.timestamp.getTime() > new Date().getTime() - maxDelAge * 1000 : false);
|
||
}
|
||
if (canDelete) {
|
||
menuItems.push('message_delete_hard');
|
||
}
|
||
}
|
||
}
|
||
}
|
||
menuItems.push('menu_item_select');
|
||
this.props.showContextMenu(params, menuItems);
|
||
}
|
||
handleNewChatAcceptance(action) {
|
||
this.props.onNewChat(this.state.topic, action);
|
||
}
|
||
handleEnablePeer(e) {
|
||
e.preventDefault();
|
||
this.props.onChangePermissions(this.state.topic, _config_js__WEBPACK_IMPORTED_MODULE_15__.DEFAULT_P2P_ACCESS_MODE, this.state.topic);
|
||
}
|
||
loadRelationshipContext(topic) {
|
||
if (!topic || !topic.isP2PType()) {
|
||
return;
|
||
}
|
||
const topicName = topic.name;
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.getConversationContext)(topicName).then(data => {
|
||
if (this.state.topic == topicName) {
|
||
this.setState({
|
||
relationshipContext: data
|
||
});
|
||
}
|
||
}).catch(_ => {});
|
||
}
|
||
loadGroupContext(topic) {
|
||
if (!topic || !topic.isGroupType() || topic.isChannelType()) {
|
||
return;
|
||
}
|
||
const topicName = topic.name;
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.getGroupMembers)(topicName).then(data => {
|
||
if (this.state.topic != topicName) {
|
||
return;
|
||
}
|
||
const roles = {};
|
||
(data.items || []).forEach(m => {
|
||
roles[m.user_id] = m.role;
|
||
});
|
||
this.setState({
|
||
groupRoles: roles,
|
||
myGroupRole: roles[this.props.myUserId] || null
|
||
});
|
||
if (roles[this.props.myUserId] == 'owner' || roles[this.props.myUserId] == 'admin') {
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.getGroupJoinRequests)(topicName, 'pending').then(reqData => {
|
||
if (this.state.topic == topicName) {
|
||
this.setState({
|
||
pendingJoinRequests: (reqData.items || []).length
|
||
});
|
||
}
|
||
}).catch(_ => {});
|
||
}
|
||
}).catch(_ => {});
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.getLatestGroupAnnouncement)(topicName).then(data => {
|
||
if (this.state.topic == topicName) {
|
||
this.setState({
|
||
groupAnnouncement: data && data.content ? data : null
|
||
});
|
||
}
|
||
}).catch(_ => {});
|
||
}
|
||
mentionSearch(query) {
|
||
const topic = this.props.tinode ? this.props.tinode.getTopic(this.state.topic) : null;
|
||
if (!topic || !topic.isGroupType()) {
|
||
return Promise.resolve([]);
|
||
}
|
||
return (0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.getGroupMembers)(this.state.topic, {
|
||
mentionable: true,
|
||
query
|
||
}).then(data => {
|
||
const items = data.items || [];
|
||
const canMentionAll = !!(data.permissions && data.permissions.can_mention_all);
|
||
if (canMentionAll && '所有人'.includes((query || '').trim())) {
|
||
return [{
|
||
user_id: MENTION_ALL_UID,
|
||
display_name: '所有人'
|
||
}, ...items];
|
||
}
|
||
return items;
|
||
});
|
||
}
|
||
mentionSelected(uid, name) {
|
||
if (uid === MENTION_ALL_UID) {
|
||
this.mentionAllPending = true;
|
||
return;
|
||
}
|
||
this.mentionedUsers = (this.mentionedUsers || []).filter(m => m.name !== name);
|
||
this.mentionedUsers.push({
|
||
uid,
|
||
name
|
||
});
|
||
}
|
||
loadDraft(topic) {
|
||
if (!topic) {
|
||
return;
|
||
}
|
||
const topicName = topic.name;
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.getTopicDraft)(topicName).then(data => {
|
||
if (this.state.topic != topicName) {
|
||
return;
|
||
}
|
||
this.setState({
|
||
draftText: data.content || '',
|
||
draftVersion: data.version || 0
|
||
});
|
||
}).catch(_ => {});
|
||
}
|
||
handleComposerChange(text) {
|
||
if (this.state.reply && this.state.reply.editing) {
|
||
return;
|
||
}
|
||
if (this.props.forwardMessage) {
|
||
return;
|
||
}
|
||
const topicName = this.state.topic;
|
||
if (!topicName) {
|
||
return;
|
||
}
|
||
const version = this.state.draftVersion || 0;
|
||
this.draftSaveTimers = this.draftSaveTimers || {};
|
||
if (this.draftSaveTimers[topicName]) {
|
||
window.clearTimeout(this.draftSaveTimers[topicName]);
|
||
}
|
||
this.draftSaveTimers[topicName] = window.setTimeout(() => {
|
||
delete this.draftSaveTimers[topicName];
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.updateTopicDraft)(topicName, text, [], version).then(d => {
|
||
if (this.state.topic == topicName) {
|
||
this.setState({
|
||
draftVersion: d.version
|
||
});
|
||
}
|
||
}).catch(_ => {});
|
||
}, 600);
|
||
}
|
||
clearDraft(topicName, knownVersion) {
|
||
if (this.draftSaveTimers && this.draftSaveTimers[topicName]) {
|
||
window.clearTimeout(this.draftSaveTimers[topicName]);
|
||
delete this.draftSaveTimers[topicName];
|
||
}
|
||
const version = knownVersion != null ? knownVersion : this.state.topic == topicName ? this.state.draftVersion || 0 : 0;
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.updateTopicDraft)(topicName, '', [], version).then(d => {
|
||
if (this.state.topic == topicName) {
|
||
this.setState({
|
||
draftText: '',
|
||
draftVersion: d.version
|
||
});
|
||
}
|
||
}).catch(_ => {});
|
||
}
|
||
handleShowAnnouncement(e) {
|
||
e.preventDefault();
|
||
let hash = _lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].addUrlParam(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].setUrlSidePanel('', 'group-announcement-detail'), 'topic', this.state.topic);
|
||
hash = _lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].addUrlParam(hash, 'from', 'chat');
|
||
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].navigateTo(hash);
|
||
}
|
||
handleShowJoinRequests(e) {
|
||
e.preventDefault();
|
||
let hash = _lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].addUrlParam(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].setUrlSidePanel('', 'group-join-requests'), 'topic', this.state.topic);
|
||
hash = _lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].addUrlParam(hash, 'from', 'chat');
|
||
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].navigateTo(hash);
|
||
}
|
||
handleSendFriendVerification(e) {
|
||
e.preventDefault();
|
||
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].addUrlParam(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].setUrlSidePanel('', 'friend-verification'), 'uid', this.state.topic));
|
||
}
|
||
handleAcceptGroupInvitation(invitationId) {
|
||
return (0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.acceptGroupInvitation)(invitationId).then(result => {
|
||
if (result && result.status == 'joined') {
|
||
this.showToast('已加入群聊');
|
||
} else if (result && result.status == 'pending_approval') {
|
||
this.showToast('申请已提交,等待管理员通过');
|
||
} else {
|
||
this.showToast('已处理该邀请');
|
||
}
|
||
return result;
|
||
}).catch(err => {
|
||
this.showToast(err.message || '加入群聊失败,请重试');
|
||
throw err;
|
||
});
|
||
}
|
||
showToast(text) {
|
||
this.setState({
|
||
toast: text
|
||
});
|
||
window.clearTimeout(this.toastTimer);
|
||
this.toastTimer = window.setTimeout(() => this.setState({
|
||
toast: null
|
||
}), 3500);
|
||
}
|
||
handleBatchDelete() {
|
||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||
if (!topic || !this.props.selectedSeqs.length) {
|
||
return;
|
||
}
|
||
const remaining = this.props.selectedSeqs.filter(seq => !topic.cancelSend(seq));
|
||
if (!remaining.length) {
|
||
this.props.onExitSelectMode();
|
||
return;
|
||
}
|
||
topic.delMessagesList(remaining, true).then(_ => {
|
||
this.props.onExitSelectMode();
|
||
}).catch(err => {
|
||
this.props.onError(err.message, 'err');
|
||
});
|
||
}
|
||
handleBatchFavorite() {
|
||
if (!this.props.selectedSeqs.length) {
|
||
return;
|
||
}
|
||
const topicName = this.state.topic;
|
||
const seqs = this.props.selectedSeqs.slice();
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.favoriteMessages)(seqs.map(seq => ({
|
||
topic: topicName,
|
||
seq
|
||
}))).then(() => {
|
||
return (0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.getFavorites)({
|
||
type: 'all',
|
||
limit: 100
|
||
}).then(data => {
|
||
const items = data && data.items || [];
|
||
const landed = new Set(items.filter(it => it.source && it.source.topic == topicName).map(it => String(it.source.seq)));
|
||
const missing = seqs.filter(seq => !landed.has(String(seq)));
|
||
if (missing.length === seqs.length) {
|
||
this.props.onError('收藏失败', 'err');
|
||
} else if (missing.length) {
|
||
this.showToast(`已收藏 ${seqs.length - missing.length}/${seqs.length} 条`);
|
||
} else {
|
||
this.showToast('收藏成功');
|
||
}
|
||
});
|
||
}).catch(err => {
|
||
this.props.onError(err.message, 'err');
|
||
});
|
||
}
|
||
sendKeyPress(audio) {
|
||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||
if (!topic.isSubscribed()) {
|
||
return;
|
||
}
|
||
if (audio) {
|
||
return;
|
||
}
|
||
topic.noteKeyPress();
|
||
}
|
||
applyMentionResolution(parsed) {
|
||
if (!parsed || !Array.isArray(parsed.ent) || !this.mentionedUsers || !this.mentionedUsers.length) {
|
||
return;
|
||
}
|
||
parsed.ent.forEach(e => {
|
||
if (e.tp === 'MN' && e.data && typeof e.data.val === 'string') {
|
||
const match = this.mentionedUsers.find(m => m.name === e.data.val);
|
||
if (match) {
|
||
e.data.val = match.uid;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
sendMessage(msg, uploadCompletionPromise, uploader, typeHint) {
|
||
const originalText = typeof msg === 'string' ? msg : null;
|
||
let head;
|
||
if (!msg && this.props.forwardMessage) {
|
||
msg = this.props.forwardMessage.msg;
|
||
head = this.props.forwardMessage.head;
|
||
} else if (this.state.reply) {
|
||
if (this.state.reply.editing) {
|
||
if (msg == this.state.contentToEdit) {
|
||
this.handleCancelReply();
|
||
return;
|
||
}
|
||
head = {
|
||
replace: ':' + this.state.reply.seq
|
||
};
|
||
} else if (this.state.reply.content) {
|
||
head = {
|
||
reply: '' + this.state.reply.seq
|
||
};
|
||
if (typeof msg == 'string') {
|
||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.parse(msg);
|
||
this.applyMentionResolution(msg);
|
||
}
|
||
const replyContentCopy = JSON.parse(JSON.stringify(this.state.reply.content));
|
||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.append(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.sanitizeEntities(replyContentCopy), msg);
|
||
}
|
||
} else if (typeof msg == 'string' && this.mentionedUsers && this.mentionedUsers.length) {
|
||
const parsed = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.parse(msg);
|
||
this.applyMentionResolution(parsed);
|
||
msg = parsed;
|
||
}
|
||
this.mentionedUsers = [];
|
||
if (this.mentionAllPending) {
|
||
head = {
|
||
...(head || {}),
|
||
mention_all: true
|
||
};
|
||
this.mentionAllPending = false;
|
||
}
|
||
if (typeHint) {
|
||
head = {
|
||
...(head || {}),
|
||
'x-im-type': typeHint
|
||
};
|
||
}
|
||
const topicForDraft = this.state.topic;
|
||
const draftVersionAtSend = this.state.draftVersion || 0;
|
||
const doSend = () => {
|
||
this.handleCancelReply();
|
||
const result = this.props.sendMessage(msg, uploadCompletionPromise, uploader, head);
|
||
if (originalText != null && result && typeof result.then == 'function' && topicForDraft) {
|
||
result.then(() => this.clearDraft(topicForDraft, draftVersionAtSend));
|
||
}
|
||
return result;
|
||
};
|
||
const urlMatches = originalText && originalText.match(/https?:\/\/\S+/g);
|
||
if (urlMatches) {
|
||
const RISK_RANK = {
|
||
block: 2,
|
||
warn: 1,
|
||
allow: 0
|
||
};
|
||
const urls = Array.from(new Set(urlMatches));
|
||
return Promise.all(urls.map(url => (0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.checkURL)(url, 'message_compose'))).then(results => {
|
||
if (this.state.topic !== topicForDraft) {
|
||
return false;
|
||
}
|
||
const result = results.reduce((worst, r) => (RISK_RANK[r.decision] || 0) > (RISK_RANK[worst.decision] || 0) ? r : worst);
|
||
if (result.decision === 'allow') {
|
||
return doSend();
|
||
}
|
||
return new Promise(resolve => {
|
||
if (this.state.riskDialog) this.state.riskDialog.resolve(false);
|
||
this.setState({
|
||
riskDialog: {
|
||
blocked: result.decision === 'block',
|
||
challengeId: result.risk_challenge_id,
|
||
send: doSend,
|
||
resolve
|
||
}
|
||
});
|
||
});
|
||
}).catch(_ => doSend());
|
||
}
|
||
return doSend();
|
||
}
|
||
handleRiskConfirm() {
|
||
const dialog = this.state.riskDialog;
|
||
if (!dialog) return;
|
||
this.setState({
|
||
riskDialog: null
|
||
});
|
||
const finish = () => dialog.resolve(dialog.send());
|
||
if (dialog.challengeId) {
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.confirmRiskChallenge)(dialog.challengeId, 'continue').then(finish).catch(finish);
|
||
} else {
|
||
finish();
|
||
}
|
||
}
|
||
handleRiskCancel() {
|
||
const dialog = this.state.riskDialog;
|
||
if (!dialog) return;
|
||
this.setState({
|
||
riskDialog: null
|
||
});
|
||
if (dialog.challengeId && !dialog.blocked) {
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.confirmRiskChallenge)(dialog.challengeId, 'cancel').catch(_ => {});
|
||
}
|
||
dialog.resolve(false);
|
||
}
|
||
retrySend(pub) {
|
||
this.props.sendMessage(pub.content, undefined, undefined, pub.head).then(_ => {
|
||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||
topic.delMessagesList([pub.seq], true);
|
||
});
|
||
}
|
||
sendFileAttachment(file) {
|
||
const maxInbandAttachmentSize = this.props.tinode.getServerParam('maxMessageSize', _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024 | 0;
|
||
if (tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.isFileSupported(file.type, file.name) || file.type == tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.contentType) {
|
||
if (this.sendTheCardAttachment(file, maxInbandAttachmentSize)) {
|
||
return;
|
||
}
|
||
}
|
||
const jsonMimeConverter = fileType => fileType === _config_js__WEBPACK_IMPORTED_MODULE_15__.DRAFTY_FR_MIME_TYPE_LEGACY ? 'application/octet-stream' : fileType;
|
||
if (file.size > maxInbandAttachmentSize) {
|
||
const uploader = (0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.createChatUploader)(this.props.tinode);
|
||
if (!uploader) {
|
||
this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));
|
||
return;
|
||
}
|
||
const uploadCompletionPromise = uploader.upload(file);
|
||
const msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.attachFile(null, {
|
||
mime: jsonMimeConverter(file.type),
|
||
filename: file.name,
|
||
size: file.size,
|
||
urlPromise: uploadCompletionPromise
|
||
});
|
||
this.sendMessage(msg, uploadCompletionPromise, uploader, 'file');
|
||
} else {
|
||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.fileToBase64)(file).then(b64 => this.sendMessage(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.attachFile(null, {
|
||
mime: jsonMimeConverter(b64.mime),
|
||
data: b64.bits,
|
||
filename: b64.name,
|
||
size: file.size
|
||
}), undefined, undefined, 'file')).catch(err => this.props.onError(err.message, 'err'));
|
||
}
|
||
}
|
||
sendTheCardAttachment(file, maxInbandAttachmentSize) {
|
||
if (file.size > maxInbandAttachmentSize) {
|
||
return false;
|
||
}
|
||
if (file.type == tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.contentType) {
|
||
this.sendMessage(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.appendTheCard(null, file.object));
|
||
return true;
|
||
}
|
||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.importVCard)(file).then(card => {
|
||
this.sendMessage(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.appendTheCard(null, card));
|
||
}).catch(err => {
|
||
this.props.onError(this.props.intl.formatMessage(messages.cannot_parse_vcard, {
|
||
error: err.message
|
||
}), 'err');
|
||
});
|
||
return true;
|
||
}
|
||
handleAttachFile(file) {
|
||
const maxExternAttachmentSize = this.props.tinode.getServerParam('maxFileUploadSize', _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_EXTERN_ATTACHMENT_SIZE);
|
||
if (file.size > maxExternAttachmentSize) {
|
||
this.props.onError(this.props.intl.formatMessage(messages.file_attachment_too_large, {
|
||
size: (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_21__.bytesToHumanSize)(file.size),
|
||
limit: (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_21__.bytesToHumanSize)(maxExternAttachmentSize)
|
||
}), 'err');
|
||
} else {
|
||
this.setState({
|
||
docPreview: {
|
||
file: file,
|
||
name: file.name,
|
||
size: file.size,
|
||
type: file.type
|
||
}
|
||
});
|
||
}
|
||
}
|
||
handleCallHangup(topic, seq) {
|
||
this.props.onVideoCallClosed();
|
||
this.setState({
|
||
rtcPanel: null
|
||
});
|
||
this.props.onCallHangup(topic, seq);
|
||
}
|
||
handleCallPanelToggle() {
|
||
this.setState({
|
||
minimizedCallPanel: !this.state.minimizedCallPanel
|
||
});
|
||
}
|
||
sendImageAttachment(caption, blob) {
|
||
const mime = this.state.imagePreview.mime;
|
||
const width = this.state.imagePreview.width;
|
||
const height = this.state.imagePreview.height;
|
||
const fname = this.state.imagePreview.filename;
|
||
const maxInbandAttachmentSize = this.props.tinode.getServerParam('maxMessageSize', _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024 | 0;
|
||
if (blob.size > maxInbandAttachmentSize) {
|
||
const uploader = (0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.createChatUploader)(this.props.tinode);
|
||
if (!uploader) {
|
||
this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));
|
||
return;
|
||
}
|
||
const uploadCompletionPromise = uploader.upload(blob);
|
||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.imageScaled)(blob, _config_js__WEBPACK_IMPORTED_MODULE_15__.IMAGE_PREVIEW_DIM, _config_js__WEBPACK_IMPORTED_MODULE_15__.IMAGE_PREVIEW_DIM, -1, false).then(scaled => (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.blobToBase64)(scaled.blob)).then(b64 => {
|
||
let msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.insertImage(null, 0, {
|
||
mime: mime,
|
||
_tempPreview: b64.bits,
|
||
bits: b64.bits,
|
||
width: width,
|
||
height: height,
|
||
filename: fname,
|
||
size: blob.size,
|
||
urlPromise: uploadCompletionPromise
|
||
});
|
||
if (caption) {
|
||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.appendLineBreak(msg);
|
||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.append(msg, tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.parse(caption));
|
||
}
|
||
this.sendMessage(msg, uploadCompletionPromise, uploader, 'image');
|
||
}).catch(err => this.props.onError(err, 'err'));
|
||
return;
|
||
}
|
||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.blobToBase64)(blob).then(b64 => {
|
||
let msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.insertImage(null, 0, {
|
||
mime: b64.mime,
|
||
bits: b64.bits,
|
||
width: width,
|
||
height: height,
|
||
filename: fname,
|
||
size: blob.size
|
||
});
|
||
if (caption) {
|
||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.appendLineBreak(msg);
|
||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.append(msg, tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.parse(caption));
|
||
}
|
||
this.sendMessage(msg, undefined, undefined, 'image');
|
||
});
|
||
}
|
||
sendVideoAttachment(caption, videoBlob, previewBlob, params) {
|
||
const width = params.width;
|
||
const height = params.height;
|
||
const maxInbandAttachmentSize = this.props.tinode.getServerParam('maxMessageSize', _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024 | 0;
|
||
const uploads = [];
|
||
let uploader;
|
||
if (videoBlob.size + previewBlob.size > maxInbandAttachmentSize) {
|
||
uploader = (0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.createChatUploader)(this.props.tinode);
|
||
if (!uploader) {
|
||
this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));
|
||
return;
|
||
}
|
||
uploads[0] = videoBlob.size > maxInbandAttachmentSize * 0.675 ? uploader.upload(videoBlob) : null;
|
||
uploads[1] = previewBlob.size > maxInbandAttachmentSize * 0.275 ? uploader.upload(previewBlob) : null;
|
||
}
|
||
if (uploads.length == 0) {
|
||
Promise.all([(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.blobToBase64)(videoBlob), (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.blobToBase64)(previewBlob)]).then(b64s => {
|
||
const [v64, i64] = b64s;
|
||
let msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.insertVideo(null, 0, {
|
||
mime: v64.mime,
|
||
bits: v64.bits,
|
||
preview: i64.bits,
|
||
premime: i64.mime,
|
||
width: width,
|
||
height: height,
|
||
duration: params.duration,
|
||
filename: params.name,
|
||
size: videoBlob.size
|
||
});
|
||
if (caption) {
|
||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.appendLineBreak(msg);
|
||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.append(msg, tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.parse(caption));
|
||
}
|
||
this.sendMessage(msg, undefined, undefined, 'video');
|
||
});
|
||
return;
|
||
}
|
||
const uploadCompletionPromise = Promise.all(uploads);
|
||
const b64conv = [];
|
||
b64conv[0] = uploads[0] ? null : (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.blobToBase64)(videoBlob);
|
||
b64conv[1] = uploads[1] ? null : (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.imageScaled)(previewBlob, _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_IMAGE_DIM, _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_IMAGE_DIM, -1, false).then(scaled => (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.blobToBase64)(scaled.blob));
|
||
b64conv[2] = (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.imageScaled)(previewBlob, _config_js__WEBPACK_IMPORTED_MODULE_15__.VIDEO_PREVIEW_DIM, _config_js__WEBPACK_IMPORTED_MODULE_15__.VIDEO_PREVIEW_DIM, -1, false).then(scaled => (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.blobToBase64)(scaled.blob));
|
||
Promise.all(b64conv).then(b64s => {
|
||
const [video, img, preview] = b64s;
|
||
let msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.insertVideo(null, 0, {
|
||
mime: params.mime,
|
||
bits: video ? video.bits : null,
|
||
_tempPreview: preview.bits,
|
||
preview: img ? img.bits : preview.bits,
|
||
premime: img ? img.mime : preview.mime,
|
||
width: width,
|
||
height: height,
|
||
duration: params.duration,
|
||
filename: params.name,
|
||
size: videoBlob.size,
|
||
urlPromise: uploadCompletionPromise
|
||
});
|
||
if (caption) {
|
||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.appendLineBreak(msg);
|
||
msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.append(msg, tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.parse(caption));
|
||
}
|
||
this.sendMessage(msg, uploadCompletionPromise, uploader, 'video');
|
||
}).catch(err => this.props.onError(err.message, 'err'));
|
||
}
|
||
handleAttachImageOrVideo(file) {
|
||
const maxExternAttachmentSize = this.props.tinode.getServerParam('maxFileUploadSize', _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_EXTERN_ATTACHMENT_SIZE);
|
||
if (file.type.startsWith('video/')) {
|
||
this.setState({
|
||
videoPreview: {
|
||
url: URL.createObjectURL(file),
|
||
blob: file,
|
||
filename: file.name,
|
||
size: file.size,
|
||
mime: file.type
|
||
}
|
||
});
|
||
return;
|
||
}
|
||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.imageScaled)(file, _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_IMAGE_DIM, _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_IMAGE_DIM, maxExternAttachmentSize, false).then(scaled => {
|
||
this.setState({
|
||
imagePreview: {
|
||
url: URL.createObjectURL(scaled.blob),
|
||
blob: scaled.blob,
|
||
filename: scaled.name,
|
||
width: scaled.width,
|
||
height: scaled.height,
|
||
size: scaled.blob.size,
|
||
mime: scaled.mime
|
||
}
|
||
});
|
||
}).catch(err => {
|
||
this.props.onError(err.message, 'err');
|
||
});
|
||
}
|
||
handleFileDrop(files) {
|
||
if (!files || files.length == 0) {
|
||
return;
|
||
}
|
||
const file = files[0];
|
||
if (file.type && file.type.startsWith('image/')) {
|
||
this.handleAttachImageOrVideo(file);
|
||
} else {
|
||
this.handleAttachFile(file);
|
||
}
|
||
}
|
||
sendAudioAttachment(url, preview, duration) {
|
||
fetch(url).then(result => result.blob()).then(blob => {
|
||
const maxInbandAttachmentSize = this.props.tinode.getServerParam('maxMessageSize', _config_js__WEBPACK_IMPORTED_MODULE_15__.MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024;
|
||
if (blob.size > maxInbandAttachmentSize) {
|
||
const uploader = (0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.createChatUploader)(this.props.tinode);
|
||
if (!uploader) {
|
||
this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));
|
||
return;
|
||
}
|
||
const uploadCompletionPromise = uploader.upload(blob);
|
||
const msg = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.appendAudio(null, {
|
||
mime: blob.type,
|
||
size: blob.size,
|
||
duration: duration,
|
||
preview: preview,
|
||
urlPromise: uploadCompletionPromise
|
||
});
|
||
this.sendMessage(msg, uploadCompletionPromise, uploader, 'voice');
|
||
} else {
|
||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.blobToBase64)(blob).then(b64 => {
|
||
this.sendMessage(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.appendAudio(null, {
|
||
mime: b64.mime,
|
||
bits: b64.bits,
|
||
size: blob.size,
|
||
duration: duration,
|
||
preview: preview
|
||
}), undefined, undefined, 'voice');
|
||
});
|
||
}
|
||
}).catch(err => {
|
||
this.props.onError(err.message, 'err');
|
||
});
|
||
;
|
||
}
|
||
handleCancelUpload(seq, uploader) {
|
||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||
const found = topic.findMessage(seq);
|
||
if (found) {
|
||
found._cancelled = true;
|
||
}
|
||
uploader.cancel();
|
||
}
|
||
handlePickReply(seq, content, senderId, senderName) {
|
||
if (!seq || !content) {
|
||
this.setState({
|
||
reply: null
|
||
});
|
||
return;
|
||
}
|
||
content = typeof content == 'string' ? tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.init(content) : content;
|
||
if (tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.isValid(content)) {
|
||
content = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.replyContent(content, _config_js__WEBPACK_IMPORTED_MODULE_15__.QUOTED_REPLY_LENGTH);
|
||
} else {
|
||
content = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.append(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.init('\u26A0 '), tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.wrapInto(this.props.intl.formatMessage(messages.invalid_content), 'EM'));
|
||
}
|
||
this.setState({
|
||
reply: {
|
||
content: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.quote(senderName, senderId, content),
|
||
seq: seq
|
||
}
|
||
});
|
||
this.props.onCancelForwardMessage();
|
||
}
|
||
handleEditMessage(seq, content) {
|
||
if (!seq || !content) {
|
||
this.setState({
|
||
reply: null
|
||
});
|
||
return;
|
||
}
|
||
content = typeof content == 'string' ? tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.init(content) : content;
|
||
const editable = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.toMarkdown(content);
|
||
if (tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.isValid(content)) {
|
||
content = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.replyContent(content, _config_js__WEBPACK_IMPORTED_MODULE_15__.EDIT_PREVIEW_LENGTH);
|
||
} else {
|
||
content = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.append(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.init('\u26A0 '), tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.wrapInto(this.props.intl.formatMessage(messages.invalid_content), 'EM'));
|
||
}
|
||
this.setState({
|
||
reply: {
|
||
content: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.quote(this.props.intl.formatMessage(messages.editing_message), null, content),
|
||
seq: seq,
|
||
editing: true
|
||
},
|
||
contentToEdit: editable
|
||
});
|
||
this.props.onCancelForwardMessage();
|
||
}
|
||
handleCancelReply() {
|
||
this.setState({
|
||
reply: null,
|
||
contentToEdit: null
|
||
});
|
||
this.props.onCancelForwardMessage();
|
||
}
|
||
handleQuoteClick(replyToSeq) {
|
||
const ref = this.getOrCreateMessageRef(replyToSeq);
|
||
if (ref && ref.current) {
|
||
ref.current.scrollIntoView({
|
||
block: "center",
|
||
behavior: "smooth"
|
||
});
|
||
ref.current.classList.add('flash');
|
||
setTimeout(_ => {
|
||
ref.current.classList.remove('flash');
|
||
}, 1000);
|
||
} else {
|
||
console.error("Unresolved message ref", replyToSeq);
|
||
if (this.props.onError) {
|
||
this.props.onError('该消息已被删除或撤回', 'err');
|
||
}
|
||
}
|
||
}
|
||
jumpToMessage(seq) {
|
||
const topic = this.props.tinode && this.props.topic ? this.props.tinode.getTopic(this.props.topic) : null;
|
||
if (!topic || !seq || isNaN(seq)) {
|
||
return;
|
||
}
|
||
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_20__["default"].removeUrlParam(window.location.hash, 'jumpseq'));
|
||
const jump = () => window.setTimeout(() => this.handleQuoteClick(seq), 150);
|
||
if (topic.findMessage(seq)) {
|
||
jump();
|
||
} else {
|
||
topic.getMeta(topic.startMetaQuery().withData(Math.max(1, seq - 20), seq + 21, 41).build()).then(jump).catch(err => this.props.onError(err.message, 'err'));
|
||
}
|
||
}
|
||
handleUnpinMessage(seq) {
|
||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||
topic.pinMessage(seq, false);
|
||
}
|
||
isDragEnabled() {
|
||
const editingMessage = !!(this.state.reply && this.state.reply.editing);
|
||
return this.state.isWriter && !this.state.unconfirmed && !this.props.forwardMessage && !this.state.peerMessagingDisabled && !editingMessage;
|
||
}
|
||
handleDragStart(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
e.dataTransfer.clearData();
|
||
}
|
||
handleDragIn(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
this.dragCounter++;
|
||
if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
|
||
this.setState({
|
||
dragging: true
|
||
});
|
||
}
|
||
}
|
||
handleDragOut(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
this.dragCounter--;
|
||
if (this.dragCounter <= 0) {
|
||
this.setState({
|
||
dragging: false
|
||
});
|
||
}
|
||
}
|
||
handleDrag(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
}
|
||
handleDrop(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
this.setState({
|
||
dragging: false
|
||
});
|
||
if (this.isDragEnabled() && e.dataTransfer.files && e.dataTransfer.files.length > 0) {
|
||
this.handleFileDrop(e.dataTransfer.files);
|
||
this.dragCounter = 0;
|
||
}
|
||
}
|
||
render() {
|
||
const {
|
||
formatMessage
|
||
} = this.props.intl;
|
||
const overlay = this.state.rtcPanel && (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react__WEBPACK_IMPORTED_MODULE_0__.Suspense, {
|
||
fallback: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "loading_note",
|
||
defaultMessage: "Loading...",
|
||
description: "Message shown when component is loading"
|
||
}, void 0, false)
|
||
}, void 0, false),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(CallPanel, {
|
||
topic: this.state.topic,
|
||
seq: this.props.callSeq,
|
||
callState: this.props.callState,
|
||
callAudioOnly: this.props.callAudioOnly,
|
||
tinode: this.props.tinode,
|
||
title: this.state.title,
|
||
avatar: this.state.avatar || true,
|
||
minimized: this.state.minimizedCallPanel,
|
||
onError: this.props.onError,
|
||
onHangup: this.handleCallHangup,
|
||
onToggleMinimize: this.handleCallPanelToggle,
|
||
onInvite: this.props.onCallInvite,
|
||
onSendOffer: this.props.onCallSendOffer,
|
||
onIceCandidate: this.props.onCallIceCandidate,
|
||
onSendAnswer: this.props.onCallSendAnswer
|
||
}, void 0, false)
|
||
}, void 0, false);
|
||
const riskDialog = this.state.riskDialog && (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_security_support_view_jsx__WEBPACK_IMPORTED_MODULE_13__.RiskConfirmDialog, {
|
||
blocked: this.state.riskDialog.blocked,
|
||
onConfirm: this.handleRiskConfirm,
|
||
onCancel: this.handleRiskCancel
|
||
}, void 0, false);
|
||
let component;
|
||
if (!this.state.topic) {
|
||
component = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_logo_view_jsx__WEBPACK_IMPORTED_MODULE_10__["default"], {}, void 0, false);
|
||
} else {
|
||
let component2;
|
||
if (this.state.imagePreview) {
|
||
component2 = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react__WEBPACK_IMPORTED_MODULE_0__.Suspense, {
|
||
fallback: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "loading_note",
|
||
defaultMessage: "Loading...",
|
||
description: "Message shown when component is loading"
|
||
}, void 0, false)
|
||
}, void 0, false),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(ImagePreview, {
|
||
content: this.state.imagePreview,
|
||
tinode: this.props.tinode,
|
||
reply: this.state.reply,
|
||
onCancelReply: this.handleCancelReply,
|
||
onClose: this.handleClosePreview,
|
||
onSendMessage: this.sendImageAttachment
|
||
}, void 0, false)
|
||
}, void 0, false);
|
||
} else if (this.state.videoPreview) {
|
||
component2 = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react__WEBPACK_IMPORTED_MODULE_0__.Suspense, {
|
||
fallback: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "loading_note",
|
||
defaultMessage: "Loading...",
|
||
description: "Message shown when component is loading"
|
||
}, void 0, false)
|
||
}, void 0, false),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(VideoPreview, {
|
||
content: this.state.videoPreview,
|
||
tinode: this.props.tinode,
|
||
reply: this.state.reply,
|
||
onError: this.props.onError,
|
||
onCancelReply: this.handleCancelReply,
|
||
onClose: this.handleClosePreview,
|
||
onSendMessage: this.sendVideoAttachment
|
||
}, void 0, false)
|
||
}, void 0, false);
|
||
} else if (this.state.imagePostview) {
|
||
component2 = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react__WEBPACK_IMPORTED_MODULE_0__.Suspense, {
|
||
fallback: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "loading_note",
|
||
defaultMessage: "Loading...",
|
||
description: "Message shown when component is loading"
|
||
}, void 0, false)
|
||
}, void 0, false),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(ImagePreview, {
|
||
content: this.state.imagePostview,
|
||
onClose: this.handleClosePreview
|
||
}, void 0, false)
|
||
}, void 0, false);
|
||
} else if (this.state.videoPostview) {
|
||
component2 = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react__WEBPACK_IMPORTED_MODULE_0__.Suspense, {
|
||
fallback: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "loading_note",
|
||
defaultMessage: "Loading...",
|
||
description: "Message shown when component is loading"
|
||
}, void 0, false)
|
||
}, void 0, false),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(VideoPreview, {
|
||
content: this.state.videoPostview,
|
||
tinode: this.props.tinode,
|
||
onError: this.props.onError,
|
||
onClose: this.handleClosePreview
|
||
}, void 0, false)
|
||
}, void 0, false);
|
||
} else if (this.state.docPreview) {
|
||
if (tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.isFileSupported(this.state.docPreview.type, this.state.docPreview.name) || this.state.docPreview.type == tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.contentType) {
|
||
component2 = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react__WEBPACK_IMPORTED_MODULE_0__.Suspense, {
|
||
fallback: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "loading_note",
|
||
defaultMessage: "Loading...",
|
||
description: "Message shown when component is loading"
|
||
}, void 0, false)
|
||
}, void 0, false),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(TheCardPreview, {
|
||
content: this.state.docPreview,
|
||
tinode: this.props.tinode,
|
||
reply: this.state.reply,
|
||
onCancelReply: this.handleCancelReply,
|
||
onClose: this.handleClosePreview,
|
||
onSendMessage: this.sendFileAttachment
|
||
}, void 0, false)
|
||
}, void 0, false);
|
||
} else {
|
||
component2 = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react__WEBPACK_IMPORTED_MODULE_0__.Suspense, {
|
||
fallback: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "loading_note",
|
||
defaultMessage: "Loading...",
|
||
description: "Message shown when component is loading"
|
||
}, void 0, false)
|
||
}, void 0, false),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(DocPreview, {
|
||
content: this.state.docPreview,
|
||
tinode: this.props.tinode,
|
||
reply: this.state.reply,
|
||
onCancelReply: this.handleCancelReply,
|
||
onClose: this.handleClosePreview,
|
||
onSendMessage: this.sendFileAttachment
|
||
}, void 0, false)
|
||
}, void 0, false);
|
||
}
|
||
} else {
|
||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||
const isChannel = topic.isChannelType() || topic.chan;
|
||
const groupTopic = topic.isGroupType() && !isChannel;
|
||
const icon_badges = [];
|
||
if (topic.trusted) {
|
||
if (topic.trusted.verified) {
|
||
icon_badges.push({
|
||
icon: 'verified',
|
||
color: 'badge-inv'
|
||
});
|
||
}
|
||
if (topic.trusted.staff) {
|
||
icon_badges.push({
|
||
icon: 'staff',
|
||
color: 'badge-inv'
|
||
});
|
||
}
|
||
if (topic.trusted.danger) {
|
||
icon_badges.push({
|
||
icon: 'dangerous',
|
||
color: 'badge-inv'
|
||
});
|
||
}
|
||
}
|
||
const pinnedMessages = [];
|
||
this.state.pins.forEach(seq => pinnedMessages.push(topic.latestMsgVersion(seq) || topic.findMessage(seq)));
|
||
const messageNodes = [];
|
||
let previousFrom = null;
|
||
let prevDate = null;
|
||
let chatBoxClass = groupTopic ? 'chat-box group' : 'chat-box';
|
||
topic.messages((msg, prev, next, i) => {
|
||
let nextFrom = next ? next.from || 'chan' : null;
|
||
let sequence = 'single';
|
||
let thisFrom = msg.from || 'chan';
|
||
if (thisFrom == previousFrom) {
|
||
if (thisFrom == nextFrom) {
|
||
sequence = 'middle';
|
||
} else {
|
||
sequence = 'last';
|
||
}
|
||
} else if (thisFrom == nextFrom) {
|
||
sequence = 'first';
|
||
}
|
||
previousFrom = thisFrom;
|
||
const isReply = !(thisFrom == this.props.myUserId);
|
||
const deliveryStatus = topic.msgStatus(msg, true);
|
||
let userFrom = thisFrom,
|
||
userName,
|
||
userAvatar;
|
||
const user = topic.userDesc(thisFrom);
|
||
if (user && user.public) {
|
||
userName = user.public.fn;
|
||
userAvatar = (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_17__.makeImageUrl)(user.public.photo);
|
||
}
|
||
const ref = this.getOrCreateMessageRef(msg.seq);
|
||
let replyToSeq = msg.head ? parseInt(msg.head.reply) : null;
|
||
if (!replyToSeq || isNaN(replyToSeq)) {
|
||
replyToSeq = null;
|
||
}
|
||
const systemActions = msg.content && typeof msg.content == 'object' && Array.isArray(msg.content.actions) ? msg.content.actions : null;
|
||
const isActionableSystemMessage = systemActions && systemActions.some(a => a && a.type == 'group_invitation_accept');
|
||
const isPlainSystemMessage = msg.head && (msg.head['x-im-type'] == 'system_notice' || msg.head['x-im-type'] == 'system_action') && !isActionableSystemMessage && msg.content && typeof msg.content == 'object' && typeof msg.content.text == 'string';
|
||
if (msg.hi) {
|
||
messageNodes.push((0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_meta_message_jsx__WEBPACK_IMPORTED_MODULE_11__["default"], {
|
||
deleted: true,
|
||
deletedText: this.props.intl.formatMessage(messages.message_deleted)
|
||
}, msg.seq, false));
|
||
} else if (isPlainSystemMessage) {
|
||
const thisDate = new Date(msg.ts);
|
||
if (!prevDate || prevDate.toDateString() != thisDate.toDateString()) {
|
||
messageNodes.push((0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_meta_message_jsx__WEBPACK_IMPORTED_MODULE_11__["default"], {
|
||
date: (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_21__.relativeDateFormat)(msg.ts),
|
||
locale: this.props.intl.locale
|
||
}, 'date-' + msg.seq, false));
|
||
prevDate = thisDate;
|
||
}
|
||
messageNodes.push((0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_meta_message_jsx__WEBPACK_IMPORTED_MODULE_11__["default"], {
|
||
systemText: msg.content.text
|
||
}, msg.seq, false));
|
||
} else {
|
||
const thisDate = new Date(msg.ts);
|
||
if (!prevDate || prevDate.toDateString() != thisDate.toDateString()) {
|
||
messageNodes.push((0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_meta_message_jsx__WEBPACK_IMPORTED_MODULE_11__["default"], {
|
||
date: (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_21__.relativeDateFormat)(msg.ts),
|
||
locale: this.props.intl.locale
|
||
}, 'date-' + msg.seq, false));
|
||
prevDate = thisDate;
|
||
}
|
||
messageNodes.push((0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_chat_message_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||
tinode: this.props.tinode,
|
||
topic: this.state.topic,
|
||
content: msg.content,
|
||
mimeType: msg.head && msg.head.mime,
|
||
head: msg.head,
|
||
replyToSeq: replyToSeq,
|
||
edited: msg.head && !msg.head.webrtc && msg.head.replace,
|
||
timestamp: msg.ts,
|
||
response: isReply,
|
||
seq: msg.seq,
|
||
isGroup: groupTopic,
|
||
isChan: this.state.channel,
|
||
userFrom: userFrom,
|
||
userName: userName,
|
||
userAvatar: userAvatar,
|
||
senderRole: groupTopic && this.state.groupRoles ? this.state.groupRoles[thisFrom] : null,
|
||
sequence: sequence,
|
||
received: deliveryStatus,
|
||
uploader: msg._uploader,
|
||
userIsWriter: this.state.isWriter,
|
||
userIsAdmin: this.state.isAdmin,
|
||
relationship: this.state.relationshipContext ? this.state.relationshipContext.relationship : null,
|
||
onSendFriendVerification: this.handleSendFriendVerification,
|
||
onAcceptGroupInvitation: this.handleAcceptGroupInvitation,
|
||
selectMode: this.props.selectMode,
|
||
selected: this.props.selectedSeqs && this.props.selectedSeqs.includes(msg.seq),
|
||
onToggleSelect: this.props.onToggleMessageSelect,
|
||
pinned: this.state.pins.includes(msg.seq),
|
||
viewportWidth: this.props.viewportWidth,
|
||
showContextMenu: this.handleShowMessageContextMenu,
|
||
onExpandMedia: this.handleExpandMedia,
|
||
onFormResponse: this.handleFormResponse,
|
||
onCancelUpload: this.handleCancelUpload,
|
||
pickReply: this.handlePickReply,
|
||
editMessage: this.handleEditMessage,
|
||
onQuoteClick: this.handleQuoteClick,
|
||
onAcceptCall: this.props.onAcceptCall,
|
||
onError: this.props.onError,
|
||
ref: ref
|
||
}, msg.seq, false));
|
||
}
|
||
});
|
||
let lastSeen = null;
|
||
if (isChannel) {
|
||
if (topic.subcnt) {
|
||
lastSeen = formatMessage(messages.subscriber_count, {
|
||
count: topic.subcnt
|
||
});
|
||
}
|
||
} else {
|
||
const cont = this.props.tinode.getMeTopic().getContact(this.state.topic);
|
||
if (cont) {
|
||
if (tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.isP2PTopicName(cont.topic)) {
|
||
if (cont.online) {
|
||
lastSeen = formatMessage(messages.online_now);
|
||
} else if (cont.seen) {
|
||
lastSeen = formatMessage(messages.last_seen, {
|
||
timestamp: (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_21__.shortDateFormat)(cont.seen.when, this.props.intl.locale)
|
||
});
|
||
}
|
||
} else if (tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.isGroupTopicName(cont.topic)) {
|
||
if (topic.subcnt) {
|
||
lastSeen = formatMessage(messages.member_count, {
|
||
count: topic.subcnt
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
const avatar = this.state.avatar || true;
|
||
const online = this.state.deleted || topic.isSelfType() ? null : this.props.online ? 'online' + (this.state.typingIndicator ? ' typing' : '') : 'offline';
|
||
const titleClass = 'panel-title' + (this.state.deleted ? ' deleted' : '');
|
||
const darkModeClass = this.props.colorSchema == 'dark' ? 'dark' : '';
|
||
const editingMessage = !!(this.state.reply && this.state.reply.editing);
|
||
let messagesComponent = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.Fragment, {
|
||
children: [this.state.groupAnnouncement ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("a", {
|
||
href: "#",
|
||
id: "group-announcement-banner",
|
||
onClick: this.handleShowAnnouncement,
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "campaign"
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("span", {
|
||
children: this.state.groupAnnouncement.content
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "chevron_right"
|
||
}, void 0, false)]
|
||
}, void 0, true) : null, this.state.pendingJoinRequests > 0 ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("a", {
|
||
href: "#",
|
||
id: "group-join-request-banner",
|
||
onClick: this.handleShowJoinRequests,
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "person_add_alt"
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("span", {
|
||
children: ["新邀请进群申请(", this.state.pendingJoinRequests, ")"]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "chevron_right"
|
||
}, void 0, false)]
|
||
}, void 0, true) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "messages-container",
|
||
className: darkModeClass,
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("button", {
|
||
id: "go-to-latest",
|
||
className: 'action-button' + (this.state.showGoToLastButton ? '' : ' hidden'),
|
||
onClick: this.goToLatestMessage,
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "arrow_downward"
|
||
}, void 0, false)
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "messages-panel",
|
||
ref: this.handleScrollReference,
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("ul", {
|
||
id: "scroller",
|
||
className: chatBoxClass,
|
||
children: messageNodes
|
||
}, void 0, false)
|
||
}, void 0, false), !this.state.isReader ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "write-only-background",
|
||
children: this.state.readingBlocked ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "write-only-note",
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "messages_not_readable",
|
||
defaultMessage: "no access to messages",
|
||
description: "Message shown in topic without the read access"
|
||
}, void 0, false)
|
||
}, void 0, false) : null
|
||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
className: 'toast' + (this.state.toast ? ' show' : ''),
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "check"
|
||
}, void 0, false), "\xA0", this.state.toast]
|
||
}, void 0, true)]
|
||
}, void 0, true), this.state.peerMessagingDisabled && !this.state.unconfirmed ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "peer-messaging-disabled-note",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("i", {
|
||
className: "material-icons secondary",
|
||
children: "block"
|
||
}, void 0, false), " ", (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "peers_messaging_disabled",
|
||
defaultMessage: "Peer's messaging is disabled.",
|
||
description: "Shown when the p2p peer's messaging is disabled"
|
||
}, void 0, false), " ", (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: this.handleEnablePeer,
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "enable_peers_messaging",
|
||
defaultMessage: "Enable",
|
||
description: "Call to action to enable peer's messaging"
|
||
}, void 0, false)
|
||
}, void 0, false), "."]
|
||
}, void 0, true) : null, !this.state.unconfirmed && this.state.relationshipContext && this.state.relationshipContext.relationship == 'pending_inbound' ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "relationship-status-note",
|
||
children: "以上是打招呼的消息"
|
||
}, void 0, false) : null, !this.state.unconfirmed && this.state.relationshipContext && this.state.relationshipContext.relationship == 'pending_outbound' ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "relationship-status-note",
|
||
children: "好友申请正在等待处理"
|
||
}, void 0, false) : null, !this.state.unconfirmed && this.state.relationshipContext && this.state.relationshipContext.relationship == 'none' ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "relationship-status-note",
|
||
children: ["你们还不是好友,发送消息前需先验证 ", (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: this.handleSendFriendVerification,
|
||
children: "发送朋友验证"
|
||
}, void 0, false)]
|
||
}, void 0, true) : null, this.props.selectMode ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "batch-action-bar",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "batch-action-count",
|
||
children: ["已选 ", this.props.selectedSeqs.length, " 个"]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "batch-action-buttons",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.props.selectedSeqs.length && this.props.onShowBatchForward();
|
||
},
|
||
className: this.props.selectedSeqs.length ? '' : 'disabled',
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "forward"
|
||
}, void 0, false), "转发"]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.handleBatchFavorite();
|
||
},
|
||
className: this.props.selectedSeqs.length ? '' : 'disabled',
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "star_border"
|
||
}, void 0, false), "收藏"]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.handleBatchDelete();
|
||
},
|
||
className: this.props.selectedSeqs.length ? '' : 'disabled',
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "delete_outline"
|
||
}, void 0, false), "删除"]
|
||
}, void 0, true)]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("a", {
|
||
href: "#",
|
||
id: "batch-action-cancel",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.props.onExitSelectMode();
|
||
},
|
||
children: "取消"
|
||
}, void 0, false)]
|
||
}, void 0, true) : this.state.unconfirmed ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_invitation_jsx__WEBPACK_IMPORTED_MODULE_7__["default"], {
|
||
onAction: this.handleNewChatAcceptance
|
||
}, void 0, false) : (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_send_message_jsx__WEBPACK_IMPORTED_MODULE_14__["default"], {
|
||
tinode: this.props.tinode,
|
||
topicName: this.state.topic,
|
||
noInput: !!this.props.forwardMessage,
|
||
disabled: !this.state.isWriter || this.state.deleted,
|
||
reply: this.state.reply,
|
||
initMessage: this.state.contentToEdit != null ? this.state.contentToEdit : this.state.draftText,
|
||
sendOnEnter: this.props.sendOnEnter,
|
||
onKeyPress: this.sendKeyPress,
|
||
onRecordingProgress: this.sendKeyPress,
|
||
onMessageChange: this.props.forwardMessage ? null : this.handleComposerChange,
|
||
onMentionSearch: this.props.forwardMessage || !groupTopic ? null : this.mentionSearch,
|
||
onMentionSelected: this.mentionSelected,
|
||
onSendMessage: this.sendMessage,
|
||
onAttachFile: this.props.forwardMessage || editingMessage ? null : this.handleAttachFile,
|
||
onAttachImage: this.props.forwardMessage || editingMessage ? null : this.handleAttachImageOrVideo,
|
||
onAttachAudio: this.props.forwardMessage || editingMessage ? null : this.sendAudioAttachment,
|
||
canStartCall: !this.props.forwardMessage && this.props.canStartCall,
|
||
onStartAudioCall: this.props.onStartAudioCall,
|
||
onStartVideoCall: this.props.onStartVideoCall,
|
||
onError: this.props.onError,
|
||
onQuoteClick: this.handleQuoteClick,
|
||
onCancelReply: this.handleCancelReply
|
||
}, void 0, false)]
|
||
}, void 0, true);
|
||
component2 = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.Fragment, {
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "topic-caption-panel",
|
||
className: "caption-panel",
|
||
children: [this.props.displayMobile ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("a", {
|
||
href: "#",
|
||
id: "hide-message-view",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.leave(this.state.topic);
|
||
this.props.onHideMessagesView();
|
||
},
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "arrow_back"
|
||
}, void 0, false)
|
||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
className: "avatar-box",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_8__["default"], {
|
||
authorizeURL: this.props.tinode.authorizeURL,
|
||
avatar: avatar,
|
||
topic: this.state.topic,
|
||
title: this.state.title,
|
||
deleted: this.state.deleted
|
||
}, void 0, false), !isChannel ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("span", {
|
||
className: online
|
||
}, void 0, false) : null]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "topic-title-group",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "topic-title",
|
||
className: titleClass,
|
||
children: [this.state.title || (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("i", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "unnamed_topic",
|
||
defaultMessage: "Unnamed",
|
||
description: "Title shown when the topic has no name"
|
||
}, void 0, false)
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_contact_badges_jsx__WEBPACK_IMPORTED_MODULE_4__["default"], {
|
||
badges: icon_badges
|
||
}, void 0, false)]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "topic-last-seen",
|
||
children: lastSeen
|
||
}, void 0, false)]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
style: {
|
||
marginLeft: 'auto'
|
||
}
|
||
}, void 0, false), !this.props.displayMobile && this.state.pins.length > 0 ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_pinned_messages_jsx__WEBPACK_IMPORTED_MODULE_12__["default"], {
|
||
tinode: this.props.tinode,
|
||
pins: this.state.pins,
|
||
messages: pinnedMessages,
|
||
selected: this.state.selectedPin,
|
||
isAdmin: this.state.isAdmin,
|
||
setSelected: index => this.setState({
|
||
selectedPin: index
|
||
}),
|
||
onSelected: this.handleQuoteClick,
|
||
onCancel: this.handleUnpinMessage
|
||
}, void 0, false) : null, groupTopic ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_group_subs_jsx__WEBPACK_IMPORTED_MODULE_6__["default"], {
|
||
tinode: this.props.tinode,
|
||
subscribers: this.state.onlineSubs
|
||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: this.handleContextClick,
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "more_vert"
|
||
}, void 0, false)
|
||
}, void 0, false)
|
||
}, void 0, false)]
|
||
}, void 0, true), this.props.displayMobile ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.Fragment, {
|
||
children: [this.state.pins.length > 0 ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_pinned_messages_jsx__WEBPACK_IMPORTED_MODULE_12__["default"], {
|
||
tinode: this.props.tinode,
|
||
pins: this.state.pins,
|
||
messages: pinnedMessages,
|
||
selected: this.state.selectedPin,
|
||
isAdmin: this.state.isAdmin,
|
||
setSelected: index => this.setState({
|
||
selectedPin: index
|
||
}),
|
||
onSelected: this.handleQuoteClick,
|
||
onCancel: this.handleUnpinMessage
|
||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_error_panel_jsx__WEBPACK_IMPORTED_MODULE_5__["default"], {
|
||
level: this.props.errorLevel,
|
||
text: this.props.errorText,
|
||
onClearError: this.props.onError
|
||
}, void 0, false)]
|
||
}, void 0, true) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_load_spinner_jsx__WEBPACK_IMPORTED_MODULE_9__["default"], {
|
||
show: this.state.fetchingMessages
|
||
}, void 0, false), messagesComponent, this.state.dragging && this.isDragEnabled() ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
className: "drag-n-drop",
|
||
children: formatMessage(messages.drag_file)
|
||
}, void 0, false) : null]
|
||
}, void 0, true);
|
||
}
|
||
component = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
|
||
id: "topic-view",
|
||
ref: this.mountDnDEvents,
|
||
children: [component2, overlay, riskDialog]
|
||
}, void 0, true);
|
||
}
|
||
return component;
|
||
}
|
||
}
|
||
;
|
||
/* harmony default export */ __webpack_exports__["default"] = ((0,react_intl__WEBPACK_IMPORTED_MODULE_1__.injectIntl)(MessagesView));
|
||
|
||
/***/ }),
|
||
|
||
/***/ "./src/widgets/attachment.jsx":
|
||
/*!************************************!*\
|
||
!*** ./src/widgets/attachment.jsx ***!
|
||
\************************************/
|
||
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||
|
||
__webpack_require__.r(__webpack_exports__);
|
||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||
/* harmony export */ "default": function() { return /* binding */ Attachment; }
|
||
/* harmony export */ });
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ "react-intl");
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
|
||
/* harmony import */ var _file_progress_jsx__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./file-progress.jsx */ "./src/widgets/file-progress.jsx");
|
||
/* harmony import */ var _lib_im_api_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../lib/im-api.js */ "./src/lib/im-api.js");
|
||
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
|
||
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
|
||
/* harmony import */ var react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! react/jsx-dev-runtime */ "./node_modules/react/jsx-dev-runtime.js");
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
const ATTACHMENT_TYPE_META = {
|
||
image: ['image', '#3ba55c'],
|
||
video: ['videocam', '#3b82c4'],
|
||
audio: ['audiotrack', '#8b5cf6'],
|
||
document: ['description', '#e05d44'],
|
||
file: ['insert_drive_file', '#8a8f98']
|
||
};
|
||
function attachmentTypeFromMime(mime) {
|
||
const m = (mime || '').toLowerCase();
|
||
if (m.startsWith('image/')) return 'image';
|
||
if (m.startsWith('video/')) return 'video';
|
||
if (m.startsWith('audio/')) return 'audio';
|
||
if (m === 'application/pdf' || m === 'text/plain' || m.includes('msword') || m.includes('wordprocessingml') || m.includes('spreadsheetml') || m.includes('presentationml') || m === 'application/vnd.ms-excel' || m === 'application/vnd.ms-powerpoint') {
|
||
return 'document';
|
||
}
|
||
return 'file';
|
||
}
|
||
class Attachment extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
|
||
constructor(props) {
|
||
super(props);
|
||
this.state = {
|
||
downloader: null,
|
||
progress: 0
|
||
};
|
||
this.downloadFile = this.downloadFile.bind(this);
|
||
this.handleCancel = this.handleCancel.bind(this);
|
||
}
|
||
downloadFile(url, filename, mimetype) {
|
||
if (!url) {
|
||
this.props.onError("Invalid download URL '" + url + "'");
|
||
return;
|
||
}
|
||
const downloader = (0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_3__.createFileDownloader)(this.props.tinode);
|
||
this.setState({
|
||
downloader: downloader
|
||
});
|
||
downloader.download(url, filename, mimetype, loaded => this.setState({
|
||
progress: loaded / this.props.size
|
||
}), err => this.props.onError(err, 'err')).then(_ => this.setState({
|
||
downloader: null,
|
||
progress: 0
|
||
})).catch(err => {
|
||
if (err) {
|
||
this.props.onError("Error downloading file: " + err.message, 'err');
|
||
}
|
||
this.setState({
|
||
downloader: null,
|
||
progress: 0
|
||
});
|
||
});
|
||
}
|
||
handleCancel() {
|
||
if (this.props.uploading) {
|
||
this.props.onCancelUpload();
|
||
} else if (this.state.downloader) {
|
||
this.state.downloader.cancel();
|
||
}
|
||
}
|
||
render() {
|
||
let filename = this.props.filename || 'file_attachment';
|
||
if (filename.length > 36) {
|
||
filename = filename.substr(0, 16) + '...' + filename.substr(-16);
|
||
}
|
||
let size = this.props.size > 0 ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("span", {
|
||
className: "small gray",
|
||
children: ["(", (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_4__.bytesToHumanSize)(this.props.size), ")"]
|
||
}, void 0, true) : null;
|
||
let url, helperFunc;
|
||
if (!this.props.uploading && !this.state.downloader && (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_5__.isUrlRelative)(this.props.downloadUrl)) {
|
||
url = '#';
|
||
helperFunc = e => {
|
||
e.preventDefault();
|
||
this.downloadFile(this.props.downloadUrl, this.props.filename, this.props.mimetype);
|
||
};
|
||
} else {
|
||
url = (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_5__.sanitizeUrl)(this.props.downloadUrl);
|
||
helperFunc = null;
|
||
}
|
||
const downloadWidget = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.Fragment, {
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "file_download"
|
||
}, void 0, false), " ", (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "save_attachment",
|
||
defaultMessage: "save",
|
||
description: "Call to save an attachment"
|
||
}, void 0, false)]
|
||
}, void 0, true);
|
||
const [icon, color] = ATTACHMENT_TYPE_META[attachmentTypeFromMime(this.props.mimetype)];
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||
className: "attachment",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("i", {
|
||
className: "material-icons big",
|
||
style: {
|
||
color
|
||
},
|
||
children: icon
|
||
}, void 0, false)
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||
className: "flex-column",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||
children: [filename, " ", size]
|
||
}, void 0, true), this.props.uploading || this.state.downloader ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(_file_progress_jsx__WEBPACK_IMPORTED_MODULE_2__["default"], {
|
||
progress: this.props.uploading ? this.props.progress : this.state.progress,
|
||
onCancel: this.handleCancel
|
||
}, void 0, false) : (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||
children: url ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("a", {
|
||
href: url,
|
||
download: this.props.filename,
|
||
onClick: helperFunc,
|
||
children: downloadWidget
|
||
}, void 0, false) : (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("span", {
|
||
className: "light-gray",
|
||
children: downloadWidget
|
||
}, void 0, false)
|
||
}, void 0, false)]
|
||
}, void 0, true)]
|
||
}, void 0, true);
|
||
}
|
||
}
|
||
;
|
||
|
||
/***/ }),
|
||
|
||
/***/ "./src/widgets/chat-message.jsx":
|
||
/*!**************************************!*\
|
||
!*** ./src/widgets/chat-message.jsx ***!
|
||
\**************************************/
|
||
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||
|
||
__webpack_require__.r(__webpack_exports__);
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ "react-intl");
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
|
||
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! tinode-sdk */ "tinode-sdk");
|
||
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__);
|
||
/* harmony import */ var _attachment_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./attachment.jsx */ "./src/widgets/attachment.jsx");
|
||
/* harmony import */ var _forward_bundle_jsx__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./forward-bundle.jsx */ "./src/widgets/forward-bundle.jsx");
|
||
/* harmony import */ var _letter_tile_jsx__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./letter-tile.jsx */ "./src/widgets/letter-tile.jsx");
|
||
/* harmony import */ var _received_marker_jsx__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./received-marker.jsx */ "./src/widgets/received-marker.jsx");
|
||
/* harmony import */ var _lib_formatters_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../lib/formatters.js */ "./src/lib/formatters.js");
|
||
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
|
||
/* harmony import */ var _lib_navigation_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../lib/navigation.js */ "./src/lib/navigation.js");
|
||
/* harmony import */ var react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! react/jsx-dev-runtime */ "./node_modules/react/jsx-dev-runtime.js");
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
const VOICE_PLAYED_KEY_PREFIX = 'im-voice-played:';
|
||
function isVoicePlayed(myUid, topic, seq) {
|
||
try {
|
||
const list = JSON.parse(localStorage.getItem(VOICE_PLAYED_KEY_PREFIX + myUid + ':' + topic) || '[]');
|
||
return Array.isArray(list) && list.includes(seq);
|
||
} catch (_) {
|
||
return false;
|
||
}
|
||
}
|
||
function markVoicePlayed(myUid, topic, seq) {
|
||
try {
|
||
const key = VOICE_PLAYED_KEY_PREFIX + myUid + ':' + topic;
|
||
const list = JSON.parse(localStorage.getItem(key) || '[]');
|
||
const set = Array.isArray(list) ? list : [];
|
||
if (!set.includes(seq)) {
|
||
localStorage.setItem(key, JSON.stringify(set.concat(seq).slice(-500)));
|
||
}
|
||
} catch (_) {}
|
||
}
|
||
class BaseChatMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||
constructor(props) {
|
||
super(props);
|
||
this.state = {
|
||
progress: 0,
|
||
voicePlayed: isVoicePlayed(props.tinode.getCurrentUserID(), props.topic, props.seq),
|
||
invitationBusy: false,
|
||
invitationResult: null,
|
||
invitationError: null
|
||
};
|
||
if (props.uploader) {
|
||
props.uploader.onProgress = this.handleProgress.bind(this);
|
||
}
|
||
this.handleExpandImage = this.handleExpandImage.bind(this);
|
||
this.handlePlayVideo = this.handlePlayVideo.bind(this);
|
||
this.handleFormButtonClick = this.handleFormButtonClick.bind(this);
|
||
this.handleQuoteClick = this.handleQuoteClick.bind(this);
|
||
this.handleContextClick = this.handleContextClick.bind(this);
|
||
this.handleCancelUpload = this.handleCancelUpload.bind(this);
|
||
this.handleDraftyClick = this.handleDraftyClick.bind(this);
|
||
this.handleVoicePlayed = this.handleVoicePlayed.bind(this);
|
||
this.handleAcceptGroupInvitation = this.handleAcceptGroupInvitation.bind(this);
|
||
this.formatterContext = {
|
||
formatMessage: props.intl.formatMessage.bind(props.intl),
|
||
viewportWidth: props.viewportWidth,
|
||
authorizeURL: props.tinode.authorizeURL.bind(props.tinode),
|
||
onHandleClick: this.handleDraftyClick,
|
||
onVoicePlayed: this.handleVoicePlayed
|
||
};
|
||
}
|
||
handleDraftyClick = (e, action) => {
|
||
switch (action) {
|
||
case 'image':
|
||
this.handleExpandImage(e);
|
||
break;
|
||
case 'video':
|
||
this.handlePlayVideo(e);
|
||
break;
|
||
case 'form_button':
|
||
this.handleFormButtonClick(e);
|
||
break;
|
||
case 'quote':
|
||
this.handleQuoteClick(e);
|
||
break;
|
||
case 'contact_chat':
|
||
e.preventDefault();
|
||
try {
|
||
const pathname = new URL(e.target.dataset.val)?.pathname;
|
||
const parts = pathname.split('/').filter(Boolean);
|
||
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_9__["default"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_9__["default"].setUrlTopic('', parts.pop() || ''));
|
||
} catch (error) {
|
||
console.error("Invalid URL:", error);
|
||
}
|
||
break;
|
||
case 'contact_find':
|
||
e.preventDefault();
|
||
let hashUrl = _lib_navigation_js__WEBPACK_IMPORTED_MODULE_9__["default"].setUrlSidePanel(window.location.hash, 'newtpk');
|
||
hashUrl = _lib_navigation_js__WEBPACK_IMPORTED_MODULE_9__["default"].addUrlParam(hashUrl, 'q', e.target.dataset.val);
|
||
hashUrl = _lib_navigation_js__WEBPACK_IMPORTED_MODULE_9__["default"].addUrlParam(hashUrl, 'tab', 'find');
|
||
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_9__["default"].navigateTo(hashUrl);
|
||
break;
|
||
default:
|
||
console.info('Unhandled drafty action.', action, e.target.dataset);
|
||
break;
|
||
}
|
||
};
|
||
handleExpandImage(e) {
|
||
e.preventDefault();
|
||
this.props.onExpandMedia({
|
||
url: e.target.src,
|
||
filename: e.target.dataset.name,
|
||
width: e.target.dataset.width,
|
||
height: e.target.dataset.height,
|
||
size: e.target.dataset.size,
|
||
type: e.target.dataset.mime
|
||
});
|
||
}
|
||
handlePlayVideo(e) {
|
||
e.preventDefault();
|
||
this.props.onExpandMedia({
|
||
video: true,
|
||
url: e.target.dataset.src,
|
||
preview: e.target.src,
|
||
filename: e.target.dataset.name,
|
||
width: e.target.dataset.width,
|
||
height: e.target.dataset.height,
|
||
duration: e.target.dataset.duration,
|
||
size: e.target.dataset.size,
|
||
type: e.target.dataset.mime
|
||
});
|
||
}
|
||
handleFormButtonClick(e) {
|
||
e.preventDefault();
|
||
const data = {
|
||
seq: this.props.seq
|
||
};
|
||
data.resp = {};
|
||
if (e.target.dataset.name) {
|
||
data.resp[e.target.dataset.name] = e.target.dataset.val ? e.target.dataset.val : e.target.dataset.val === undefined ? 1 : '' + e.target.dataset.val;
|
||
}
|
||
if (e.target.dataset.act == 'url') {
|
||
data.ref = (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_8__.sanitizeUrl)(e.target.dataset.ref) || 'about:blank';
|
||
}
|
||
const text = e.target.dataset.title || 'unknown';
|
||
this.props.onFormResponse(e.target.dataset.act, text, data);
|
||
}
|
||
handleContextClick(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
const menuItems = [];
|
||
if (this.props.received == tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.MESSAGE_STATUS_FAILED) {
|
||
menuItems.push('menu_item_send_retry');
|
||
}
|
||
if (this.props.received > tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.MESSAGE_STATUS_FATAL) {
|
||
if (this.props.userIsWriter) {
|
||
menuItems.push('menu_item_reply');
|
||
if (!this.props.response) {
|
||
let immutable = false;
|
||
tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.entities(this.props.content, (_0, _1, tp) => {
|
||
immutable = ['AU', 'EX', 'FM', 'IM', 'TC', 'VC', 'VD'].includes(tp);
|
||
return immutable;
|
||
});
|
||
if (!immutable) {
|
||
tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.styles(this.props.content, tp => {
|
||
immutable = ['QQ'].includes(tp);
|
||
return immutable;
|
||
});
|
||
}
|
||
if (!immutable) {
|
||
menuItems.push('menu_item_edit');
|
||
}
|
||
}
|
||
}
|
||
if (this.props.userIsAdmin) {
|
||
menuItems.push(this.props.pinned ? 'menu_item_unpin' : 'menu_item_pin');
|
||
}
|
||
}
|
||
menuItems.push('menu_item_forward');
|
||
this.props.showContextMenu({
|
||
seq: this.props.seq,
|
||
replace: this.props.edited ? parseInt(this.props.edited.split(':')[1]) : 0,
|
||
content: this.props.content,
|
||
head: this.props.head,
|
||
userFrom: this.props.userFrom,
|
||
userName: this.props.userName,
|
||
y: e.pageY,
|
||
x: e.pageX,
|
||
pickReply: this.props.pickReply,
|
||
editMessage: this.props.editMessage,
|
||
timestamp: this.props.timestamp
|
||
}, menuItems);
|
||
}
|
||
handleProgress(ratio) {
|
||
this.setState({
|
||
progress: ratio
|
||
});
|
||
}
|
||
handleCancelUpload() {
|
||
this.props.onCancelUpload(this.props.seq, this.props.uploader);
|
||
}
|
||
handleVoicePlayed() {
|
||
if (this.state.voicePlayed) {
|
||
return;
|
||
}
|
||
markVoicePlayed(this.props.tinode.getCurrentUserID(), this.props.topic, this.props.seq);
|
||
this.setState({
|
||
voicePlayed: true
|
||
});
|
||
}
|
||
handleQuoteClick(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
const replyToSeq = this.props.replyToSeq;
|
||
if (replyToSeq) {
|
||
this.props.onQuoteClick(replyToSeq);
|
||
}
|
||
}
|
||
handleAcceptGroupInvitation(invitationId) {
|
||
if (this.state.invitationBusy || this.state.invitationResult || !this.props.onAcceptGroupInvitation) {
|
||
return;
|
||
}
|
||
this.setState({
|
||
invitationBusy: true,
|
||
invitationError: null
|
||
});
|
||
this.props.onAcceptGroupInvitation(invitationId).then(result => {
|
||
this.setState({
|
||
invitationBusy: false,
|
||
invitationResult: result && result.status || 'joined'
|
||
});
|
||
}).catch(err => {
|
||
this.setState({
|
||
invitationBusy: false,
|
||
invitationError: err && err.message || '操作失败,请重试'
|
||
});
|
||
});
|
||
}
|
||
renderGroupInvitationCard(text, action) {
|
||
const {
|
||
invitationBusy,
|
||
invitationResult,
|
||
invitationError
|
||
} = this.state;
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("div", {
|
||
className: "message-group-invitation",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("div", {
|
||
children: text
|
||
}, void 0, false), invitationResult == 'joined' ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("em", {
|
||
children: "已加入群聊"
|
||
}, void 0, false) : invitationResult == 'pending_approval' ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("em", {
|
||
children: "已提交申请,等待管理员通过"
|
||
}, void 0, false) : invitationResult ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("em", {
|
||
children: invitationResult
|
||
}, void 0, false) : (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("button", {
|
||
type: "button",
|
||
disabled: invitationBusy,
|
||
onClick: () => this.handleAcceptGroupInvitation(action.target_id),
|
||
children: invitationBusy ? '处理中…' : action.label || '立即加入'
|
||
}, void 0, false), invitationError ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("small", {
|
||
className: "message-group-invitation-error",
|
||
children: invitationError
|
||
}, void 0, false) : null]
|
||
}, void 0, true);
|
||
}
|
||
renderFailReason() {
|
||
if (this.props.response || this.props.received != tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.MESSAGE_STATUS_FAILED && this.props.received != tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.MESSAGE_STATUS_FATAL) {
|
||
return null;
|
||
}
|
||
if (this.props.relationship == 'blocked_by_peer' || this.props.relationship == 'blocked_by_me') {
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("div", {
|
||
className: "message-fail-reason",
|
||
children: "消息已发送,但被对方拒收了"
|
||
}, void 0, false);
|
||
}
|
||
if (this.props.relationship == 'none') {
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("div", {
|
||
className: "message-fail-reason",
|
||
children: ["你还不是他(她)朋友。", (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: this.props.onSendFriendVerification,
|
||
children: "发送朋友验证"
|
||
}, void 0, false)]
|
||
}, void 0, true);
|
||
}
|
||
return null;
|
||
}
|
||
render() {
|
||
const sideClass = this.props.sequence + ' ' + (this.props.response ? 'left' : 'right');
|
||
const bubbleClass = this.props.sequence == 'single' || this.props.sequence == 'last' ? 'bubble tip' : 'bubble';
|
||
const avatar = this.props.userAvatar || true;
|
||
const isFailed = !this.props.response && (this.props.received == tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.MESSAGE_STATUS_FAILED || this.props.received == tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.MESSAGE_STATUS_FATAL);
|
||
const failReason = this.renderFailReason();
|
||
let isVoiceMessage = false;
|
||
if (this.props.response && this.props.mimeType == tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.getContentType() && tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.isValid(this.props.content)) {
|
||
tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.entities(this.props.content, (_0, _1, tp) => {
|
||
if (tp === 'AU') {
|
||
isVoiceMessage = true;
|
||
}
|
||
return isVoiceMessage;
|
||
});
|
||
}
|
||
const showVoiceUnreadDot = isVoiceMessage && !this.state.voicePlayed;
|
||
let textSizeClass = 'message-content';
|
||
const fullDisplay = this.props.isGroup && this.props.response && (this.props.sequence == 'single' || this.props.sequence == 'last');
|
||
let content = this.props.content;
|
||
const systemActions = content && typeof content == 'object' && Array.isArray(content.actions) ? content.actions : null;
|
||
const inviteAction = this.props.head && this.props.head['x-im-type'] === 'system_action' && systemActions && systemActions.find(a => a && a.type === 'group_invitation_accept');
|
||
let invitationCard = null;
|
||
if (inviteAction && typeof content.text == 'string') {
|
||
invitationCard = this.renderGroupInvitationCard(content.text, inviteAction);
|
||
} else if (this.props.head && (this.props.head['x-im-type'] === 'system_notice' || this.props.head['x-im-type'] === 'system_action') && content && typeof content == 'object' && typeof content.text == 'string') {
|
||
content = content.text;
|
||
}
|
||
let bundleCard = null;
|
||
if (this.props.head && this.props.head['x-im-type'] === 'forward_bundle' && content && typeof content == 'object' && (content.items || content.preview)) {
|
||
bundleCard = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)(_forward_bundle_jsx__WEBPACK_IMPORTED_MODULE_4__["default"], {
|
||
content: content,
|
||
tinode: this.props.tinode
|
||
}, void 0, false);
|
||
}
|
||
const attachments = [];
|
||
if (this.props.mimeType == tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.getContentType() && tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.isValid(content)) {
|
||
tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.attachments(content, (att, i) => {
|
||
if (tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.isFormResponseType(att.mime)) {
|
||
return;
|
||
}
|
||
attachments.push((0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)(_attachment_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||
tinode: this.props.tinode,
|
||
downloadUrl: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.getDownloadUrl(att),
|
||
filename: att.name,
|
||
uploading: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.isProcessing(att),
|
||
mimetype: att.mime,
|
||
size: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.getEntitySize(att),
|
||
progress: this.state.progress,
|
||
onCancelUpload: this.handleCancelUpload,
|
||
onError: this.props.onError
|
||
}, i, false));
|
||
}, this);
|
||
const tree = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.format(content, _lib_formatters_js__WEBPACK_IMPORTED_MODULE_7__.fullFormatter, this.formatterContext);
|
||
content = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, tree);
|
||
} else if (typeof content == 'string') {
|
||
if (new RegExp('^\\p{RGI_Emoji}{1,5}$', 'v').test(content || '')) {
|
||
textSizeClass += ' emoji-' + (content || '').match(/(?:👨🏻❤️💋👨🏻|👨🏻❤️💋👨🏼|👨🏻❤️💋👨🏽|👨🏻❤️💋👨🏾|👨🏻❤️💋👨🏿|👨🏼❤️💋👨🏻|👨🏼❤️💋👨🏼|👨🏼❤️💋👨🏽|👨🏼❤️💋👨🏾|👨🏼❤️💋👨🏿|👨🏽❤️💋👨🏻|👨🏽❤️💋👨🏼|👨🏽❤️💋👨🏽|👨🏽❤️💋👨🏾|👨🏽❤️💋👨🏿|👨🏾❤️💋👨🏻|👨🏾❤️💋👨🏼|👨🏾❤️💋👨🏽|👨🏾❤️💋👨🏾|👨🏾❤️💋👨🏿|👨🏿❤️💋👨🏻|👨🏿❤️💋👨🏼|👨🏿❤️💋👨🏽|👨🏿❤️💋👨🏾|👨🏿❤️💋👨🏿|👩🏻❤️💋👨🏻|👩🏻❤️💋👨🏼|👩🏻❤️💋👨🏽|👩🏻❤️💋👨🏾|👩🏻❤️💋👨🏿|👩🏻❤️💋👩🏻|👩🏻❤️💋👩🏼|👩🏻❤️💋👩🏽|👩🏻❤️💋👩🏾|👩🏻❤️💋👩🏿|👩🏼❤️💋👨🏻|👩🏼❤️💋👨🏼|👩🏼❤️💋👨🏽|👩🏼❤️💋👨🏾|👩🏼❤️💋👨🏿|👩🏼❤️💋👩🏻|👩🏼❤️💋👩🏼|👩🏼❤️💋👩🏽|👩🏼❤️💋👩🏾|👩🏼❤️💋👩🏿|👩🏽❤️💋👨🏻|👩🏽❤️💋👨🏼|👩🏽❤️💋👨🏽|👩🏽❤️💋👨🏾|👩🏽❤️💋👨🏿|👩🏽❤️💋👩🏻|👩🏽❤️💋👩🏼|👩🏽❤️💋👩🏽|👩🏽❤️💋👩🏾|👩🏽❤️💋👩🏿|👩🏾❤️💋👨🏻|👩🏾❤️💋👨🏼|👩🏾❤️💋👨🏽|👩🏾❤️💋👨🏾|👩🏾❤️💋👨🏿|👩🏾❤️💋👩🏻|👩🏾❤️💋👩🏼|👩🏾❤️💋👩🏽|👩🏾❤️💋👩🏾|👩🏾❤️💋👩🏿|👩🏿❤️💋👨🏻|👩🏿❤️💋👨🏼|👩🏿❤️💋👨🏽|👩🏿❤️💋👨🏾|👩🏿❤️💋👨🏿|👩🏿❤️💋👩🏻|👩🏿❤️💋👩🏼|👩🏿❤️💋👩🏽|👩🏿❤️💋👩🏾|👩🏿❤️💋👩🏿|🧑🏻❤️💋🧑🏼|🧑🏻❤️💋🧑🏽|🧑🏻❤️💋🧑🏾|🧑🏻❤️💋🧑🏿|🧑🏼❤️💋🧑🏻|🧑🏼❤️💋🧑🏽|🧑🏼❤️💋🧑🏾|🧑🏼❤️💋🧑🏿|🧑🏽❤️💋🧑🏻|🧑🏽❤️💋🧑🏼|🧑🏽❤️💋🧑🏾|🧑🏽❤️💋🧑🏿|🧑🏾❤️💋🧑🏻|🧑🏾❤️💋🧑🏼|🧑🏾❤️💋🧑🏽|🧑🏾❤️💋🧑🏿|🧑🏿❤️💋🧑🏻|🧑🏿❤️💋🧑🏼|🧑🏿❤️💋🧑🏽|🧑🏿❤️💋🧑🏾|🏴|🏴|🏴|👨🏻❤️👨🏻|👨🏻❤️👨🏼|👨🏻❤️👨🏽|👨🏻❤️👨🏾|👨🏻❤️👨🏿|👨🏻🐰👨🏼|👨🏻🐰👨🏽|👨🏻🐰👨🏾|👨🏻🐰👨🏿|👨🏻🤝👨🏼|👨🏻🤝👨🏽|👨🏻🤝👨🏾|👨🏻🤝👨🏿|👨🏻👨🏼|👨🏻👨🏽|👨🏻👨🏾|👨🏻👨🏿|👨🏼❤️👨🏻|👨🏼❤️👨🏼|👨🏼❤️👨🏽|👨🏼❤️👨🏾|👨🏼❤️👨🏿|👨🏼🐰👨🏻|👨🏼🐰👨🏽|👨🏼🐰👨🏾|👨🏼🐰👨🏿|👨🏼🤝👨🏻|👨🏼🤝👨🏽|👨🏼🤝👨🏾|👨🏼🤝👨🏿|👨🏼👨🏻|👨🏼👨🏽|👨🏼👨🏾|👨🏼👨🏿|👨🏽❤️👨🏻|👨🏽❤️👨🏼|👨🏽❤️👨🏽|👨🏽❤️👨🏾|👨🏽❤️👨🏿|👨🏽🐰👨🏻|👨🏽🐰👨🏼|👨🏽🐰👨🏾|👨🏽🐰👨🏿|👨🏽🤝👨🏻|👨🏽🤝👨🏼|👨🏽🤝👨🏾|👨🏽🤝👨🏿|👨🏽👨🏻|👨🏽👨🏼|👨🏽👨🏾|👨🏽👨🏿|👨🏾❤️👨🏻|👨🏾❤️👨🏼|👨🏾❤️👨🏽|👨🏾❤️👨🏾|👨🏾❤️👨🏿|👨🏾🐰👨🏻|👨🏾🐰👨🏼|👨🏾🐰👨🏽|👨🏾🐰👨🏿|👨🏾🤝👨🏻|👨🏾🤝👨🏼|👨🏾🤝👨🏽|👨🏾🤝👨🏿|👨🏾👨🏻|👨🏾👨🏼|👨🏾👨🏽|👨🏾👨🏿|👨🏿❤️👨🏻|👨🏿❤️👨🏼|👨🏿❤️👨🏽|👨🏿❤️👨🏾|👨🏿❤️👨🏿|👨🏿🐰👨🏻|👨🏿🐰👨🏼|👨🏿🐰👨🏽|👨🏿🐰👨🏾|👨🏿🤝👨🏻|👨🏿🤝👨🏼|👨🏿🤝👨🏽|👨🏿🤝👨🏾|👨🏿👨🏻|👨🏿👨🏼|👨🏿👨🏽|👨🏿👨🏾|👩🏻❤️👨🏻|👩🏻❤️👨🏼|👩🏻❤️👨🏽|👩🏻❤️👨🏾|👩🏻❤️👨🏿|👩🏻❤️👩🏻|👩🏻❤️👩🏼|👩🏻❤️👩🏽|👩🏻❤️👩🏾|👩🏻❤️👩🏿|👩🏻🐰👩🏼|👩🏻🐰👩🏽|👩🏻🐰👩🏾|👩🏻🐰👩🏿|👩🏻🤝👨🏼|👩🏻🤝👨🏽|👩🏻🤝👨🏾|👩🏻🤝👨🏿|👩🏻🤝👩🏼|👩🏻🤝👩🏽|👩🏻🤝👩🏾|👩🏻🤝👩🏿|👩🏻👩🏼|👩🏻👩🏽|👩🏻👩🏾|👩🏻👩🏿|👩🏼❤️👨🏻|👩🏼❤️👨🏼|👩🏼❤️👨🏽|👩🏼❤️👨🏾|👩🏼❤️👨🏿|👩🏼❤️👩🏻|👩🏼❤️👩🏼|👩🏼❤️👩🏽|👩🏼❤️👩🏾|👩🏼❤️👩🏿|👩🏼🐰👩🏻|👩🏼🐰👩🏽|👩🏼🐰👩🏾|👩🏼🐰👩🏿|👩🏼🤝👨🏻|👩🏼🤝👨🏽|👩🏼🤝👨🏾|👩🏼🤝👨🏿|👩🏼🤝👩🏻|👩🏼🤝👩🏽|👩🏼🤝👩🏾|👩🏼🤝👩🏿|👩🏼👩🏻|👩🏼👩🏽|👩🏼👩🏾|👩🏼👩🏿|👩🏽❤️👨🏻|👩🏽❤️👨🏼|👩🏽❤️👨🏽|👩🏽❤️👨🏾|👩🏽❤️👨🏿|👩🏽❤️👩🏻|👩🏽❤️👩🏼|👩🏽❤️👩🏽|👩🏽❤️👩🏾|👩🏽❤️👩🏿|👩🏽🐰👩🏻|👩🏽🐰👩🏼|👩🏽🐰👩🏾|👩🏽🐰👩🏿|👩🏽🤝👨🏻|👩🏽🤝👨🏼|👩🏽🤝👨🏾|👩🏽🤝👨🏿|👩🏽🤝👩🏻|👩🏽🤝👩🏼|👩🏽🤝👩🏾|👩🏽🤝👩🏿|👩🏽👩🏻|👩🏽👩🏼|👩🏽👩🏾|👩🏽👩🏿|👩🏾❤️👨🏻|👩🏾❤️👨🏼|👩🏾❤️👨🏽|👩🏾❤️👨🏾|👩🏾❤️👨🏿|👩🏾❤️👩🏻|👩🏾❤️👩🏼|👩🏾❤️👩🏽|👩🏾❤️👩🏾|👩🏾❤️👩🏿|👩🏾🐰👩🏻|👩🏾🐰👩🏼|👩🏾🐰👩🏽|👩🏾🐰👩🏿|👩🏾🤝👨🏻|👩🏾🤝👨🏼|👩🏾🤝👨🏽|👩🏾🤝👨🏿|👩🏾🤝👩🏻|👩🏾🤝👩🏼|👩🏾🤝👩🏽|👩🏾🤝👩🏿|👩🏾👩🏻|👩🏾👩🏼|👩🏾👩🏽|👩🏾👩🏿|👩🏿❤️👨🏻|👩🏿❤️👨🏼|👩🏿❤️👨🏽|👩🏿❤️👨🏾|👩🏿❤️👨🏿|👩🏿❤️👩🏻|👩🏿❤️👩🏼|👩🏿❤️👩🏽|👩🏿❤️👩🏾|👩🏿❤️👩🏿|👩🏿🐰👩🏻|👩🏿🐰👩🏼|👩🏿🐰👩🏽|👩🏿🐰👩🏾|👩🏿🤝👨🏻|👩🏿🤝👨🏼|👩🏿🤝👨🏽|👩🏿🤝👨🏾|👩🏿🤝👩🏻|👩🏿🤝👩🏼|👩🏿🤝👩🏽|👩🏿🤝👩🏾|👩🏿👩🏻|👩🏿👩🏼|👩🏿👩🏽|👩🏿👩🏾|🧑🏻❤️🧑🏼|🧑🏻❤️🧑🏽|🧑🏻❤️🧑🏾|🧑🏻❤️🧑🏿|🧑🏻🐰🧑🏼|🧑🏻🐰🧑🏽|🧑🏻🐰🧑🏾|🧑🏻🐰🧑🏿|🧑🏻🤝🧑🏻|🧑🏻🤝🧑🏼|🧑🏻🤝🧑🏽|🧑🏻🤝🧑🏾|🧑🏻🤝🧑🏿|🧑🏻🧑🏼|🧑🏻🧑🏽|🧑🏻🧑🏾|🧑🏻🧑🏿|🧑🏼❤️🧑🏻|🧑🏼❤️🧑🏽|🧑🏼❤️🧑🏾|🧑🏼❤️🧑🏿|🧑🏼🐰🧑🏻|🧑🏼🐰🧑🏽|🧑🏼🐰🧑🏾|🧑🏼🐰🧑🏿|🧑🏼🤝🧑🏻|🧑🏼🤝🧑🏼|🧑🏼🤝🧑🏽|🧑🏼🤝🧑🏾|🧑🏼🤝🧑🏿|🧑🏼🧑🏻|🧑🏼🧑🏽|🧑🏼🧑🏾|🧑🏼🧑🏿|🧑🏽❤️🧑🏻|🧑🏽❤️🧑🏼|🧑🏽❤️🧑🏾|🧑🏽❤️🧑🏿|🧑🏽🐰🧑🏻|🧑🏽🐰🧑🏼|🧑🏽🐰🧑🏾|🧑🏽🐰🧑🏿|🧑🏽🤝🧑🏻|🧑🏽🤝🧑🏼|🧑🏽🤝🧑🏽|🧑🏽🤝🧑🏾|🧑🏽🤝🧑🏿|🧑🏽🧑🏻|🧑🏽🧑🏼|🧑🏽🧑🏾|🧑🏽🧑🏿|🧑🏾❤️🧑🏻|🧑🏾❤️🧑🏼|🧑🏾❤️🧑🏽|🧑🏾❤️🧑🏿|🧑🏾🐰🧑🏻|🧑🏾🐰🧑🏼|🧑🏾🐰🧑🏽|🧑🏾🐰🧑🏿|🧑🏾🤝🧑🏻|🧑🏾🤝🧑🏼|🧑🏾🤝🧑🏽|🧑🏾🤝🧑🏾|🧑🏾🤝🧑🏿|🧑🏾🧑🏻|🧑🏾🧑🏼|🧑🏾🧑🏽|🧑🏾🧑🏿|🧑🏿❤️🧑🏻|🧑🏿❤️🧑🏼|🧑🏿❤️🧑🏽|🧑🏿❤️🧑🏾|🧑🏿🐰🧑🏻|🧑🏿🐰🧑🏼|🧑🏿🐰🧑🏽|🧑🏿🐰🧑🏾|🧑🏿🤝🧑🏻|🧑🏿🤝🧑🏼|🧑🏿🤝🧑🏽|🧑🏿🤝🧑🏾|🧑🏿🤝🧑🏿|🧑🏿🧑🏻|🧑🏿🧑🏼|🧑🏿🧑🏽|🧑🏿🧑🏾|👨❤️💋👨|👨👨👦👦|👨👨👧👦|👨👨👧👧|👨👩👦👦|👨👩👧👦|👨👩👧👧|👩❤️💋👨|👩❤️💋👩|👩👩👦👦|👩👩👧👦|👩👩👧👧|🧑🧑🧒🧒|🏃🏻♀️➡️|🏃🏻♂️➡️|🏃🏼♀️➡️|🏃🏼♂️➡️|🏃🏽♀️➡️|🏃🏽♂️➡️|🏃🏾♀️➡️|🏃🏾♂️➡️|🏃🏿♀️➡️|🏃🏿♂️➡️|👨🏻🦯➡️|👨🏻🦼➡️|👨🏻🦽➡️|👨🏼🦯➡️|👨🏼🦼➡️|👨🏼🦽➡️|👨🏽🦯➡️|👨🏽🦼➡️|👨🏽🦽➡️|👨🏾🦯➡️|👨🏾🦼➡️|👨🏾🦽➡️|👨🏿🦯➡️|👨🏿🦼➡️|👨🏿🦽➡️|👩🏻🦯➡️|👩🏻🦼➡️|👩🏻🦽➡️|👩🏼🦯➡️|👩🏼🦼➡️|👩🏼🦽➡️|👩🏽🦯➡️|👩🏽🦼➡️|👩🏽🦽➡️|👩🏾🦯➡️|👩🏾🦼➡️|👩🏾🦽➡️|👩🏿🦯➡️|👩🏿🦼➡️|👩🏿🦽➡️|🚶🏻♀️➡️|🚶🏻♂️➡️|🚶🏼♀️➡️|🚶🏼♂️➡️|🚶🏽♀️➡️|🚶🏽♂️➡️|🚶🏾♀️➡️|🚶🏾♂️➡️|🚶🏿♀️➡️|🚶🏿♂️➡️|🧎🏻♀️➡️|🧎🏻♂️➡️|🧎🏼♀️➡️|🧎🏼♂️➡️|🧎🏽♀️➡️|🧎🏽♂️➡️|🧎🏾♀️➡️|🧎🏾♂️➡️|🧎🏿♀️➡️|🧎🏿♂️➡️|🧑🏻🦯➡️|🧑🏻🦼➡️|🧑🏻🦽➡️|🧑🏼🦯➡️|🧑🏼🦼➡️|🧑🏼🦽➡️|🧑🏽🦯➡️|🧑🏽🦼➡️|🧑🏽🦽➡️|🧑🏾🦯➡️|🧑🏾🦼➡️|🧑🏾🦽➡️|🧑🏿🦯➡️|🧑🏿🦼➡️|🧑🏿🦽➡️|🫱🏻🫲🏼|🫱🏻🫲🏽|🫱🏻🫲🏾|🫱🏻🫲🏿|🫱🏼🫲🏻|🫱🏼🫲🏽|🫱🏼🫲🏾|🫱🏼🫲🏿|🫱🏽🫲🏻|🫱🏽🫲🏼|🫱🏽🫲🏾|🫱🏽🫲🏿|🫱🏾🫲🏻|🫱🏾🫲🏼|🫱🏾🫲🏽|🫱🏾🫲🏿|🫱🏿🫲🏻|🫱🏿🫲🏼|🫱🏿🫲🏽|🫱🏿🫲🏾|🏃♀️➡️|🏃♂️➡️|👨❤️👨|👨👦👦|👨👧👦|👨👧👧|👨👨👦|👨👨👧|👨👩👦|👨👩👧|👨🦯➡️|👨🦼➡️|👨🦽➡️|👩❤️👨|👩❤️👩|👩👦👦|👩👧👦|👩👧👧|👩👩👦|👩👩👧|👩🦯➡️|👩🦼➡️|👩🦽➡️|🚶♀️➡️|🚶♂️➡️|🧎♀️➡️|🧎♂️➡️|🧑🤝🧑|🧑🦯➡️|🧑🦼➡️|🧑🦽➡️|🧑🧑🧒|🧑🧒🧒|🏃🏻♀️|🏃🏻♂️|🏃🏻➡️|🏃🏼♀️|🏃🏼♂️|🏃🏼➡️|🏃🏽♀️|🏃🏽♂️|🏃🏽➡️|🏃🏾♀️|🏃🏾♂️|🏃🏾➡️|🏃🏿♀️|🏃🏿♂️|🏃🏿➡️|🏄🏻♀️|🏄🏻♂️|🏄🏼♀️|🏄🏼♂️|🏄🏽♀️|🏄🏽♂️|🏄🏾♀️|🏄🏾♂️|🏄🏿♀️|🏄🏿♂️|🏊🏻♀️|🏊🏻♂️|🏊🏼♀️|🏊🏼♂️|🏊🏽♀️|🏊🏽♂️|🏊🏾♀️|🏊🏾♂️|🏊🏿♀️|🏊🏿♂️|🏋🏻♀️|🏋🏻♂️|🏋🏼♀️|🏋🏼♂️|🏋🏽♀️|🏋🏽♂️|🏋🏾♀️|🏋🏾♂️|🏋🏿♀️|🏋🏿♂️|🏌🏻♀️|🏌🏻♂️|🏌🏼♀️|🏌🏼♂️|🏌🏽♀️|🏌🏽♂️|🏌🏾♀️|🏌🏾♂️|🏌🏿♀️|🏌🏿♂️|👁️🗨️|👨🏻⚕️|👨🏻⚖️|👨🏻✈️|👨🏻🌾|👨🏻🍳|👨🏻🍼|👨🏻🎓|👨🏻🎤|👨🏻🎨|👨🏻🏫|👨🏻🏭|👨🏻💻|👨🏻💼|👨🏻🔧|👨🏻🔬|👨🏻🚀|👨🏻🚒|👨🏻🦯|👨🏻🦰|👨🏻🦱|👨🏻🦲|👨🏻🦳|👨🏻🦼|👨🏻🦽|👨🏼⚕️|👨🏼⚖️|👨🏼✈️|👨🏼🌾|👨🏼🍳|👨🏼🍼|👨🏼🎓|👨🏼🎤|👨🏼🎨|👨🏼🏫|👨🏼🏭|👨🏼💻|👨🏼💼|👨🏼🔧|👨🏼🔬|👨🏼🚀|👨🏼🚒|👨🏼🦯|👨🏼🦰|👨🏼🦱|👨🏼🦲|👨🏼🦳|👨🏼🦼|👨🏼🦽|👨🏽⚕️|👨🏽⚖️|👨🏽✈️|👨🏽🌾|👨🏽🍳|👨🏽🍼|👨🏽🎓|👨🏽🎤|👨🏽🎨|👨🏽🏫|👨🏽🏭|👨🏽💻|👨🏽💼|👨🏽🔧|👨🏽🔬|👨🏽🚀|👨🏽🚒|👨🏽🦯|👨🏽🦰|👨🏽🦱|👨🏽🦲|👨🏽🦳|👨🏽🦼|👨🏽🦽|👨🏾⚕️|👨🏾⚖️|👨🏾✈️|👨🏾🌾|👨🏾🍳|👨🏾🍼|👨🏾🎓|👨🏾🎤|👨🏾🎨|👨🏾🏫|👨🏾🏭|👨🏾💻|👨🏾💼|👨🏾🔧|👨🏾🔬|👨🏾🚀|👨🏾🚒|👨🏾🦯|👨🏾🦰|👨🏾🦱|👨🏾🦲|👨🏾🦳|👨🏾🦼|👨🏾🦽|👨🏿⚕️|👨🏿⚖️|👨🏿✈️|👨🏿🌾|👨🏿🍳|👨🏿🍼|👨🏿🎓|👨🏿🎤|👨🏿🎨|👨🏿🏫|👨🏿🏭|👨🏿💻|👨🏿💼|👨🏿🔧|👨🏿🔬|👨🏿🚀|👨🏿🚒|👨🏿🦯|👨🏿🦰|👨🏿🦱|👨🏿🦲|👨🏿🦳|👨🏿🦼|👨🏿🦽|👩🏻⚕️|👩🏻⚖️|👩🏻✈️|👩🏻🌾|👩🏻🍳|👩🏻🍼|👩🏻🎓|👩🏻🎤|👩🏻🎨|👩🏻🏫|👩🏻🏭|👩🏻💻|👩🏻💼|👩🏻🔧|👩🏻🔬|👩🏻🚀|👩🏻🚒|👩🏻🦯|👩🏻🦰|👩🏻🦱|👩🏻🦲|👩🏻🦳|👩🏻🦼|👩🏻🦽|👩🏼⚕️|👩🏼⚖️|👩🏼✈️|👩🏼🌾|👩🏼🍳|👩🏼🍼|👩🏼🎓|👩🏼🎤|👩🏼🎨|👩🏼🏫|👩🏼🏭|👩🏼💻|👩🏼💼|👩🏼🔧|👩🏼🔬|👩🏼🚀|👩🏼🚒|👩🏼🦯|👩🏼🦰|👩🏼🦱|👩🏼🦲|👩🏼🦳|👩🏼🦼|👩🏼🦽|👩🏽⚕️|👩🏽⚖️|👩🏽✈️|👩🏽🌾|👩🏽🍳|👩🏽🍼|👩🏽🎓|👩🏽🎤|👩🏽🎨|👩🏽🏫|👩🏽🏭|👩🏽💻|👩🏽💼|👩🏽🔧|👩🏽🔬|👩🏽🚀|👩🏽🚒|👩🏽🦯|👩🏽🦰|👩🏽🦱|👩🏽🦲|👩🏽🦳|👩🏽🦼|👩🏽🦽|👩🏾⚕️|👩🏾⚖️|👩🏾✈️|👩🏾🌾|👩🏾🍳|👩🏾🍼|👩🏾🎓|👩🏾🎤|👩🏾🎨|👩🏾🏫|👩🏾🏭|👩🏾💻|👩🏾💼|👩🏾🔧|👩🏾🔬|👩🏾🚀|👩🏾🚒|👩🏾🦯|👩🏾🦰|👩🏾🦱|👩🏾🦲|👩🏾🦳|👩🏾🦼|👩🏾🦽|👩🏿⚕️|👩🏿⚖️|👩🏿✈️|👩🏿🌾|👩🏿🍳|👩🏿🍼|👩🏿🎓|👩🏿🎤|👩🏿🎨|👩🏿🏫|👩🏿🏭|👩🏿💻|👩🏿💼|👩🏿🔧|👩🏿🔬|👩🏿🚀|👩🏿🚒|👩🏿🦯|👩🏿🦰|👩🏿🦱|👩🏿🦲|👩🏿🦳|👩🏿🦼|👩🏿🦽|👮🏻♀️|👮🏻♂️|👮🏼♀️|👮🏼♂️|👮🏽♀️|👮🏽♂️|👮🏾♀️|👮🏾♂️|👮🏿♀️|👮🏿♂️|👯🏻♀️|👯🏻♂️|👯🏼♀️|👯🏼♂️|👯🏽♀️|👯🏽♂️|👯🏾♀️|👯🏾♂️|👯🏿♀️|👯🏿♂️|👰🏻♀️|👰🏻♂️|👰🏼♀️|👰🏼♂️|👰🏽♀️|👰🏽♂️|👰🏾♀️|👰🏾♂️|👰🏿♀️|👰🏿♂️|👱🏻♀️|👱🏻♂️|👱🏼♀️|👱🏼♂️|👱🏽♀️|👱🏽♂️|👱🏾♀️|👱🏾♂️|👱🏿♀️|👱🏿♂️|👳🏻♀️|👳🏻♂️|👳🏼♀️|👳🏼♂️|👳🏽♀️|👳🏽♂️|👳🏾♀️|👳🏾♂️|👳🏿♀️|👳🏿♂️|👷🏻♀️|👷🏻♂️|👷🏼♀️|👷🏼♂️|👷🏽♀️|👷🏽♂️|👷🏾♀️|👷🏾♂️|👷🏿♀️|👷🏿♂️|💁🏻♀️|💁🏻♂️|💁🏼♀️|💁🏼♂️|💁🏽♀️|💁🏽♂️|💁🏾♀️|💁🏾♂️|💁🏿♀️|💁🏿♂️|💂🏻♀️|💂🏻♂️|💂🏼♀️|💂🏼♂️|💂🏽♀️|💂🏽♂️|💂🏾♀️|💂🏾♂️|💂🏿♀️|💂🏿♂️|💆🏻♀️|💆🏻♂️|💆🏼♀️|💆🏼♂️|💆🏽♀️|💆🏽♂️|💆🏾♀️|💆🏾♂️|💆🏿♀️|💆🏿♂️|💇🏻♀️|💇🏻♂️|💇🏼♀️|💇🏼♂️|💇🏽♀️|💇🏽♂️|💇🏾♀️|💇🏾♂️|💇🏿♀️|💇🏿♂️|🕵🏻♀️|🕵🏻♂️|🕵🏼♀️|🕵🏼♂️|🕵🏽♀️|🕵🏽♂️|🕵🏾♀️|🕵🏾♂️|🕵🏿♀️|🕵🏿♂️|🙅🏻♀️|🙅🏻♂️|🙅🏼♀️|🙅🏼♂️|🙅🏽♀️|🙅🏽♂️|🙅🏾♀️|🙅🏾♂️|🙅🏿♀️|🙅🏿♂️|🙆🏻♀️|🙆🏻♂️|🙆🏼♀️|🙆🏼♂️|🙆🏽♀️|🙆🏽♂️|🙆🏾♀️|🙆🏾♂️|🙆🏿♀️|🙆🏿♂️|🙇🏻♀️|🙇🏻♂️|🙇🏼♀️|🙇🏼♂️|🙇🏽♀️|🙇🏽♂️|🙇🏾♀️|🙇🏾♂️|🙇🏿♀️|🙇🏿♂️|🙋🏻♀️|🙋🏻♂️|🙋🏼♀️|🙋🏼♂️|🙋🏽♀️|🙋🏽♂️|🙋🏾♀️|🙋🏾♂️|🙋🏿♀️|🙋🏿♂️|🙍🏻♀️|🙍🏻♂️|🙍🏼♀️|🙍🏼♂️|🙍🏽♀️|🙍🏽♂️|🙍🏾♀️|🙍🏾♂️|🙍🏿♀️|🙍🏿♂️|🙎🏻♀️|🙎🏻♂️|🙎🏼♀️|🙎🏼♂️|🙎🏽♀️|🙎🏽♂️|🙎🏾♀️|🙎🏾♂️|🙎🏿♀️|🙎🏿♂️|🚣🏻♀️|🚣🏻♂️|🚣🏼♀️|🚣🏼♂️|🚣🏽♀️|🚣🏽♂️|🚣🏾♀️|🚣🏾♂️|🚣🏿♀️|🚣🏿♂️|🚴🏻♀️|🚴🏻♂️|🚴🏼♀️|🚴🏼♂️|🚴🏽♀️|🚴🏽♂️|🚴🏾♀️|🚴🏾♂️|🚴🏿♀️|🚴🏿♂️|🚵🏻♀️|🚵🏻♂️|🚵🏼♀️|🚵🏼♂️|🚵🏽♀️|🚵🏽♂️|🚵🏾♀️|🚵🏾♂️|🚵🏿♀️|🚵🏿♂️|🚶🏻♀️|🚶🏻♂️|🚶🏻➡️|🚶🏼♀️|🚶🏼♂️|🚶🏼➡️|🚶🏽♀️|🚶🏽♂️|🚶🏽➡️|🚶🏾♀️|🚶🏾♂️|🚶🏾➡️|🚶🏿♀️|🚶🏿♂️|🚶🏿➡️|🤦🏻♀️|🤦🏻♂️|🤦🏼♀️|🤦🏼♂️|🤦🏽♀️|🤦🏽♂️|🤦🏾♀️|🤦🏾♂️|🤦🏿♀️|🤦🏿♂️|🤵🏻♀️|🤵🏻♂️|🤵🏼♀️|🤵🏼♂️|🤵🏽♀️|🤵🏽♂️|🤵🏾♀️|🤵🏾♂️|🤵🏿♀️|🤵🏿♂️|🤷🏻♀️|🤷🏻♂️|🤷🏼♀️|🤷🏼♂️|🤷🏽♀️|🤷🏽♂️|🤷🏾♀️|🤷🏾♂️|🤷🏿♀️|🤷🏿♂️|🤸🏻♀️|🤸🏻♂️|🤸🏼♀️|🤸🏼♂️|🤸🏽♀️|🤸🏽♂️|🤸🏾♀️|🤸🏾♂️|🤸🏿♀️|🤸🏿♂️|🤹🏻♀️|🤹🏻♂️|🤹🏼♀️|🤹🏼♂️|🤹🏽♀️|🤹🏽♂️|🤹🏾♀️|🤹🏾♂️|🤹🏿♀️|🤹🏿♂️|🤼🏻♀️|🤼🏻♂️|🤼🏼♀️|🤼🏼♂️|🤼🏽♀️|🤼🏽♂️|🤼🏾♀️|🤼🏾♂️|🤼🏿♀️|🤼🏿♂️|🤽🏻♀️|🤽🏻♂️|🤽🏼♀️|🤽🏼♂️|🤽🏽♀️|🤽🏽♂️|🤽🏾♀️|🤽🏾♂️|🤽🏿♀️|🤽🏿♂️|🤾🏻♀️|🤾🏻♂️|🤾🏼♀️|🤾🏼♂️|🤾🏽♀️|🤾🏽♂️|🤾🏾♀️|🤾🏾♂️|🤾🏿♀️|🤾🏿♂️|🦸🏻♀️|🦸🏻♂️|🦸🏼♀️|🦸🏼♂️|🦸🏽♀️|🦸🏽♂️|🦸🏾♀️|🦸🏾♂️|🦸🏿♀️|🦸🏿♂️|🦹🏻♀️|🦹🏻♂️|🦹🏼♀️|🦹🏼♂️|🦹🏽♀️|🦹🏽♂️|🦹🏾♀️|🦹🏾♂️|🦹🏿♀️|🦹🏿♂️|🧍🏻♀️|🧍🏻♂️|🧍🏼♀️|🧍🏼♂️|🧍🏽♀️|🧍🏽♂️|🧍🏾♀️|🧍🏾♂️|🧍🏿♀️|🧍🏿♂️|🧎🏻♀️|🧎🏻♂️|🧎🏻➡️|🧎🏼♀️|🧎🏼♂️|🧎🏼➡️|🧎🏽♀️|🧎🏽♂️|🧎🏽➡️|🧎🏾♀️|🧎🏾♂️|🧎🏾➡️|🧎🏿♀️|🧎🏿♂️|🧎🏿➡️|🧏🏻♀️|🧏🏻♂️|🧏🏼♀️|🧏🏼♂️|🧏🏽♀️|🧏🏽♂️|🧏🏾♀️|🧏🏾♂️|🧏🏿♀️|🧏🏿♂️|🧑🏻⚕️|🧑🏻⚖️|🧑🏻✈️|🧑🏻🌾|🧑🏻🍳|🧑🏻🍼|🧑🏻🎄|🧑🏻🎓|🧑🏻🎤|🧑🏻🎨|🧑🏻🏫|🧑🏻🏭|🧑🏻💻|🧑🏻💼|🧑🏻🔧|🧑🏻🔬|🧑🏻🚀|🧑🏻🚒|🧑🏻🦯|🧑🏻🦰|🧑🏻🦱|🧑🏻🦲|🧑🏻🦳|🧑🏻🦼|🧑🏻🦽|🧑🏻🩰|🧑🏼⚕️|🧑🏼⚖️|🧑🏼✈️|🧑🏼🌾|🧑🏼🍳|🧑🏼🍼|🧑🏼🎄|🧑🏼🎓|🧑🏼🎤|🧑🏼🎨|🧑🏼🏫|🧑🏼🏭|🧑🏼💻|🧑🏼💼|🧑🏼🔧|🧑🏼🔬|🧑🏼🚀|🧑🏼🚒|🧑🏼🦯|🧑🏼🦰|🧑🏼🦱|🧑🏼🦲|🧑🏼🦳|🧑🏼🦼|🧑🏼🦽|🧑🏼🩰|🧑🏽⚕️|🧑🏽⚖️|🧑🏽✈️|🧑🏽🌾|🧑🏽🍳|🧑🏽🍼|🧑🏽🎄|🧑🏽🎓|🧑🏽🎤|🧑🏽🎨|🧑🏽🏫|🧑🏽🏭|🧑🏽💻|🧑🏽💼|🧑🏽🔧|🧑🏽🔬|🧑🏽🚀|🧑🏽🚒|🧑🏽🦯|🧑🏽🦰|🧑🏽🦱|🧑🏽🦲|🧑🏽🦳|🧑🏽🦼|🧑🏽🦽|🧑🏽🩰|🧑🏾⚕️|🧑🏾⚖️|🧑🏾✈️|🧑🏾🌾|🧑🏾🍳|🧑🏾🍼|🧑🏾🎄|🧑🏾🎓|🧑🏾🎤|🧑🏾🎨|🧑🏾🏫|🧑🏾🏭|🧑🏾💻|🧑🏾💼|🧑🏾🔧|🧑🏾🔬|🧑🏾🚀|🧑🏾🚒|🧑🏾🦯|🧑🏾🦰|🧑🏾🦱|🧑🏾🦲|🧑🏾🦳|🧑🏾🦼|🧑🏾🦽|🧑🏾🩰|🧑🏿⚕️|🧑🏿⚖️|🧑🏿✈️|🧑🏿🌾|🧑🏿🍳|🧑🏿🍼|🧑🏿🎄|🧑🏿🎓|🧑🏿🎤|🧑🏿🎨|🧑🏿🏫|🧑🏿🏭|🧑🏿💻|🧑🏿💼|🧑🏿🔧|🧑🏿🔬|🧑🏿🚀|🧑🏿🚒|🧑🏿🦯|🧑🏿🦰|🧑🏿🦱|🧑🏿🦲|🧑🏿🦳|🧑🏿🦼|🧑🏿🦽|🧑🏿🩰|🧔🏻♀️|🧔🏻♂️|🧔🏼♀️|🧔🏼♂️|🧔🏽♀️|🧔🏽♂️|🧔🏾♀️|🧔🏾♂️|🧔🏿♀️|🧔🏿♂️|🧖🏻♀️|🧖🏻♂️|🧖🏼♀️|🧖🏼♂️|🧖🏽♀️|🧖🏽♂️|🧖🏾♀️|🧖🏾♂️|🧖🏿♀️|🧖🏿♂️|🧗🏻♀️|🧗🏻♂️|🧗🏼♀️|🧗🏼♂️|🧗🏽♀️|🧗🏽♂️|🧗🏾♀️|🧗🏾♂️|🧗🏿♀️|🧗🏿♂️|🧘🏻♀️|🧘🏻♂️|🧘🏼♀️|🧘🏼♂️|🧘🏽♀️|🧘🏽♂️|🧘🏾♀️|🧘🏾♂️|🧘🏿♀️|🧘🏿♂️|🧙🏻♀️|🧙🏻♂️|🧙🏼♀️|🧙🏼♂️|🧙🏽♀️|🧙🏽♂️|🧙🏾♀️|🧙🏾♂️|🧙🏿♀️|🧙🏿♂️|🧚🏻♀️|🧚🏻♂️|🧚🏼♀️|🧚🏼♂️|🧚🏽♀️|🧚🏽♂️|🧚🏾♀️|🧚🏾♂️|🧚🏿♀️|🧚🏿♂️|🧛🏻♀️|🧛🏻♂️|🧛🏼♀️|🧛🏼♂️|🧛🏽♀️|🧛🏽♂️|🧛🏾♀️|🧛🏾♂️|🧛🏿♀️|🧛🏿♂️|🧜🏻♀️|🧜🏻♂️|🧜🏼♀️|🧜🏼♂️|🧜🏽♀️|🧜🏽♂️|🧜🏾♀️|🧜🏾♂️|🧜🏿♀️|🧜🏿♂️|🧝🏻♀️|🧝🏻♂️|🧝🏼♀️|🧝🏼♂️|🧝🏽♀️|🧝🏽♂️|🧝🏾♀️|🧝🏾♂️|🧝🏿♀️|🧝🏿♂️|⛹🏻♀️|⛹🏻♂️|⛹🏼♀️|⛹🏼♂️|⛹🏽♀️|⛹🏽♂️|⛹🏾♀️|⛹🏾♂️|⛹🏿♀️|⛹🏿♂️|🏋️♀️|🏋️♂️|🏌️♀️|🏌️♂️|🏳️⚧️|🏳️🌈|🕵️♀️|🕵️♂️|😶🌫️|⛓️💥|⛹️♀️|⛹️♂️|❤️🔥|❤️🩹|🍄🟫|🍋🟩|🏃♀️|🏃♂️|🏃➡️|🏄♀️|🏄♂️|🏊♀️|🏊♂️|🏴☠️|🐕🦺|🐦🔥|🐻❄️|👨⚕️|👨⚖️|👨✈️|👨🌾|👨🍳|👨🍼|👨🎓|👨🎤|👨🎨|👨🏫|👨🏭|👨👦|👨👧|👨💻|👨💼|👨🔧|👨🔬|👨🚀|👨🚒|👨🦯|👨🦰|👨🦱|👨🦲|👨🦳|👨🦼|👨🦽|👩⚕️|👩⚖️|👩✈️|👩🌾|👩🍳|👩🍼|👩🎓|👩🎤|👩🎨|👩🏫|👩🏭|👩👦|👩👧|👩💻|👩💼|👩🔧|👩🔬|👩🚀|👩🚒|👩🦯|👩🦰|👩🦱|👩🦲|👩🦳|👩🦼|👩🦽|👮♀️|👮♂️|👯♀️|👯♂️|👰♀️|👰♂️|👱♀️|👱♂️|👳♀️|👳♂️|👷♀️|👷♂️|💁♀️|💁♂️|💂♀️|💂♂️|💆♀️|💆♂️|💇♀️|💇♂️|😮💨|😵💫|🙂↔️|🙂↕️|🙅♀️|🙅♂️|🙆♀️|🙆♂️|🙇♀️|🙇♂️|🙋♀️|🙋♂️|🙍♀️|🙍♂️|🙎♀️|🙎♂️|🚣♀️|🚣♂️|🚴♀️|🚴♂️|🚵♀️|🚵♂️|🚶♀️|🚶♂️|🚶➡️|🤦♀️|🤦♂️|🤵♀️|🤵♂️|🤷♀️|🤷♂️|🤸♀️|🤸♂️|🤹♀️|🤹♂️|🤼♀️|🤼♂️|🤽♀️|🤽♂️|🤾♀️|🤾♂️|🦸♀️|🦸♂️|🦹♀️|🦹♂️|🧍♀️|🧍♂️|🧎♀️|🧎♂️|🧎➡️|🧏♀️|🧏♂️|🧑⚕️|🧑⚖️|🧑✈️|🧑🌾|🧑🍳|🧑🍼|🧑🎄|🧑🎓|🧑🎤|🧑🎨|🧑🏫|🧑🏭|🧑💻|🧑💼|🧑🔧|🧑🔬|🧑🚀|🧑🚒|🧑🦯|🧑🦰|🧑🦱|🧑🦲|🧑🦳|🧑🦼|🧑🦽|🧑🧒|🧑🩰|🧔♀️|🧔♂️|🧖♀️|🧖♂️|🧗♀️|🧗♂️|🧘♀️|🧘♂️|🧙♀️|🧙♂️|🧚♀️|🧚♂️|🧛♀️|🧛♂️|🧜♀️|🧜♂️|🧝♀️|🧝♂️|🧞♀️|🧞♂️|🧟♀️|🧟♂️|\*️⃣|🇦🇨|🇦🇩|🇦🇪|🇦🇫|🇦🇬|🇦🇮|🇦🇱|🇦🇲|🇦🇴|🇦🇶|🇦🇷|🇦🇸|🇦🇹|🇦🇺|🇦🇼|🇦🇽|🇦🇿|🇧🇦|🇧🇧|🇧🇩|🇧🇪|🇧🇫|🇧🇬|🇧🇭|🇧🇮|🇧🇯|🇧🇱|🇧🇲|🇧🇳|🇧🇴|🇧🇶|🇧🇷|🇧🇸|🇧🇹|🇧🇻|🇧🇼|🇧🇾|🇧🇿|🇨🇦|🇨🇨|🇨🇩|🇨🇫|🇨🇬|🇨🇭|🇨🇮|🇨🇰|🇨🇱|🇨🇲|🇨🇳|🇨🇴|🇨🇵|🇨🇶|🇨🇷|🇨🇺|🇨🇻|🇨🇼|🇨🇽|🇨🇾|🇨🇿|🇩🇪|🇩🇬|🇩🇯|🇩🇰|🇩🇲|🇩🇴|🇩🇿|🇪🇦|🇪🇨|🇪🇪|🇪🇬|🇪🇭|🇪🇷|🇪🇸|🇪🇹|🇪🇺|🇫🇮|🇫🇯|🇫🇰|🇫🇲|🇫🇴|🇫🇷|🇬🇦|🇬🇧|🇬🇩|🇬🇪|🇬🇫|🇬🇬|🇬🇭|🇬🇮|🇬🇱|🇬🇲|🇬🇳|🇬🇵|🇬🇶|🇬🇷|🇬🇸|🇬🇹|🇬🇺|🇬🇼|🇬🇾|🇭🇰|🇭🇲|🇭🇳|🇭🇷|🇭🇹|🇭🇺|🇮🇨|🇮🇩|🇮🇪|🇮🇱|🇮🇲|🇮🇳|🇮🇴|🇮🇶|🇮🇷|🇮🇸|🇮🇹|🇯🇪|🇯🇲|🇯🇴|🇯🇵|🇰🇪|🇰🇬|🇰🇭|🇰🇮|🇰🇲|🇰🇳|🇰🇵|🇰🇷|🇰🇼|🇰🇾|🇰🇿|🇱🇦|🇱🇧|🇱🇨|🇱🇮|🇱🇰|🇱🇷|🇱🇸|🇱🇹|🇱🇺|🇱🇻|🇱🇾|🇲🇦|🇲🇨|🇲🇩|🇲🇪|🇲🇫|🇲🇬|🇲🇭|🇲🇰|🇲🇱|🇲🇲|🇲🇳|🇲🇴|🇲🇵|🇲🇶|🇲🇷|🇲🇸|🇲🇹|🇲🇺|🇲🇻|🇲🇼|🇲🇽|🇲🇾|🇲🇿|🇳🇦|🇳🇨|🇳🇪|🇳🇫|🇳🇬|🇳🇮|🇳🇱|🇳🇴|🇳🇵|🇳🇷|🇳🇺|🇳🇿|🇴🇲|🇵🇦|🇵🇪|🇵🇫|🇵🇬|🇵🇭|🇵🇰|🇵🇱|🇵🇲|🇵🇳|🇵🇷|🇵🇸|🇵🇹|🇵🇼|🇵🇾|🇶🇦|🇷🇪|🇷🇴|🇷🇸|🇷🇺|🇷🇼|🇸🇦|🇸🇧|🇸🇨|🇸🇩|🇸🇪|🇸🇬|🇸🇭|🇸🇮|🇸🇯|🇸🇰|🇸🇱|🇸🇲|🇸🇳|🇸🇴|🇸🇷|🇸🇸|🇸🇹|🇸🇻|🇸🇽|🇸🇾|🇸🇿|🇹🇦|🇹🇨|🇹🇩|🇹🇫|🇹🇬|🇹🇭|🇹🇯|🇹🇰|🇹🇱|🇹🇲|🇹🇳|🇹🇴|🇹🇷|🇹🇹|🇹🇻|🇹🇼|🇹🇿|🇺🇦|🇺🇬|🇺🇲|🇺🇳|🇺🇸|🇺🇾|🇺🇿|🇻🇦|🇻🇨|🇻🇪|🇻🇬|🇻🇮|🇻🇳|🇻🇺|🇼🇫|🇼🇸|🇽🇰|🇾🇪|🇾🇹|🇿🇦|🇿🇲|🇿🇼|🎅🏻|🎅🏼|🎅🏽|🎅🏾|🎅🏿|🏂🏻|🏂🏼|🏂🏽|🏂🏾|🏂🏿|🏃🏻|🏃🏼|🏃🏽|🏃🏾|🏃🏿|🏄🏻|🏄🏼|🏄🏽|🏄🏾|🏄🏿|🏇🏻|🏇🏼|🏇🏽|🏇🏾|🏇🏿|🏊🏻|🏊🏼|🏊🏽|🏊🏾|🏊🏿|🏋🏻|🏋🏼|🏋🏽|🏋🏾|🏋🏿|🏌🏻|🏌🏼|🏌🏽|🏌🏾|🏌🏿|🐈⬛|🐦⬛|👂🏻|👂🏼|👂🏽|👂🏾|👂🏿|👃🏻|👃🏼|👃🏽|👃🏾|👃🏿|👆🏻|👆🏼|👆🏽|👆🏾|👆🏿|👇🏻|👇🏼|👇🏽|👇🏾|👇🏿|👈🏻|👈🏼|👈🏽|👈🏾|👈🏿|👉🏻|👉🏼|👉🏽|👉🏾|👉🏿|👊🏻|👊🏼|👊🏽|👊🏾|👊🏿|👋🏻|👋🏼|👋🏽|👋🏾|👋🏿|👌🏻|👌🏼|👌🏽|👌🏾|👌🏿|👍🏻|👍🏼|👍🏽|👍🏾|👍🏿|👎🏻|👎🏼|👎🏽|👎🏾|👎🏿|👏🏻|👏🏼|👏🏽|👏🏾|👏🏿|👐🏻|👐🏼|👐🏽|👐🏾|👐🏿|👦🏻|👦🏼|👦🏽|👦🏾|👦🏿|👧🏻|👧🏼|👧🏽|👧🏾|👧🏿|👨🏻|👨🏼|👨🏽|👨🏾|👨🏿|👩🏻|👩🏼|👩🏽|👩🏾|👩🏿|👫🏻|👫🏼|👫🏽|👫🏾|👫🏿|👬🏻|👬🏼|👬🏽|👬🏾|👬🏿|👭🏻|👭🏼|👭🏽|👭🏾|👭🏿|👮🏻|👮🏼|👮🏽|👮🏾|👮🏿|👯🏻|👯🏼|👯🏽|👯🏾|👯🏿|👰🏻|👰🏼|👰🏽|👰🏾|👰🏿|👱🏻|👱🏼|👱🏽|👱🏾|👱🏿|👲🏻|👲🏼|👲🏽|👲🏾|👲🏿|👳🏻|👳🏼|👳🏽|👳🏾|👳🏿|👴🏻|👴🏼|👴🏽|👴🏾|👴🏿|👵🏻|👵🏼|👵🏽|👵🏾|👵🏿|👶🏻|👶🏼|👶🏽|👶🏾|👶🏿|👷🏻|👷🏼|👷🏽|👷🏾|👷🏿|👸🏻|👸🏼|👸🏽|👸🏾|👸🏿|👼🏻|👼🏼|👼🏽|👼🏾|👼🏿|💁🏻|💁🏼|💁🏽|💁🏾|💁🏿|💂🏻|💂🏼|💂🏽|💂🏾|💂🏿|💃🏻|💃🏼|💃🏽|💃🏾|💃🏿|💅🏻|💅🏼|💅🏽|💅🏾|💅🏿|💆🏻|💆🏼|💆🏽|💆🏾|💆🏿|💇🏻|💇🏼|💇🏽|💇🏾|💇🏿|💏🏻|💏🏼|💏🏽|💏🏾|💏🏿|💑🏻|💑🏼|💑🏽|💑🏾|💑🏿|💪🏻|💪🏼|💪🏽|💪🏾|💪🏿|🕴🏻|🕴🏼|🕴🏽|🕴🏾|🕴🏿|🕵🏻|🕵🏼|🕵🏽|🕵🏾|🕵🏿|🕺🏻|🕺🏼|🕺🏽|🕺🏾|🕺🏿|🖐🏻|🖐🏼|🖐🏽|🖐🏾|🖐🏿|🖕🏻|🖕🏼|🖕🏽|🖕🏾|🖕🏿|🖖🏻|🖖🏼|🖖🏽|🖖🏾|🖖🏿|🙅🏻|🙅🏼|🙅🏽|🙅🏾|🙅🏿|🙆🏻|🙆🏼|🙆🏽|🙆🏾|🙆🏿|🙇🏻|🙇🏼|🙇🏽|🙇🏾|🙇🏿|🙋🏻|🙋🏼|🙋🏽|🙋🏾|🙋🏿|🙌🏻|🙌🏼|🙌🏽|🙌🏾|🙌🏿|🙍🏻|🙍🏼|🙍🏽|🙍🏾|🙍🏿|🙎🏻|🙎🏼|🙎🏽|🙎🏾|🙎🏿|🙏🏻|🙏🏼|🙏🏽|🙏🏾|🙏🏿|🚣🏻|🚣🏼|🚣🏽|🚣🏾|🚣🏿|🚴🏻|🚴🏼|🚴🏽|🚴🏾|🚴🏿|🚵🏻|🚵🏼|🚵🏽|🚵🏾|🚵🏿|🚶🏻|🚶🏼|🚶🏽|🚶🏾|🚶🏿|🛀🏻|🛀🏼|🛀🏽|🛀🏾|🛀🏿|🛌🏻|🛌🏼|🛌🏽|🛌🏾|🛌🏿|🤌🏻|🤌🏼|🤌🏽|🤌🏾|🤌🏿|🤏🏻|🤏🏼|🤏🏽|🤏🏾|🤏🏿|🤘🏻|🤘🏼|🤘🏽|🤘🏾|🤘🏿|🤙🏻|🤙🏼|🤙🏽|🤙🏾|🤙🏿|🤚🏻|🤚🏼|🤚🏽|🤚🏾|🤚🏿|🤛🏻|🤛🏼|🤛🏽|🤛🏾|🤛🏿|🤜🏻|🤜🏼|🤜🏽|🤜🏾|🤜🏿|🤝🏻|🤝🏼|🤝🏽|🤝🏾|🤝🏿|🤞🏻|🤞🏼|🤞🏽|🤞🏾|🤞🏿|🤟🏻|🤟🏼|🤟🏽|🤟🏾|🤟🏿|🤦🏻|🤦🏼|🤦🏽|🤦🏾|🤦🏿|🤰🏻|🤰🏼|🤰🏽|🤰🏾|🤰🏿|🤱🏻|🤱🏼|🤱🏽|🤱🏾|🤱🏿|🤲🏻|🤲🏼|🤲🏽|🤲🏾|🤲🏿|🤳🏻|🤳🏼|🤳🏽|🤳🏾|🤳🏿|🤴🏻|🤴🏼|🤴🏽|🤴🏾|🤴🏿|🤵🏻|🤵🏼|🤵🏽|🤵🏾|🤵🏿|🤶🏻|🤶🏼|🤶🏽|🤶🏾|🤶🏿|🤷🏻|🤷🏼|🤷🏽|🤷🏾|🤷🏿|🤸🏻|🤸🏼|🤸🏽|🤸🏾|🤸🏿|🤹🏻|🤹🏼|🤹🏽|🤹🏾|🤹🏿|🤼🏻|🤼🏼|🤼🏽|🤼🏾|🤼🏿|🤽🏻|🤽🏼|🤽🏽|🤽🏾|🤽🏿|🤾🏻|🤾🏼|🤾🏽|🤾🏾|🤾🏿|🥷🏻|🥷🏼|🥷🏽|🥷🏾|🥷🏿|🦵🏻|🦵🏼|🦵🏽|🦵🏾|🦵🏿|🦶🏻|🦶🏼|🦶🏽|🦶🏾|🦶🏿|🦸🏻|🦸🏼|🦸🏽|🦸🏾|🦸🏿|🦹🏻|🦹🏼|🦹🏽|🦹🏾|🦹🏿|🦻🏻|🦻🏼|🦻🏽|🦻🏾|🦻🏿|🧍🏻|🧍🏼|🧍🏽|🧍🏾|🧍🏿|🧎🏻|🧎🏼|🧎🏽|🧎🏾|🧎🏿|🧏🏻|🧏🏼|🧏🏽|🧏🏾|🧏🏿|🧑🏻|🧑🏼|🧑🏽|🧑🏾|🧑🏿|🧒🏻|🧒🏼|🧒🏽|🧒🏾|🧒🏿|🧓🏻|🧓🏼|🧓🏽|🧓🏾|🧓🏿|🧔🏻|🧔🏼|🧔🏽|🧔🏾|🧔🏿|🧕🏻|🧕🏼|🧕🏽|🧕🏾|🧕🏿|🧖🏻|🧖🏼|🧖🏽|🧖🏾|🧖🏿|🧗🏻|🧗🏼|🧗🏽|🧗🏾|🧗🏿|🧘🏻|🧘🏼|🧘🏽|🧘🏾|🧘🏿|🧙🏻|🧙🏼|🧙🏽|🧙🏾|🧙🏿|🧚🏻|🧚🏼|🧚🏽|🧚🏾|🧚🏿|🧛🏻|🧛🏼|🧛🏽|🧛🏾|🧛🏿|🧜🏻|🧜🏼|🧜🏽|🧜🏾|🧜🏿|🧝🏻|🧝🏼|🧝🏽|🧝🏾|🧝🏿|🫃🏻|🫃🏼|🫃🏽|🫃🏾|🫃🏿|🫄🏻|🫄🏼|🫄🏽|🫄🏾|🫄🏿|🫅🏻|🫅🏼|🫅🏽|🫅🏾|🫅🏿|🫰🏻|🫰🏼|🫰🏽|🫰🏾|🫰🏿|🫱🏻|🫱🏼|🫱🏽|🫱🏾|🫱🏿|🫲🏻|🫲🏼|🫲🏽|🫲🏾|🫲🏿|🫳🏻|🫳🏼|🫳🏽|🫳🏾|🫳🏿|🫴🏻|🫴🏼|🫴🏽|🫴🏾|🫴🏿|🫵🏻|🫵🏼|🫵🏽|🫵🏾|🫵🏿|🫶🏻|🫶🏼|🫶🏽|🫶🏾|🫶🏿|🫷🏻|🫷🏼|🫷🏽|🫷🏾|🫷🏿|🫸🏻|🫸🏼|🫸🏽|🫸🏾|🫸🏿|#️⃣|0️⃣|1️⃣|2️⃣|3️⃣|4️⃣|5️⃣|6️⃣|7️⃣|8️⃣|9️⃣|☝🏻|☝🏼|☝🏽|☝🏾|☝🏿|⛹🏻|⛹🏼|⛹🏽|⛹🏾|⛹🏿|✊🏻|✊🏼|✊🏽|✊🏾|✊🏿|✋🏻|✋🏼|✋🏽|✋🏾|✋🏿|✌🏻|✌🏼|✌🏽|✌🏾|✌🏿|✍🏻|✍🏼|✍🏽|✍🏾|✍🏿|🅰️|🅱️|🅾️|🅿️|🈂️|🈷️|🌡️|🌤️|🌥️|🌦️|🌧️|🌨️|🌩️|🌪️|🌫️|🌬️|🌶️|🍽️|🎖️|🎗️|🎙️|🎚️|🎛️|🎞️|🎟️|🏋️|🏌️|🏍️|🏎️|🏔️|🏕️|🏖️|🏗️|🏘️|🏙️|🏚️|🏛️|🏜️|🏝️|🏞️|🏟️|🏳️|🏵️|🏷️|🐿️|👁️|📽️|🕉️|🕊️|🕯️|🕰️|🕳️|🕴️|🕵️|🕶️|🕷️|🕸️|🕹️|🖇️|🖊️|🖋️|🖌️|🖍️|🖐️|🖥️|🖨️|🖱️|🖲️|🖼️|🗂️|🗃️|🗄️|🗑️|🗒️|🗓️|🗜️|🗝️|🗞️|🗡️|🗣️|🗨️|🗯️|🗳️|🗺️|🛋️|🛍️|🛎️|🛏️|🛠️|🛡️|🛢️|🛣️|🛤️|🛥️|🛩️|🛰️|🛳️|©️|®️|‼️|⁉️|™️|ℹ️|↔️|↕️|↖️|↗️|↘️|↙️|↩️|↪️|⌨️|⏏️|⏭️|⏮️|⏯️|⏱️|⏲️|⏸️|⏹️|⏺️|Ⓜ️|▪️|▫️|▶️|◀️|◻️|◼️|☀️|☁️|☂️|☃️|☄️|☎️|☑️|☘️|☝️|☠️|☢️|☣️|☦️|☪️|☮️|☯️|☸️|☹️|☺️|♀️|♂️|♟️|♠️|♣️|♥️|♦️|♨️|♻️|♾️|⚒️|⚔️|⚕️|⚖️|⚗️|⚙️|⚛️|⚜️|⚠️|⚧️|⚰️|⚱️|⛈️|⛏️|⛑️|⛓️|⛩️|⛰️|⛱️|⛴️|⛷️|⛸️|⛹️|✂️|✈️|✉️|✌️|✍️|✏️|✒️|✔️|✖️|✝️|✡️|✳️|✴️|❄️|❇️|❣️|❤️|➡️|⤴️|⤵️|⬅️|⬆️|⬇️|〰️|〽️|㊗️|㊙️|[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55\u{1F004}\u{1F0CF}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F201}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F236}\u{1F238}-\u{1F23A}\u{1F250}\u{1F251}\u{1F300}-\u{1F320}\u{1F32D}-\u{1F335}\u{1F337}-\u{1F37C}\u{1F37E}-\u{1F393}\u{1F3A0}-\u{1F3CA}\u{1F3CF}-\u{1F3D3}\u{1F3E0}-\u{1F3F0}\u{1F3F4}\u{1F3F8}-\u{1F43E}\u{1F440}\u{1F442}-\u{1F4FC}\u{1F4FF}-\u{1F53D}\u{1F54B}-\u{1F54E}\u{1F550}-\u{1F567}\u{1F57A}\u{1F595}\u{1F596}\u{1F5A4}\u{1F5FB}-\u{1F64F}\u{1F680}-\u{1F6C5}\u{1F6CC}\u{1F6D0}-\u{1F6D2}\u{1F6D5}-\u{1F6D8}\u{1F6DC}-\u{1F6DF}\u{1F6EB}\u{1F6EC}\u{1F6F4}-\u{1F6FC}\u{1F7E0}-\u{1F7EB}\u{1F7F0}\u{1F90C}-\u{1F93A}\u{1F93C}-\u{1F945}\u{1F947}-\u{1F9FF}\u{1FA70}-\u{1FA7C}\u{1FA80}-\u{1FA8A}\u{1FA8E}-\u{1FAC6}\u{1FAC8}\u{1FACD}-\u{1FADC}\u{1FADF}-\u{1FAEA}\u{1FAEF}-\u{1FAF8}])/ug).length;
|
||
}
|
||
} else if (content == null) {
|
||
content = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.Fragment, {
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("i", {
|
||
className: "material-icons gray",
|
||
children: "block"
|
||
}, void 0, false), " ", (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("i", {
|
||
className: "gray",
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "message_deleted",
|
||
defaultMessage: "message deleted",
|
||
description: "Placeholder shown in the timeline where a message was recalled or deleted"
|
||
}, void 0, false)
|
||
}, void 0, false)]
|
||
}, void 0, true);
|
||
} else {
|
||
content = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.Fragment, {
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("i", {
|
||
className: "material-icons gray",
|
||
children: "warning_amber"
|
||
}, void 0, false), " ", (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("i", {
|
||
className: "gray",
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "invalid_content",
|
||
defaultMessage: "invalid content",
|
||
description: "Shown when the message is unreadable"
|
||
}, void 0, false)
|
||
}, void 0, false)]
|
||
}, void 0, true);
|
||
}
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("li", {
|
||
ref: this.props.innerRef,
|
||
className: sideClass,
|
||
children: [this.props.selectMode ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("i", {
|
||
className: 'material-icons select-checkbox' + (this.props.selected ? ' blue' : ''),
|
||
onClick: () => this.props.onToggleSelect(this.props.seq),
|
||
children: this.props.selected ? 'check_circle' : 'radio_button_unchecked'
|
||
}, void 0, false) : null, this.props.isGroup && this.props.response ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("div", {
|
||
className: "avatar-box",
|
||
children: fullDisplay ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_5__["default"], {
|
||
authorizeURL: this.props.tinode.authorizeURL,
|
||
topic: this.props.userFrom,
|
||
title: this.props.userName,
|
||
avatar: avatar
|
||
}, void 0, false) : null
|
||
}, void 0, false) : null, isFailed && failReason ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("i", {
|
||
className: "material-icons red message-fail-icon",
|
||
children: "error"
|
||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("div", {
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("div", {
|
||
className: bubbleClass,
|
||
children: [showVoiceUnreadDot ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("i", {
|
||
className: "voice-unread-dot"
|
||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("div", {
|
||
className: "content-meta",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("div", {
|
||
className: textSizeClass,
|
||
children: [invitationCard || bundleCard || content, attachments]
|
||
}, void 0, true), this.props.timestamp ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)(_received_marker_jsx__WEBPACK_IMPORTED_MODULE_6__["default"], {
|
||
edited: this.props.edited,
|
||
timestamp: this.props.timestamp,
|
||
received: this.props.received
|
||
}, void 0, false) : null]
|
||
}, void 0, true), this.props.showContextMenu && !this.props.selectMode ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("span", {
|
||
className: "menuTrigger",
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: this.handleContextClick,
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "expand_more"
|
||
}, void 0, false)
|
||
}, void 0, false)
|
||
}, void 0, false) : null]
|
||
}, void 0, true), failReason, fullDisplay ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("div", {
|
||
className: "author",
|
||
children: [this.props.userName || (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("i", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "user_not_found",
|
||
defaultMessage: "Not found",
|
||
description: "In place of a user's full name when the user is not found."
|
||
}, void 0, false)
|
||
}, void 0, false), this.props.senderRole == 'owner' ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("em", {
|
||
className: "role-badge owner",
|
||
children: "群主"
|
||
}, void 0, false) : this.props.senderRole == 'admin' ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)("em", {
|
||
className: "role-badge admin",
|
||
children: "管理员"
|
||
}, void 0, false) : null]
|
||
}, void 0, true) : null]
|
||
}, void 0, true)]
|
||
}, void 0, true);
|
||
}
|
||
}
|
||
;
|
||
const IntlChatMessage = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.injectIntl)(BaseChatMessage);
|
||
const ChatMessage = react__WEBPACK_IMPORTED_MODULE_0___default().forwardRef((props, ref) => (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_10__.jsxDEV)(IntlChatMessage, {
|
||
innerRef: ref,
|
||
...props
|
||
}, void 0, false));
|
||
/* harmony default export */ __webpack_exports__["default"] = (ChatMessage);
|
||
|
||
/***/ }),
|
||
|
||
/***/ "./src/widgets/forward-bundle.jsx":
|
||
/*!****************************************!*\
|
||
!*** ./src/widgets/forward-bundle.jsx ***!
|
||
\****************************************/
|
||
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||
|
||
__webpack_require__.r(__webpack_exports__);
|
||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||
/* harmony export */ "default": function() { return /* binding */ ForwardBundle; }
|
||
/* harmony export */ });
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
||
/* harmony import */ var react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react/jsx-dev-runtime */ "./node_modules/react/jsx-dev-runtime.js");
|
||
|
||
|
||
function senderName(tinode, from) {
|
||
const raw = (from || '').trim();
|
||
if (!raw) return '';
|
||
const uid = raw.startsWith('usr') ? raw : 'usr' + raw;
|
||
const topic = tinode && tinode.getTopic ? tinode.getTopic(uid) : null;
|
||
const pub = topic && topic.public;
|
||
return pub && (pub.fn || pub.name) || '';
|
||
}
|
||
const TYPE_LABEL = {
|
||
image: '[图片]',
|
||
video: '[视频]',
|
||
voice: '[语音]',
|
||
file: '[文件]',
|
||
link: '[链接]'
|
||
};
|
||
function lineText(entry) {
|
||
if (!entry) return '';
|
||
const summary = (entry.summary || entry.text || '').trim();
|
||
if (summary) return summary;
|
||
return TYPE_LABEL[entry.type] || '';
|
||
}
|
||
class ForwardBundle extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||
constructor(props) {
|
||
super(props);
|
||
this.state = {
|
||
expanded: false
|
||
};
|
||
this.handleToggle = this.handleToggle.bind(this);
|
||
}
|
||
handleToggle(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
this.setState(s => ({
|
||
expanded: !s.expanded
|
||
}));
|
||
}
|
||
render() {
|
||
const c = this.props.content || {};
|
||
const count = Number(c.item_count) || (Array.isArray(c.items) ? c.items.length : 0);
|
||
const rows = this.state.expanded ? c.items || [] : c.preview || [];
|
||
const comment = (c.comment || '').trim();
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)("div", {
|
||
className: "forward-bundle",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)("div", {
|
||
className: "forward-bundle-title",
|
||
children: c.title || '聊天记录'
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)("ul", {
|
||
className: "forward-bundle-lines",
|
||
children: rows.map((entry, i) => {
|
||
const who = senderName(this.props.tinode, entry.from);
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)("li", {
|
||
children: [who ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)("b", {
|
||
children: [who, ":"]
|
||
}, void 0, true) : null, lineText(entry)]
|
||
}, (entry.seq != null ? entry.seq : i) + ':' + i, true);
|
||
})
|
||
}, void 0, false), count > rows.length || this.state.expanded ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)("button", {
|
||
type: "button",
|
||
className: "forward-bundle-more",
|
||
onClick: this.handleToggle,
|
||
children: this.state.expanded ? '收起' : `查看完整记录(共 ${count} 条)`
|
||
}, void 0, false) : (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)("div", {
|
||
className: "forward-bundle-count",
|
||
children: ["共 ", count, " 条消息"]
|
||
}, void 0, true), comment ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)("div", {
|
||
className: "forward-bundle-comment",
|
||
children: comment
|
||
}, void 0, false) : null]
|
||
}, void 0, true);
|
||
}
|
||
}
|
||
|
||
/***/ }),
|
||
|
||
/***/ "./src/widgets/group-subs.jsx":
|
||
/*!************************************!*\
|
||
!*** ./src/widgets/group-subs.jsx ***!
|
||
\************************************/
|
||
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||
|
||
__webpack_require__.r(__webpack_exports__);
|
||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||
/* harmony export */ "default": function() { return /* binding */ GroupSubs; }
|
||
/* harmony export */ });
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ "react-intl");
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
|
||
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
||
/* harmony import */ var _letter_tile_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./letter-tile.jsx */ "./src/widgets/letter-tile.jsx");
|
||
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
|
||
/* harmony import */ var react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! react/jsx-dev-runtime */ "./node_modules/react/jsx-dev-runtime.js");
|
||
|
||
|
||
|
||
|
||
|
||
|
||
class GroupSubs extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
|
||
constructor(props) {
|
||
super(props);
|
||
}
|
||
render() {
|
||
const usersOnline = [];
|
||
const totalCount = (this.props.subscribers || []).length;
|
||
const countToShow = Math.min(_config_js__WEBPACK_IMPORTED_MODULE_2__.MAX_ONLINE_IN_TOPIC, totalCount);
|
||
(this.props.subscribers || []).some(sub => {
|
||
usersOnline.push((0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("div", {
|
||
className: "avatar-box",
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||
authorizeURL: this.props.tinode.authorizeURL,
|
||
topic: sub.user,
|
||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__.makeImageUrl)(sub.public ? sub.public.photo : null) || true,
|
||
title: sub.public ? sub.public.fn : null
|
||
}, void 0, false)
|
||
}, sub.user, false));
|
||
return usersOnline.length == countToShow;
|
||
});
|
||
if (usersOnline.length == 0) {
|
||
return null;
|
||
}
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("div", {
|
||
id: "topic-users",
|
||
children: [usersOnline, " ", totalCount > countToShow ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("span", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "more_online_members",
|
||
defaultMessage: "+{overflow} more",
|
||
description: "Shown in MessagesView title bar when the number of online subscribers exceeds MAX_ONLINE_IN_TOPIC",
|
||
values: {
|
||
overflow: totalCount - countToShow
|
||
}
|
||
}, void 0, false)
|
||
}, void 0, false) : null]
|
||
}, void 0, true);
|
||
}
|
||
}
|
||
;
|
||
|
||
/***/ }),
|
||
|
||
/***/ "./src/widgets/invitation.jsx":
|
||
/*!************************************!*\
|
||
!*** ./src/widgets/invitation.jsx ***!
|
||
\************************************/
|
||
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||
|
||
__webpack_require__.r(__webpack_exports__);
|
||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||
/* harmony export */ "default": function() { return /* binding */ Invitation; }
|
||
/* harmony export */ });
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ "react-intl");
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
|
||
/* harmony import */ var react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! react/jsx-dev-runtime */ "./node_modules/react/jsx-dev-runtime.js");
|
||
|
||
|
||
|
||
class Invitation extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||
constructor(props) {
|
||
super(props);
|
||
this.handleButtonAction = this.handleButtonAction.bind(this);
|
||
}
|
||
handleButtonAction(evt, data) {
|
||
evt.preventDefault();
|
||
this.props.onAction(data);
|
||
}
|
||
render() {
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)("div", {
|
||
className: "accept-invite-panel",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)("div", {
|
||
className: "title",
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "chat_invitation",
|
||
defaultMessage: "You are invited to start a new chat. What would you like to do?",
|
||
description: "New chat invitation message: [Accept] [Ignore] [Block]."
|
||
}, void 0, false)
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)("div", {
|
||
className: "footer",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)("button", {
|
||
className: "primary",
|
||
onClick: event => {
|
||
this.handleButtonAction(event, "accept");
|
||
},
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "chat_invitation_accept",
|
||
defaultMessage: "Accept",
|
||
description: "Action [Accept] for chat invitation."
|
||
}, void 0, false)
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)("button", {
|
||
className: "secondary",
|
||
onClick: event => {
|
||
this.handleButtonAction(event, "delete");
|
||
},
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "chat_invitation_ignore",
|
||
defaultMessage: "Ignore",
|
||
description: "Action [Ignore] for chat invitation."
|
||
}, void 0, false)
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)("button", {
|
||
className: "secondary",
|
||
onClick: event => {
|
||
this.handleButtonAction(event, "block");
|
||
},
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "chat_invitation_block",
|
||
defaultMessage: "Block",
|
||
description: "Action [Block] for chat invitation."
|
||
}, void 0, false)
|
||
}, void 0, false)]
|
||
}, void 0, true)]
|
||
}, void 0, true);
|
||
}
|
||
}
|
||
;
|
||
|
||
/***/ }),
|
||
|
||
/***/ "./src/widgets/meta-message.jsx":
|
||
/*!**************************************!*\
|
||
!*** ./src/widgets/meta-message.jsx ***!
|
||
\**************************************/
|
||
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||
|
||
__webpack_require__.r(__webpack_exports__);
|
||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||
/* harmony export */ "default": function() { return /* binding */ MetaMessage; }
|
||
/* harmony export */ });
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
||
/* harmony import */ var react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react/jsx-dev-runtime */ "./node_modules/react/jsx-dev-runtime.js");
|
||
|
||
|
||
class MetaMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||
constructor(props) {
|
||
super(props);
|
||
}
|
||
render() {
|
||
let content = null;
|
||
let bubbleClass = 'bubble';
|
||
if (this.props.date) {
|
||
content = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.Fragment, {
|
||
children: this.props.date
|
||
}, void 0, false);
|
||
bubbleClass += ' date';
|
||
} else if (this.props.deleted) {
|
||
content = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)("i", {
|
||
className: "gray",
|
||
children: this.props.deletedText || '消息已删除'
|
||
}, void 0, false);
|
||
bubbleClass += ' deleted';
|
||
} else if (this.props.systemText) {
|
||
content = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)("span", {
|
||
className: "gray",
|
||
children: this.props.systemText
|
||
}, void 0, false);
|
||
bubbleClass += ' system';
|
||
}
|
||
if (!content) {
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.Fragment, {
|
||
children: null
|
||
}, void 0, false);
|
||
}
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)("li", {
|
||
className: "meta",
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)("div", {
|
||
className: bubbleClass,
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxDEV)("div", {
|
||
className: "message-content",
|
||
children: content
|
||
}, void 0, false)
|
||
}, void 0, false)
|
||
}, void 0, false);
|
||
}
|
||
}
|
||
;
|
||
|
||
/***/ }),
|
||
|
||
/***/ "./src/widgets/pinned-messages.jsx":
|
||
/*!*****************************************!*\
|
||
!*** ./src/widgets/pinned-messages.jsx ***!
|
||
\*****************************************/
|
||
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||
|
||
__webpack_require__.r(__webpack_exports__);
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ "react-intl");
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
|
||
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! tinode-sdk */ "tinode-sdk");
|
||
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__);
|
||
/* harmony import */ var _lib_formatters_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../lib/formatters.js */ "./src/lib/formatters.js");
|
||
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
||
/* harmony import */ var react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! react/jsx-dev-runtime */ "./node_modules/react/jsx-dev-runtime.js");
|
||
|
||
|
||
|
||
|
||
|
||
|
||
const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
|
||
message_not_found: {
|
||
id: 'message_not_found',
|
||
defaultMessage: 'message not found',
|
||
description: 'Pinned message is not found'
|
||
},
|
||
message_deleted: {
|
||
id: 'message_deleted',
|
||
defaultMessage: 'message deleted',
|
||
description: 'Pinned message is deleted'
|
||
}
|
||
});
|
||
class PinnedMessages extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||
constructor(props) {
|
||
super(props);
|
||
this.touchX = null;
|
||
this.touchY = null;
|
||
this.touchSurface = react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
|
||
this.getSelectedIndex = this.getSelectedIndex.bind(this);
|
||
this.handleCancel = this.handleCancel.bind(this);
|
||
this.handleSelected = this.handleSelected.bind(this);
|
||
this.handleMoveNext = this.handleMoveNext.bind(this);
|
||
this.handleMovePrev = this.handleMovePrev.bind(this);
|
||
this.handleTouchEventStart = this.handleTouchEventStart.bind(this);
|
||
this.handleTouchEventEnd = this.handleTouchEventEnd.bind(this);
|
||
this.handleTouchCancel = this.handleTouchCancel.bind(this);
|
||
}
|
||
componentDidMount() {
|
||
this.touchSurface.current.addEventListener('touchstart', this.handleTouchEventStart, {
|
||
passive: true
|
||
});
|
||
this.touchSurface.current.addEventListener('touchend', this.handleTouchEventEnd, {
|
||
passive: true
|
||
});
|
||
this.touchSurface.current.addEventListener('touchcancel', this.handleTouchCancel, {
|
||
passive: true
|
||
});
|
||
}
|
||
componentWillUnmount() {
|
||
this.touchSurface.current.removeEventListener('touchstart', this.handleTouchEventStart);
|
||
this.touchSurface.current.removeEventListener('touchend', this.handleTouchEventEnd);
|
||
this.touchSurface.current.removeEventListener('touchcancel', this.handleTouchCancel);
|
||
}
|
||
getSelectedIndex() {
|
||
const list = this.props.pins || [];
|
||
return list.length - this.props.selected - 1;
|
||
}
|
||
handleCancel(e) {
|
||
e.preventDefault();
|
||
this.props.onCancel(this.props.pins[this.getSelectedIndex()]);
|
||
}
|
||
handleSelected(e) {
|
||
e.preventDefault();
|
||
this.props.onSelected(this.props.pins[this.getSelectedIndex()]);
|
||
}
|
||
handleMoveNext(e, isTouch) {
|
||
isTouch || e.preventDefault();
|
||
e.stopPropagation();
|
||
const idx = Math.max(this.props.selected - 1, 0);
|
||
if (idx != this.props.selected) {
|
||
this.props.setSelected(idx);
|
||
}
|
||
}
|
||
handleMovePrev(e, isTouch) {
|
||
isTouch || e.preventDefault();
|
||
e.stopPropagation();
|
||
const idx = Math.min(this.props.selected + 1, (this.props.pins || []).length - 1);
|
||
if (idx != this.props.selected) {
|
||
this.props.setSelected(idx);
|
||
}
|
||
}
|
||
handleTouchEventStart(e) {
|
||
if (e.touches.length == 1) {
|
||
this.touchX = e.touches[0].clientX;
|
||
this.touchY = e.touches[0].clientY;
|
||
}
|
||
}
|
||
handleTouchEventEnd(e) {
|
||
if (this.touchX === null || e.changedTouches.length != 1) {
|
||
this.touchX = null;
|
||
return;
|
||
}
|
||
const dX = this.touchX - e.changedTouches[0].clientX;
|
||
const dY = this.touchY - e.changedTouches[0].clientY;
|
||
this.touchX = null;
|
||
if (Math.abs(dX) > Math.abs(dY) || Math.abs(dY) < _config_js__WEBPACK_IMPORTED_MODULE_4__.MIN_SWIPE_DISTANCE) {
|
||
return;
|
||
}
|
||
if (dY > 0) {
|
||
this.handleMovePrev(e, true);
|
||
} else {
|
||
this.handleMoveNext(e, true);
|
||
}
|
||
}
|
||
handleTouchCancel() {
|
||
this.touchX = null;
|
||
}
|
||
render() {
|
||
const selected = this.getSelectedIndex();
|
||
let messageShown = (this.props.messages || [])[selected];
|
||
messageShown = messageShown ? messageShown._deleted ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("i", {
|
||
className: "gray",
|
||
children: this.props.intl.formatMessage(messages.message_deleted)
|
||
}, void 0, false) : tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.format(messageShown.content, _lib_formatters_js__WEBPACK_IMPORTED_MODULE_3__.previewFormatter, {
|
||
formatMessage: this.props.intl.formatMessage.bind(this.props.intl),
|
||
authorizeURL: this.props.tinode.authorizeURL.bind(this.props.tinode)
|
||
}) : (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("i", {
|
||
className: "gray",
|
||
children: this.props.intl.formatMessage(messages.message_not_found)
|
||
}, void 0, false);
|
||
const dots = [];
|
||
this.props.pins.forEach(seq => {
|
||
const cn = dots.length == selected ? 'adot' : 'dot';
|
||
dots.push((0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("div", {
|
||
className: cn
|
||
}, seq, false));
|
||
});
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("div", {
|
||
id: "pinned-wrapper",
|
||
ref: this.touchSurface,
|
||
children: [this.props.isAdmin ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("div", {
|
||
className: "cancel",
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: this.handleCancel,
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("i", {
|
||
className: "material-icons gray",
|
||
children: "close"
|
||
}, void 0, false)
|
||
}, void 0, false)
|
||
}, void 0, false) : (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("div", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("i", {
|
||
className: "material-icons gray",
|
||
children: "push_pin"
|
||
}, void 0, false)
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("div", {
|
||
className: "pinned-scroll",
|
||
children: dots
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("div", {
|
||
className: "pinned",
|
||
onClick: this.handleSelected,
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("p", {
|
||
children: messageShown
|
||
}, void 0, false)
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("div", {
|
||
className: "pinned-menu",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("span", {
|
||
className: "menuTrigger upper",
|
||
children: selected > 0 ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: this.handleMovePrev,
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "expand_less"
|
||
}, void 0, false)
|
||
}, void 0, false) : null
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("span", {
|
||
className: "menuTrigger lower",
|
||
children: this.props.selected > 0 ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: this.handleMoveNext,
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "expand_more"
|
||
}, void 0, false)
|
||
}, void 0, false) : null
|
||
}, void 0, false)]
|
||
}, void 0, true)]
|
||
}, void 0, true);
|
||
}
|
||
}
|
||
/* harmony default export */ __webpack_exports__["default"] = ((0,react_intl__WEBPACK_IMPORTED_MODULE_1__.injectIntl)(PinnedMessages));
|
||
|
||
/***/ }),
|
||
|
||
/***/ "./src/widgets/received-marker.jsx":
|
||
/*!*****************************************!*\
|
||
!*** ./src/widgets/received-marker.jsx ***!
|
||
\*****************************************/
|
||
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||
|
||
__webpack_require__.r(__webpack_exports__);
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ "react-intl");
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
|
||
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! tinode-sdk */ "tinode-sdk");
|
||
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__);
|
||
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
|
||
/* harmony import */ var react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! react/jsx-dev-runtime */ "./node_modules/react/jsx-dev-runtime.js");
|
||
|
||
|
||
|
||
|
||
|
||
const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
|
||
message_sending: {
|
||
id: 'message_sending',
|
||
defaultMessage: 'sending...',
|
||
description: 'Message being sent, in place of time stamp'
|
||
},
|
||
message_sending_failed: {
|
||
id: 'message_sending_failed',
|
||
defaultMessage: 'failed',
|
||
description: 'Failed to send message, in place of time stamp'
|
||
},
|
||
message_edited_marker: {
|
||
id: 'message_edited_marker',
|
||
defaultMessage: ', edited',
|
||
description: 'Marker indicating that the message was edited'
|
||
}
|
||
});
|
||
class ReceivedMarker extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||
render() {
|
||
const {
|
||
formatMessage
|
||
} = this.props.intl;
|
||
let timestamp;
|
||
if (this.props.received <= tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.MESSAGE_STATUS_SENDING) {
|
||
timestamp = formatMessage(messages.message_sending);
|
||
} else if (this.props.received == tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.MESSAGE_STATUS_FAILED || this.props.received == tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.MESSAGE_STATUS_FATAL) {
|
||
timestamp = formatMessage(messages.message_sending_failed);
|
||
} else {
|
||
timestamp = this.props.timestamp.toLocaleTimeString(this.props.intl.locale, {
|
||
timeStyle: 'short'
|
||
});
|
||
}
|
||
const icon = (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_3__.deliveryMarker)(this.props.received);
|
||
const marker = icon ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxDEV)("i", {
|
||
className: 'material-icons small ' + (icon.color || ''),
|
||
children: icon.name
|
||
}, void 0, false) : null;
|
||
const edited = this.props.edited ? formatMessage(messages.message_edited_marker) : null;
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_4__.jsxDEV)("span", {
|
||
className: "timestamp",
|
||
children: [timestamp, edited, '\u00a0', marker]
|
||
}, void 0, true);
|
||
}
|
||
}
|
||
;
|
||
/* harmony default export */ __webpack_exports__["default"] = ((0,react_intl__WEBPACK_IMPORTED_MODULE_1__.injectIntl)(ReceivedMarker));
|
||
|
||
/***/ }),
|
||
|
||
/***/ "./src/widgets/send-message.jsx":
|
||
/*!**************************************!*\
|
||
!*** ./src/widgets/send-message.jsx ***!
|
||
\**************************************/
|
||
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||
|
||
__webpack_require__.r(__webpack_exports__);
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
|
||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ "react-intl");
|
||
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
|
||
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! tinode-sdk */ "tinode-sdk");
|
||
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__);
|
||
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
||
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
|
||
/* harmony import */ var _lib_formatters_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../lib/formatters.js */ "./src/lib/formatters.js");
|
||
/* harmony import */ var _lib_im_api_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../lib/im-api.js */ "./src/lib/im-api.js");
|
||
/* harmony import */ var react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! react/jsx-dev-runtime */ "./node_modules/react/jsx-dev-runtime.js");
|
||
|
||
|
||
|
||
const AudioRecorder = react__WEBPACK_IMPORTED_MODULE_0___default().lazy(_ => Promise.all(/*! import() */[__webpack_require__.e("vendors-node_modules_webm-duration-fix_lib_index_js"), __webpack_require__.e("src_widgets_audio-recorder_jsx")]).then(__webpack_require__.bind(__webpack_require__, /*! ./audio-recorder.jsx */ "./src/widgets/audio-recorder.jsx")));
|
||
|
||
|
||
|
||
|
||
|
||
const EMOJI_LIST = ['😀', '😁', '😂', '🤣', '😃', '😄', '😅', '😆', '😉', '😊', '😋', '😎', '😍', '😘', '🥰', '😗', '😙', '😚', '🙂', '🤗', '🤩', '🤔', '🤨', '😐', '😑', '😶', '🙄', '😏', '😣', '😥', '😮', '🤐', '😯', '😪', '😫', '🥱', '😴', '😌', '😛', '😜', '😝', '🤤', '😒', '😓', '😔', '😕', '🙃', '🤑', '😲', '☹️', '🙁', '😖', '😞', '😟', '😤', '😢', '😭', '😦', '😧', '😨', '😩', '🤯', '😬', '😰', '😱', '🥵', '🥶', '😳', '🤪', '😵', '😡', '😠', '🤬', '😷', '🤒', '🤕', '🤢', '🤮', '🥳', '🥴', '🥺', '🤫', '🤭', '🧐', '🤓', '😈', '👿', '👍', '👎', '👏', '🙌', '🤝', '🙏', '💪', '❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '💔', '💕', '💯', '🔥', '🎉', '🎂', '🌹', '☀️', '🌙', '⭐'];
|
||
const FAVORITE_TYPE_LABEL = {
|
||
image: '[图片]',
|
||
video: '[视频]',
|
||
voice: '[语音]',
|
||
file: '[文件]',
|
||
link: '[链接]',
|
||
text: '[文本]'
|
||
};
|
||
function favoriteSummary(item) {
|
||
if (item.type === 'image' || item.type === 'video' || item.type === 'voice') {
|
||
return FAVORITE_TYPE_LABEL[item.type];
|
||
}
|
||
const content = item.snapshot && item.snapshot.content;
|
||
if (typeof content === 'string' && content.trim()) return content;
|
||
if (content && typeof content.txt === 'string' && content.txt.trim()) return content.txt;
|
||
return FAVORITE_TYPE_LABEL[item.type] || item.type || item.favorite_id;
|
||
}
|
||
const RECENT_EMOJI_KEY = 'im-recent-emoji';
|
||
const MAX_RECENT_EMOJI = 30;
|
||
function loadRecentEmoji() {
|
||
try {
|
||
const raw = JSON.parse(localStorage.getItem(RECENT_EMOJI_KEY) || '[]');
|
||
return Array.isArray(raw) ? raw : [];
|
||
} catch (_) {
|
||
return [];
|
||
}
|
||
}
|
||
function pushRecentEmoji(emoji) {
|
||
try {
|
||
const list = [emoji].concat(loadRecentEmoji().filter(e => e !== emoji)).slice(0, MAX_RECENT_EMOJI);
|
||
localStorage.setItem(RECENT_EMOJI_KEY, JSON.stringify(list));
|
||
} catch (_) {}
|
||
}
|
||
const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
|
||
messaging_disabled: {
|
||
id: 'messaging_disabled_prompt',
|
||
defaultMessage: 'Messaging disabled',
|
||
description: 'Prompt in SendMessage in read-only topic'
|
||
},
|
||
type_new_message: {
|
||
id: 'new_message_prompt',
|
||
defaultMessage: 'New message',
|
||
description: 'Prompt in send message field'
|
||
},
|
||
add_image_caption: {
|
||
id: 'image_caption_prompt',
|
||
defaultMessage: 'Image caption',
|
||
description: 'Prompt in SendMessage for attached image'
|
||
},
|
||
file_attachment_too_large: {
|
||
id: 'file_attachment_too_large',
|
||
defaultMessage: 'The file size {size} exceeds the {limit} limit.',
|
||
description: 'Error message when attachment is too large'
|
||
},
|
||
cannot_initiate_upload: {
|
||
id: 'cannot_initiate_file_upload',
|
||
defaultMessage: 'Cannot initiate file upload.',
|
||
description: 'Generic error messagewhen attachment fails'
|
||
},
|
||
icon_title_record_voice: {
|
||
id: 'icon_title_record_voice',
|
||
defaultMessage: 'Record voice message',
|
||
description: 'Icon tool tip for recording a voice message'
|
||
},
|
||
icon_title_attach_file: {
|
||
id: 'icon_title_attach_file',
|
||
defaultMessage: 'Attach file',
|
||
description: 'Icon tool tip for attaching a file'
|
||
},
|
||
icon_title_add_image: {
|
||
id: 'icon_title_add_image',
|
||
defaultMessage: 'Add image',
|
||
description: 'Icon tool tip for attaching an image'
|
||
},
|
||
icon_title_send: {
|
||
id: 'icon_title_send',
|
||
defaultMessage: 'Send message',
|
||
description: 'Icon tool tip for sending a message'
|
||
}
|
||
});
|
||
class SendMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
|
||
constructor(props) {
|
||
super(props);
|
||
this.state = {
|
||
quote: null,
|
||
message: '',
|
||
audioRec: false,
|
||
audioAvailable: !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia),
|
||
mentionQuery: null,
|
||
mentionResults: [],
|
||
mentionHighlight: 0,
|
||
emojiOpen: false,
|
||
emojiTab: 'emoji',
|
||
recentEmoji: loadRecentEmoji(),
|
||
attachOpen: false,
|
||
cardPicker: null,
|
||
favPicker: null,
|
||
audioCancelling: false
|
||
};
|
||
this.keypressTimestamp = 0;
|
||
this.handlePasteEvent = this.handlePasteEvent.bind(this);
|
||
this.handleAttachImage = this.handleAttachImage.bind(this);
|
||
this.handleAttachFile = this.handleAttachFile.bind(this);
|
||
this.handleAttachAudio = this.handleAttachAudio.bind(this);
|
||
this.handleSend = this.handleSend.bind(this);
|
||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||
this.handleMessageTyping = this.handleMessageTyping.bind(this);
|
||
this.handleDropAttach = this.handleDropAttach.bind(this);
|
||
this.handleQuoteClick = this.handleQuoteClick.bind(this);
|
||
this.selectMention = this.selectMention.bind(this);
|
||
this.startHoldRecording = this.startHoldRecording.bind(this);
|
||
this.handleHoldMove = this.handleHoldMove.bind(this);
|
||
this.endHoldRecording = this.endHoldRecording.bind(this);
|
||
this.handleHoldVisibilityChange = this.handleHoldVisibilityChange.bind(this);
|
||
this.toggleEmojiPanel = this.toggleEmojiPanel.bind(this);
|
||
this.insertEmoji = this.insertEmoji.bind(this);
|
||
this.deleteLastChar = this.deleteLastChar.bind(this);
|
||
this.toggleAttachPanel = this.toggleAttachPanel.bind(this);
|
||
this.openCardPicker = this.openCardPicker.bind(this);
|
||
this.openFavPicker = this.openFavPicker.bind(this);
|
||
this.sendCard = this.sendCard.bind(this);
|
||
this.sendFavorite = this.sendFavorite.bind(this);
|
||
this.formatReply = this.formatReply.bind(this);
|
||
}
|
||
componentDidMount() {
|
||
if (this.messageEditArea) {
|
||
this.messageEditArea.addEventListener('paste', this.handlePasteEvent, false);
|
||
if (window.getComputedStyle(this.messageEditArea).getPropertyValue('transition-property') == 'all') {
|
||
this.messageEditArea.focus();
|
||
}
|
||
}
|
||
this.setState({
|
||
quote: this.formatReply()
|
||
});
|
||
}
|
||
componentWillUnmount() {
|
||
if (this.messageEditArea) {
|
||
this.messageEditArea.removeEventListener('paste', this.handlePasteEvent, false);
|
||
}
|
||
if (this.mentionSearchTimer) {
|
||
window.clearTimeout(this.mentionSearchTimer);
|
||
}
|
||
window.removeEventListener('mousemove', this.handleHoldMove);
|
||
window.removeEventListener('mouseup', this.endHoldRecording);
|
||
window.removeEventListener('touchmove', this.handleHoldMove);
|
||
window.removeEventListener('touchend', this.endHoldRecording);
|
||
window.removeEventListener('blur', this.endHoldRecording);
|
||
document.removeEventListener('visibilitychange', this.handleHoldVisibilityChange);
|
||
}
|
||
componentDidUpdate(prevProps) {
|
||
if (this.messageEditArea) {
|
||
if (window.getComputedStyle(this.messageEditArea).getPropertyValue('transition-property') == 'all') {
|
||
this.messageEditArea.focus();
|
||
}
|
||
this.messageEditArea.style.height = '0px';
|
||
this.messageEditArea.style.height = this.messageEditArea.scrollHeight + 'px';
|
||
}
|
||
if (prevProps.topicName != this.props.topicName) {
|
||
this.setState({
|
||
message: this.props.initMessage || '',
|
||
audioRec: false,
|
||
quote: null,
|
||
mentionQuery: null,
|
||
mentionResults: [],
|
||
emojiOpen: false,
|
||
attachOpen: false,
|
||
cardPicker: null,
|
||
favPicker: null
|
||
});
|
||
} else if (!prevProps.disabled && this.props.disabled && this.state.audioRec) {
|
||
this.endHoldRecording();
|
||
this.setState({
|
||
audioRec: false
|
||
});
|
||
} else if (prevProps.initMessage != this.props.initMessage) {
|
||
const msg = this.props.initMessage || '';
|
||
this.setState({
|
||
message: msg
|
||
}, _ => {
|
||
if (this.messageEditArea) {
|
||
this.messageEditArea.scrollTop = this.messageEditArea.scrollHeight;
|
||
this.messageEditArea.setSelectionRange(msg.length, msg.length);
|
||
}
|
||
});
|
||
}
|
||
if (prevProps.reply != this.props.reply) {
|
||
this.setState({
|
||
quote: this.formatReply()
|
||
});
|
||
}
|
||
}
|
||
formatReply() {
|
||
return this.props.reply ? tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.format(this.props.reply.content, _lib_formatters_js__WEBPACK_IMPORTED_MODULE_5__.replyFormatter, {
|
||
formatMessage: this.props.intl.formatMessage.bind(this.props.intl),
|
||
authorizeURL: this.props.tinode.authorizeURL.bind(this.props.tinode)
|
||
}) : null;
|
||
}
|
||
handlePasteEvent(e) {
|
||
if (this.props.disabled) {
|
||
return;
|
||
}
|
||
if ((0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__.filePasted)(e, file => {
|
||
if (this.props.onAttachImage) {
|
||
this.props.onAttachImage(file);
|
||
}
|
||
}, file => {
|
||
if (this.props.onAttachFile) {
|
||
this.props.onAttachFile(file);
|
||
}
|
||
}, this.props.onError)) {
|
||
e.preventDefault();
|
||
}
|
||
}
|
||
handleAttachImage(e) {
|
||
if (e.target.files && e.target.files.length > 0 && this.props.onAttachImage) {
|
||
this.props.onAttachImage(e.target.files[0]);
|
||
}
|
||
e.target.value = '';
|
||
}
|
||
handleAttachFile(e) {
|
||
if (e.target.files && e.target.files.length > 0 && this.props.onAttachFile) {
|
||
this.props.onAttachFile(e.target.files[0]);
|
||
}
|
||
e.target.value = '';
|
||
}
|
||
handleDropAttach(files) {
|
||
if (files && files.length > 0 && this.props.onAttachFile) {
|
||
this.props.onAttachFile(files[0]);
|
||
}
|
||
}
|
||
handleAttachAudio(url, preview, duration) {
|
||
this.setState({
|
||
audioRec: false
|
||
});
|
||
if (this.props.onAttachAudio) {
|
||
this.props.onAttachAudio(url, preview, duration);
|
||
}
|
||
}
|
||
handleSend(e) {
|
||
e.preventDefault();
|
||
const message = this.state.message.trim();
|
||
if (message || this.props.acceptBlank || this.props.noInput) {
|
||
const result = this.props.onSendMessage(message);
|
||
if (result && typeof result.then == 'function') {
|
||
result.then(sent => {
|
||
if (sent !== false) this.setState({
|
||
message: ''
|
||
});
|
||
});
|
||
} else {
|
||
this.setState({
|
||
message: ''
|
||
});
|
||
}
|
||
}
|
||
}
|
||
handleKeyPress(e) {
|
||
if (this.state.audioRec) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
return;
|
||
}
|
||
if (this.state.mentionQuery != null) {
|
||
if (e.key === 'Escape') {
|
||
e.preventDefault();
|
||
this.setState({
|
||
mentionQuery: null,
|
||
mentionResults: []
|
||
});
|
||
return;
|
||
}
|
||
if (this.state.mentionResults.length > 0 && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
|
||
e.preventDefault();
|
||
const delta = e.key === 'ArrowDown' ? 1 : -1;
|
||
const count = this.state.mentionResults.length;
|
||
this.setState({
|
||
mentionHighlight: (this.state.mentionHighlight + delta + count) % count
|
||
});
|
||
return;
|
||
}
|
||
if (e.key === 'Enter' && this.state.mentionResults.length > 0) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
this.selectMention(this.state.mentionResults[this.state.mentionHighlight]);
|
||
return;
|
||
}
|
||
}
|
||
if (e.key === 'Enter') {
|
||
if (this.props.sendOnEnter == 'plain') {
|
||
if (!e.shiftKey) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
this.handleSend(e);
|
||
}
|
||
} else {
|
||
if (e.ctrlKey || e.metaKey) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
this.handleSend(e);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
handleMessageTyping(e) {
|
||
this.setState({
|
||
message: e.target.value
|
||
});
|
||
this.updateMentionState(e.target.value, e.target.selectionStart);
|
||
if (this.props.onMessageChange) {
|
||
this.props.onMessageChange(e.target.value);
|
||
}
|
||
if (this.props.onKeyPress) {
|
||
const now = new Date().getTime();
|
||
if (now - this.keypressTimestamp > _config_js__WEBPACK_IMPORTED_MODULE_3__.KEYPRESS_DELAY) {
|
||
this.props.onKeyPress();
|
||
this.keypressTimestamp = now;
|
||
}
|
||
}
|
||
}
|
||
updateMentionState(text, caret) {
|
||
if (!this.props.onMentionSearch || caret == null) {
|
||
return;
|
||
}
|
||
const before = text.slice(0, caret);
|
||
const match = /(?:^|\s)@([^\s@]*)$/.exec(before);
|
||
if (!match) {
|
||
if (this.state.mentionQuery != null) {
|
||
this.setState({
|
||
mentionQuery: null,
|
||
mentionResults: []
|
||
});
|
||
}
|
||
return;
|
||
}
|
||
const query = match[1];
|
||
const start = caret - query.length - 1;
|
||
this.setState({
|
||
mentionQuery: query,
|
||
mentionStart: start,
|
||
mentionHighlight: 0
|
||
});
|
||
if (this.mentionSearchTimer) {
|
||
window.clearTimeout(this.mentionSearchTimer);
|
||
}
|
||
this.mentionSearchTimer = window.setTimeout(() => {
|
||
this.props.onMentionSearch(query).then(results => {
|
||
if (this.state.mentionQuery != null) {
|
||
this.setState({
|
||
mentionResults: results || []
|
||
});
|
||
}
|
||
}).catch(() => {});
|
||
}, 200);
|
||
}
|
||
selectMention(item) {
|
||
const {
|
||
message,
|
||
mentionStart,
|
||
mentionQuery
|
||
} = this.state;
|
||
if (mentionStart == null || mentionQuery == null) {
|
||
return;
|
||
}
|
||
const name = item.group_nickname || item.display_name || item.user_id;
|
||
const insertion = '@' + name + ' ';
|
||
const end = mentionStart + 1 + mentionQuery.length;
|
||
const newMessage = message.slice(0, mentionStart) + insertion + message.slice(end);
|
||
const caretPos = mentionStart + insertion.length;
|
||
this.setState({
|
||
message: newMessage,
|
||
mentionQuery: null,
|
||
mentionResults: []
|
||
});
|
||
if (this.props.onMentionSelected) {
|
||
this.props.onMentionSelected(item.user_id, name);
|
||
}
|
||
if (this.props.onMessageChange) {
|
||
this.props.onMessageChange(newMessage);
|
||
}
|
||
requestAnimationFrame(() => {
|
||
if (this.messageEditArea) {
|
||
this.messageEditArea.focus();
|
||
this.messageEditArea.setSelectionRange(caretPos, caretPos);
|
||
}
|
||
});
|
||
}
|
||
handleQuoteClick(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
if (this.props.reply && this.props.onQuoteClick) {
|
||
const replyToSeq = this.props.reply.seq;
|
||
this.props.onQuoteClick(replyToSeq);
|
||
}
|
||
}
|
||
toggleEmojiPanel(e) {
|
||
e.preventDefault();
|
||
this.setState(s => ({
|
||
emojiOpen: !s.emojiOpen,
|
||
attachOpen: false,
|
||
audioRec: false
|
||
}));
|
||
}
|
||
insertEmoji(emoji) {
|
||
const ta = this.messageEditArea;
|
||
const start = ta ? ta.selectionStart : this.state.message.length;
|
||
const end = ta ? ta.selectionEnd : this.state.message.length;
|
||
const newMessage = this.state.message.slice(0, start) + emoji + this.state.message.slice(end);
|
||
pushRecentEmoji(emoji);
|
||
this.setState({
|
||
message: newMessage,
|
||
recentEmoji: loadRecentEmoji()
|
||
});
|
||
if (this.props.onMessageChange) {
|
||
this.props.onMessageChange(newMessage);
|
||
}
|
||
const caretPosEmoji = start + emoji.length;
|
||
requestAnimationFrame(() => {
|
||
if (this.messageEditArea) {
|
||
this.messageEditArea.focus();
|
||
this.messageEditArea.setSelectionRange(caretPosEmoji, caretPosEmoji);
|
||
}
|
||
});
|
||
}
|
||
deleteLastChar() {
|
||
const ta = this.messageEditArea;
|
||
const start = ta ? ta.selectionStart : this.state.message.length;
|
||
const end = ta ? ta.selectionEnd : this.state.message.length;
|
||
let newMessage, caretPos;
|
||
if (start !== end) {
|
||
newMessage = this.state.message.slice(0, start) + this.state.message.slice(end);
|
||
caretPos = start;
|
||
} else if (start > 0) {
|
||
const before = Array.from(this.state.message.slice(0, start));
|
||
before.pop();
|
||
newMessage = before.join('') + this.state.message.slice(start);
|
||
caretPos = before.join('').length;
|
||
} else {
|
||
return;
|
||
}
|
||
this.setState({
|
||
message: newMessage
|
||
});
|
||
if (this.props.onMessageChange) {
|
||
this.props.onMessageChange(newMessage);
|
||
}
|
||
requestAnimationFrame(() => {
|
||
if (this.messageEditArea) {
|
||
this.messageEditArea.focus();
|
||
this.messageEditArea.setSelectionRange(caretPos, caretPos);
|
||
}
|
||
});
|
||
}
|
||
toggleAttachPanel(e) {
|
||
e.preventDefault();
|
||
this.setState(s => ({
|
||
attachOpen: !s.attachOpen,
|
||
emojiOpen: false
|
||
}));
|
||
}
|
||
openCardPicker(e) {
|
||
e.preventDefault();
|
||
this.setState({
|
||
attachOpen: false,
|
||
cardPicker: {
|
||
loading: true
|
||
}
|
||
});
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_6__.getFriends)().then(data => {
|
||
this.setState({
|
||
cardPicker: {
|
||
loading: false,
|
||
items: data.items || []
|
||
}
|
||
});
|
||
}).catch(() => this.setState({
|
||
cardPicker: {
|
||
loading: false,
|
||
items: [],
|
||
error: true
|
||
}
|
||
}));
|
||
}
|
||
sendCard(item) {
|
||
this.setState({
|
||
cardPicker: null
|
||
});
|
||
const build = avatarUrl => {
|
||
const card = new tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard(item.display_name || item.user_id, avatarUrl);
|
||
tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.TheCard.setTinodeID(card, item.user_id);
|
||
this.props.onSendMessage(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.appendTheCard(null, card), undefined, undefined, 'card');
|
||
};
|
||
if (item.avatar_file_id) {
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_6__.getFileUrl)(item.avatar_file_id).then(build).catch(() => build(undefined));
|
||
} else {
|
||
build(undefined);
|
||
}
|
||
}
|
||
openFavPicker(e) {
|
||
e.preventDefault();
|
||
this.setState({
|
||
attachOpen: false,
|
||
favPicker: {
|
||
loading: true
|
||
}
|
||
});
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_6__.getFavorites)({
|
||
type: 'all'
|
||
}).then(data => {
|
||
this.setState({
|
||
favPicker: {
|
||
loading: false,
|
||
items: data.items || []
|
||
}
|
||
});
|
||
}).catch(() => this.setState({
|
||
favPicker: {
|
||
loading: false,
|
||
items: [],
|
||
error: true
|
||
}
|
||
}));
|
||
}
|
||
sendFavorite(item) {
|
||
this.setState({
|
||
favPicker: null
|
||
});
|
||
if (!this.props.topicName) {
|
||
return;
|
||
}
|
||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_6__.forwardFavorites)([item.favorite_id], [this.props.topicName]).then(result => {
|
||
const failed = (result && result.items || []).filter(r => r.status === 'failed');
|
||
if (failed.length && this.props.onError) {
|
||
this.props.onError('发送失败:' + ((0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_6__.translateReason)(failed[0].reason) || failed[0].reason || '未知原因'), 'err');
|
||
}
|
||
}).catch(err => {
|
||
if (this.props.onError) {
|
||
this.props.onError(err && err.message || '发送失败', 'err');
|
||
}
|
||
});
|
||
}
|
||
startHoldRecording(e) {
|
||
e.preventDefault();
|
||
if (this.state.audioRec) {
|
||
return;
|
||
}
|
||
this.holdStartY = 'touches' in e ? e.touches[0] && e.touches[0].clientY : e.clientY;
|
||
this.setState({
|
||
audioRec: true,
|
||
audioCancelling: false,
|
||
emojiOpen: false,
|
||
attachOpen: false
|
||
});
|
||
window.addEventListener('mousemove', this.handleHoldMove);
|
||
window.addEventListener('mouseup', this.endHoldRecording);
|
||
window.addEventListener('touchmove', this.handleHoldMove, {
|
||
passive: false
|
||
});
|
||
window.addEventListener('touchend', this.endHoldRecording);
|
||
window.addEventListener('blur', this.endHoldRecording);
|
||
document.addEventListener('visibilitychange', this.handleHoldVisibilityChange);
|
||
}
|
||
handleHoldVisibilityChange() {
|
||
if (document.hidden) {
|
||
this.endHoldRecording();
|
||
}
|
||
}
|
||
handleHoldMove(e) {
|
||
const y = 'touches' in e ? e.touches[0] && e.touches[0].clientY : e.clientY;
|
||
if (y == null || this.holdStartY == null) {
|
||
return;
|
||
}
|
||
const cancelling = this.holdStartY - y > 80;
|
||
if (cancelling !== this.state.audioCancelling) {
|
||
this.setState({
|
||
audioCancelling: cancelling
|
||
});
|
||
}
|
||
}
|
||
endHoldRecording() {
|
||
window.removeEventListener('mousemove', this.handleHoldMove);
|
||
window.removeEventListener('mouseup', this.endHoldRecording);
|
||
window.removeEventListener('touchmove', this.handleHoldMove);
|
||
window.removeEventListener('touchend', this.endHoldRecording);
|
||
window.removeEventListener('blur', this.endHoldRecording);
|
||
document.removeEventListener('visibilitychange', this.handleHoldVisibilityChange);
|
||
this.holdStartY = null;
|
||
const fakeEvent = {
|
||
preventDefault: () => {}
|
||
};
|
||
if (this.audioRecorderInstance) {
|
||
if (this.state.audioCancelling) {
|
||
this.audioRecorderInstance.handleDelete(fakeEvent);
|
||
} else {
|
||
this.audioRecorderInstance.handleDone(fakeEvent);
|
||
}
|
||
}
|
||
}
|
||
render() {
|
||
const {
|
||
formatMessage
|
||
} = this.props.intl;
|
||
const prompt = this.props.disabled ? formatMessage(messages.messaging_disabled) : this.props.messagePrompt ? formatMessage(messages[this.props.messagePrompt]) : formatMessage(messages.type_new_message);
|
||
const sendIcon = this.props.reply && this.props.reply.editing ? 'check_circle' : 'send';
|
||
const quote = this.state.quote ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
id: "reply-quote-preview",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
className: "cancel",
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.props.onCancelReply();
|
||
},
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons gray",
|
||
children: "close"
|
||
}, void 0, false)
|
||
}, void 0, false)
|
||
}, void 0, false), this.state.quote]
|
||
}, void 0, true) : null;
|
||
const audioEnabled = this.state.audioAvailable && this.props.onAttachAudio;
|
||
const mentionPanel = this.state.mentionQuery != null ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
id: "mention-picker",
|
||
children: this.state.mentionResults.length === 0 ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
className: "mention-picker-empty",
|
||
children: "无匹配成员"
|
||
}, void 0, false) : this.state.mentionResults.map((item, i) => (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
className: 'mention-picker-row' + (i === this.state.mentionHighlight ? ' active' : ''),
|
||
onMouseDown: e => {
|
||
e.preventDefault();
|
||
this.selectMention(item);
|
||
},
|
||
onMouseEnter: () => this.setState({
|
||
mentionHighlight: i
|
||
}),
|
||
children: item.group_nickname || item.display_name || item.user_id
|
||
}, item.user_id, false))
|
||
}, void 0, false) : null;
|
||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
id: "send-message-wrapper",
|
||
children: [!this.props.noInput ? quote : null, mentionPanel, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
id: "send-message-panel",
|
||
children: !this.props.disabled ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.Fragment, {
|
||
children: [this.props.noInput ? quote || (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
className: "hr thin"
|
||
}, void 0, false) : this.state.audioRec ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)(react__WEBPACK_IMPORTED_MODULE_0__.Suspense, {
|
||
fallback: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||
id: "loading_note",
|
||
defaultMessage: "Loading...",
|
||
description: "Message shown when component is loading"
|
||
}, void 0, false)
|
||
}, void 0, false),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)(AudioRecorder, {
|
||
ref: ref => {
|
||
this.audioRecorderInstance = ref;
|
||
},
|
||
holdMode: true,
|
||
cancelling: this.state.audioCancelling,
|
||
onRecordingProgress: _ => this.props.onKeyPress(true),
|
||
onDeleted: _ => this.setState({
|
||
audioRec: false
|
||
}),
|
||
onFinished: this.handleAttachAudio
|
||
}, void 0, false)
|
||
}, void 0, false) : (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("textarea", {
|
||
id: "send-message-input",
|
||
placeholder: prompt,
|
||
value: this.state.message,
|
||
onChange: this.handleMessageTyping,
|
||
onKeyDown: this.handleKeyPress,
|
||
ref: ref => {
|
||
this.messageEditArea = ref;
|
||
}
|
||
}, void 0, false), !this.props.noInput && !this.state.audioRec ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: this.toggleEmojiPanel,
|
||
title: "表情",
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons secondary",
|
||
children: this.state.emojiOpen ? 'keyboard' : 'insert_emoticon'
|
||
}, void 0, false)
|
||
}, void 0, false) : null, this.props.onAttachFile && !this.state.audioRec ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: this.toggleAttachPanel,
|
||
title: formatMessage(messages.icon_title_attach_file),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons secondary",
|
||
children: this.state.attachOpen ? 'keyboard' : 'add_circle_outline'
|
||
}, void 0, false)
|
||
}, void 0, false) : null, this.state.message || !audioEnabled ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: this.handleSend,
|
||
title: formatMessage(messages.icon_title_send),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: sendIcon
|
||
}, void 0, false)
|
||
}, void 0, false) : !this.state.audioRec ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onMouseDown: this.startHoldRecording,
|
||
onTouchStart: this.startHoldRecording,
|
||
onClick: e => e.preventDefault(),
|
||
title: formatMessage(messages.icon_title_record_voice),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "mic"
|
||
}, void 0, false)
|
||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("input", {
|
||
type: "file",
|
||
ref: ref => {
|
||
this.attachFile = ref;
|
||
},
|
||
onChange: this.handleAttachFile,
|
||
style: {
|
||
display: 'none'
|
||
}
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("input", {
|
||
type: "file",
|
||
ref: ref => {
|
||
this.attachImage = ref;
|
||
},
|
||
accept: "image/*, video/*",
|
||
onChange: this.handleAttachImage,
|
||
style: {
|
||
display: 'none'
|
||
}
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("input", {
|
||
type: "file",
|
||
ref: ref => {
|
||
this.attachCamera = ref;
|
||
},
|
||
accept: "image/*",
|
||
capture: "environment",
|
||
onChange: this.handleAttachImage,
|
||
style: {
|
||
display: 'none'
|
||
}
|
||
}, void 0, false)]
|
||
}, void 0, true) : (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
id: "writing-disabled",
|
||
children: prompt
|
||
}, void 0, false)
|
||
}, void 0, false), this.state.emojiOpen ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
id: "emoji-panel",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
id: "emoji-panel-tabs",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
className: this.state.emojiTab === 'emoji' ? 'active' : '',
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.setState({
|
||
emojiTab: 'emoji'
|
||
});
|
||
},
|
||
children: "😊"
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
className: this.state.emojiTab === 'recent' ? 'active' : '',
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.setState({
|
||
emojiTab: 'recent'
|
||
});
|
||
},
|
||
children: "♡"
|
||
}, void 0, false)]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
id: "emoji-panel-grid",
|
||
children: (this.state.emojiTab === 'emoji' ? EMOJI_LIST : this.state.recentEmoji).length === 0 ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
className: "emoji-panel-empty",
|
||
children: "还没有常用表情"
|
||
}, void 0, false) : (this.state.emojiTab === 'emoji' ? EMOJI_LIST : this.state.recentEmoji).map((emoji, i) => (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.insertEmoji(emoji);
|
||
},
|
||
children: emoji
|
||
}, emoji + i, false))
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
id: "emoji-panel-footer",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.deleteLastChar();
|
||
},
|
||
title: "删除",
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "backspace"
|
||
}, void 0, false)
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
className: 'emoji-panel-send' + (this.state.message ? ' active' : ''),
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.handleSend(e);
|
||
},
|
||
title: formatMessage(messages.icon_title_send),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: sendIcon
|
||
}, void 0, false)
|
||
}, void 0, false)]
|
||
}, void 0, true)]
|
||
}, void 0, true) : null, this.state.attachOpen ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
id: "attach-panel",
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.setState({
|
||
attachOpen: false
|
||
});
|
||
this.attachImage.click();
|
||
},
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "photo"
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("span", {
|
||
children: "照片"
|
||
}, void 0, false)]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.setState({
|
||
attachOpen: false
|
||
});
|
||
this.attachCamera.click();
|
||
},
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "photo_camera"
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("span", {
|
||
children: "拍摄"
|
||
}, void 0, false)]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
className: this.props.canStartCall ? '' : 'disabled',
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.setState({
|
||
attachOpen: false
|
||
});
|
||
if (this.props.canStartCall && this.props.onStartAudioCall) this.props.onStartAudioCall();
|
||
},
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "call"
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("span", {
|
||
children: "语音通话"
|
||
}, void 0, false)]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: this.openCardPicker,
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "badge"
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("span", {
|
||
children: "名片"
|
||
}, void 0, false)]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.setState({
|
||
attachOpen: false
|
||
});
|
||
this.attachFile.click();
|
||
},
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "attach_file"
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("span", {
|
||
children: "文件"
|
||
}, void 0, false)]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: this.openFavPicker,
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "star_border"
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("span", {
|
||
children: "收藏"
|
||
}, void 0, false)]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
className: this.props.canStartCall ? '' : 'disabled',
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.setState({
|
||
attachOpen: false
|
||
});
|
||
if (this.props.canStartCall && this.props.onStartVideoCall) this.props.onStartVideoCall();
|
||
},
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "videocam"
|
||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("span", {
|
||
children: "视频通话"
|
||
}, void 0, false)]
|
||
}, void 0, true)]
|
||
}, void 0, true) : null, this.state.cardPicker ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
className: "send-message-picker-backdrop",
|
||
onClick: () => this.setState({
|
||
cardPicker: null
|
||
}),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("section", {
|
||
onClick: e => e.stopPropagation(),
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("header", {
|
||
children: ["选择联系人", (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.setState({
|
||
cardPicker: null
|
||
});
|
||
},
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "close"
|
||
}, void 0, false)
|
||
}, void 0, false)]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
className: "send-message-picker-list",
|
||
children: this.state.cardPicker.loading ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("p", {
|
||
className: "send-message-picker-empty",
|
||
children: "加载中…"
|
||
}, void 0, false) : this.state.cardPicker.items.length === 0 ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("p", {
|
||
className: "send-message-picker-empty",
|
||
children: this.state.cardPicker.error ? '加载失败' : '暂无好友'
|
||
}, void 0, false) : this.state.cardPicker.items.map(item => (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.sendCard(item);
|
||
},
|
||
children: item.display_name || item.user_id
|
||
}, item.user_id, false))
|
||
}, void 0, false)]
|
||
}, void 0, true)
|
||
}, void 0, false) : null, this.state.favPicker ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
className: "send-message-picker-backdrop",
|
||
onClick: () => this.setState({
|
||
favPicker: null
|
||
}),
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("section", {
|
||
onClick: e => e.stopPropagation(),
|
||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("header", {
|
||
children: ["我的收藏", (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.setState({
|
||
favPicker: null
|
||
});
|
||
},
|
||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||
className: "material-icons",
|
||
children: "close"
|
||
}, void 0, false)
|
||
}, void 0, false)]
|
||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||
className: "send-message-picker-list",
|
||
children: this.state.favPicker.loading ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("p", {
|
||
className: "send-message-picker-empty",
|
||
children: "加载中…"
|
||
}, void 0, false) : this.state.favPicker.items.length === 0 ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("p", {
|
||
className: "send-message-picker-empty",
|
||
children: this.state.favPicker.error ? '加载失败' : '暂无收藏'
|
||
}, void 0, false) : this.state.favPicker.items.map(item => (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||
href: "#",
|
||
onClick: e => {
|
||
e.preventDefault();
|
||
this.sendFavorite(item);
|
||
},
|
||
children: favoriteSummary(item)
|
||
}, item.favorite_id, false))
|
||
}, void 0, false)]
|
||
}, void 0, true)
|
||
}, void 0, false) : null]
|
||
}, void 0, true);
|
||
}
|
||
}
|
||
;
|
||
/* harmony default export */ __webpack_exports__["default"] = ((0,react_intl__WEBPACK_IMPORTED_MODULE_1__.injectIntl)(SendMessage));
|
||
|
||
/***/ })
|
||
|
||
}]);
|
||
//# sourceMappingURL=src_views_messages-view_jsx.dev.js.map
|