matrix-react-sdk
Version:
SDK for matrix.org using React
653 lines (632 loc) • 113 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SendMessageComposer = exports.EDITOR_STATE_STORAGE_PREFIX = void 0;
exports.attachMentions = attachMentions;
exports.attachRelation = attachRelation;
exports.createMessageContent = createMessageContent;
exports.default = void 0;
exports.isQuickReaction = isQuickReaction;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireWildcard(require("react"));
var _matrix = require("matrix-js-sdk/src/matrix");
var _lodash = require("lodash");
var _logger = require("matrix-js-sdk/src/logger");
var _dispatcher = _interopRequireDefault(require("../../../dispatcher/dispatcher"));
var _model = _interopRequireDefault(require("../../../editor/model"));
var _serialize = require("../../../editor/serialize");
var _BasicMessageComposer = _interopRequireWildcard(require("./BasicMessageComposer"));
var _parts2 = require("../../../editor/parts");
var _EventUtils = require("../../../utils/EventUtils");
var _SendHistoryManager = _interopRequireDefault(require("../../../SendHistoryManager"));
var _SlashCommands = require("../../../SlashCommands");
var _ContentMessages = _interopRequireDefault(require("../../../ContentMessages"));
var _MatrixClientContext = require("../../../contexts/MatrixClientContext");
var _actions = require("../../../dispatcher/actions");
var _utils = require("../../../effects/utils");
var _effects = require("../../../effects");
var _MatrixClientPeg = require("../../../MatrixClientPeg");
var _KeyBindingsManager = require("../../../KeyBindingsManager");
var _SettingsStore = _interopRequireDefault(require("../../../settings/SettingsStore"));
var _sendTimePerformanceMetrics = require("../../../sendTimePerformanceMetrics");
var _RoomContext = _interopRequireWildcard(require("../../../contexts/RoomContext"));
var _position = _interopRequireDefault(require("../../../editor/position"));
var _ComposerInsertPayload = require("../../../dispatcher/payloads/ComposerInsertPayload");
var _commands = require("../../../editor/commands");
var _KeyboardShortcuts = require("../../../accessibility/KeyboardShortcuts");
var _PosthogAnalytics = require("../../../PosthogAnalytics");
var _Reply = require("../../../utils/Reply");
var _localRoom = require("../../../utils/local-room");
var _blobs = require("../../../utils/blobs");
var _HtmlUtils = require("../../../HtmlUtils");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /*
Copyright 2024 New Vector Ltd.
Copyright 2019-2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
// The prefix used when persisting editor drafts to localstorage.
const EDITOR_STATE_STORAGE_PREFIX = exports.EDITOR_STATE_STORAGE_PREFIX = "mx_cider_state_";
/**
* Build the mentions information based on the editor model (and any related events):
*
* 1. Search the model parts for room or user pills and fill in the mentions object.
* 2. If this is a reply to another event, include any user mentions from that
* (but do not include a room mention).
*
* @param sender - The Matrix ID of the user sending the event.
* @param content - The event content.
* @param model - The editor model to search for mentions, null if there is no editor.
* @param replyToEvent - The event being replied to or undefined if it is not a reply.
* @param editedContent - The content of the parent event being edited.
*/
function attachMentions(sender, content, model, replyToEvent, editedContent = null) {
// We always attach the mentions even if the home server doesn't yet support
// intentional mentions. This is safe because m.mentions is an additive change
// that should simply be ignored by incapable home servers.
// The mentions property *always* gets included to disable legacy push rules.
const mentions = content["m.mentions"] = {};
const userMentions = new Set();
let roomMention = false;
// If there's a reply, initialize the mentioned users as the sender of that
// event + any mentioned users in that event.
if (replyToEvent) {
userMentions.add(replyToEvent.sender.userId);
// TODO What do we do if the reply event *doeesn't* have this property?
// Try to fish out replies from the contents?
const userIds = replyToEvent.getContent()["m.mentions"]?.user_ids;
if (Array.isArray(userIds)) {
userIds.forEach(userId => userMentions.add(userId));
}
}
// If user provided content is available, check to see if any users are mentioned.
if (model) {
// Add any mentioned users in the current content.
for (const part of model.parts) {
if (part.type === _parts2.Type.UserPill) {
userMentions.add(part.resourceId);
} else if (part.type === _parts2.Type.AtRoomPill) {
roomMention = true;
}
}
}
// Ensure the *current* user isn't listed in the mentioned users.
userMentions.delete(sender);
// Finally, if this event is editing a previous event, only include users who
// were not previously mentioned and a room mention if the previous event was
// not a room mention.
if (editedContent) {
// First, the new event content gets the *full* set of users.
const newContent = content["m.new_content"];
const newMentions = newContent["m.mentions"] = {};
// Only include the users/room if there is any content.
if (userMentions.size) {
newMentions.user_ids = [...userMentions];
}
if (roomMention) {
newMentions.room = true;
}
// Fetch the mentions from the original event and remove any previously
// mentioned users.
const prevMentions = editedContent["m.mentions"];
if (Array.isArray(prevMentions?.user_ids)) {
prevMentions.user_ids.forEach(userId => userMentions.delete(userId));
}
// If the original event mentioned the room, nothing to do here.
if (prevMentions?.room) {
roomMention = false;
}
}
// Only include the users/room if there is any content.
if (userMentions.size) {
mentions.user_ids = [...userMentions];
}
if (roomMention) {
mentions.room = true;
}
}
// Merges favouring the given relation
function attachRelation(content, relation) {
if (relation) {
content["m.relates_to"] = _objectSpread(_objectSpread({}, content["m.relates_to"] || {}), relation);
}
}
// exported for tests
function createMessageContent(sender, model, replyToEvent, relation, permalinkCreator, includeReplyLegacyFallback = true) {
const isEmote = (0, _serialize.containsEmote)(model);
if (isEmote) {
model = (0, _serialize.stripEmoteCommand)(model);
}
if ((0, _serialize.startsWith)(model, "//")) {
model = (0, _serialize.stripPrefix)(model, "/");
}
model = (0, _serialize.unescapeMessage)(model);
const body = (0, _serialize.textSerialize)(model);
const content = {
msgtype: isEmote ? _matrix.MsgType.Emote : _matrix.MsgType.Text,
body: body
};
const formattedBody = (0, _serialize.htmlSerializeIfNeeded)(model, {
forceHTML: !!replyToEvent,
useMarkdown: _SettingsStore.default.getValue("MessageComposerInput.useMarkdown")
});
if (formattedBody) {
content.format = "org.matrix.custom.html";
content.formatted_body = formattedBody;
}
// Build the mentions property and add it to the event content.
attachMentions(sender, content, model, replyToEvent);
attachRelation(content, relation);
if (replyToEvent) {
(0, _Reply.addReplyToMessageContent)(content, replyToEvent, {
permalinkCreator,
includeLegacyFallback: includeReplyLegacyFallback
});
}
return content;
}
// exported for tests
function isQuickReaction(model) {
const parts = model.parts;
if (parts.length == 0) return false;
const text = (0, _serialize.textSerialize)(model);
// shortcut takes the form "+:emoji:" or "+ :emoji:""
// can be in 1 or 2 parts
if (parts.length <= 2) {
const hasShortcut = text.startsWith("+") || text.startsWith("+ ");
const emojiMatch = text.match(_HtmlUtils.EMOJI_REGEX);
if (hasShortcut && emojiMatch && emojiMatch.length == 1) {
return emojiMatch[0] === text.substring(1) || emojiMatch[0] === text.substring(2);
}
}
return false;
}
class SendMessageComposer extends _react.default.Component {
constructor(props, context) {
super(props, context);
(0, _defineProperty2.default)(this, "prepareToEncrypt", void 0);
(0, _defineProperty2.default)(this, "editorRef", /*#__PURE__*/(0, _react.createRef)());
(0, _defineProperty2.default)(this, "model", void 0);
(0, _defineProperty2.default)(this, "currentlyComposedEditorState", null);
(0, _defineProperty2.default)(this, "dispatcherRef", void 0);
(0, _defineProperty2.default)(this, "sendHistoryManager", void 0);
(0, _defineProperty2.default)(this, "onKeyDown", event => {
// ignore any keypress while doing IME compositions
if (this.editorRef.current?.isComposing(event)) {
return;
}
const replyingToThread = this.props.relation?.key === _matrix.THREAD_RELATION_TYPE.name;
const action = (0, _KeyBindingsManager.getKeyBindingsManager)().getMessageComposerAction(event);
switch (action) {
case _KeyboardShortcuts.KeyBindingAction.SendMessage:
this.sendMessage();
event.preventDefault();
break;
case _KeyboardShortcuts.KeyBindingAction.SelectPrevSendHistory:
case _KeyboardShortcuts.KeyBindingAction.SelectNextSendHistory:
{
// Try select composer history
const selected = this.selectSendHistory(action === _KeyboardShortcuts.KeyBindingAction.SelectPrevSendHistory);
if (selected) {
// We're selecting history, so prevent the key event from doing anything else
event.preventDefault();
}
break;
}
case _KeyboardShortcuts.KeyBindingAction.ShowStickerPicker:
{
if (!_SettingsStore.default.getValue("MessageComposerInput.showStickersButton")) {
return; // Do nothing if there is no Stickers button
}
this.props.toggleStickerPickerOpen();
event.preventDefault();
break;
}
case _KeyboardShortcuts.KeyBindingAction.EditPrevMessage:
// selection must be collapsed and caret at start
if (this.editorRef.current?.isSelectionCollapsed() && this.editorRef.current?.isCaretAtStart()) {
const events = this.context.liveTimeline?.getEvents().concat(replyingToThread ? [] : this.props.room.getPendingEvents());
const editEvent = events ? (0, _EventUtils.findEditableEvent)({
events,
isForward: false,
matrixClient: _MatrixClientPeg.MatrixClientPeg.safeGet()
}) : undefined;
if (editEvent) {
// We're selecting history, so prevent the key event from doing anything else
event.preventDefault();
_dispatcher.default.dispatch({
action: _actions.Action.EditEvent,
event: editEvent,
timelineRenderingType: this.context.timelineRenderingType
});
}
}
break;
case _KeyboardShortcuts.KeyBindingAction.CancelReplyOrEdit:
if (!!this.context.replyToEvent) {
_dispatcher.default.dispatch({
action: "reply_to_event",
event: null,
context: this.context.timelineRenderingType
});
event.preventDefault();
event.stopPropagation();
}
break;
}
});
// should save state when editor has contents or reply is open
(0, _defineProperty2.default)(this, "shouldSaveStoredEditorState", () => {
return !this.model.isEmpty || !!this.props.replyToEvent;
});
(0, _defineProperty2.default)(this, "saveStoredEditorState", () => {
if (this.shouldSaveStoredEditorState()) {
const item = _SendHistoryManager.default.createItem(this.model, this.props.replyToEvent);
localStorage.setItem(this.editorStateKey, JSON.stringify(item));
} else {
this.clearStoredEditorState();
}
});
(0, _defineProperty2.default)(this, "onAction", payload => {
// don't let the user into the composer if it is disabled - all of these branches lead
// to the cursor being in the composer
if (this.props.disabled) return;
switch (payload.action) {
case "reply_to_event":
case _actions.Action.FocusSendMessageComposer:
if ((payload.context ?? _RoomContext.TimelineRenderingType.Room) === this.context.timelineRenderingType) {
this.editorRef.current?.focus();
}
break;
case _actions.Action.ComposerInsert:
if (payload.timelineRenderingType !== this.context.timelineRenderingType) break;
if (payload.composerType !== _ComposerInsertPayload.ComposerType.Send) break;
if (payload.userId) {
this.editorRef.current?.insertMention(payload.userId);
} else if (payload.event) {
this.editorRef.current?.insertQuotedMessage(payload.event);
} else if (payload.text) {
this.editorRef.current?.insertPlaintext(payload.text);
}
break;
}
});
(0, _defineProperty2.default)(this, "onPaste", (event, data) => {
// Prioritize text on the clipboard over files if RTF is present as Office on macOS puts a bitmap
// in the clipboard as well as the content being copied. Modern versions of Office seem to not do this anymore.
// We check text/rtf instead of text/plain as when copy+pasting a file from Finder or Gnome Image Viewer
// it puts the filename in as text/plain which we want to ignore.
if (data.files.length && !data.types.includes("text/rtf")) {
_ContentMessages.default.sharedInstance().sendContentListToRoom(Array.from(data.files), this.props.room.roomId, this.props.relation, this.props.mxClient, this.context.timelineRenderingType);
return true; // to skip internal onPaste handler
}
// Safari `Insert from iPhone or iPad`
// data.getData("text/html") returns a string like: <img src="blob:https://...">
if (data.types.includes("text/html")) {
const imgElementStr = data.getData("text/html");
const parser = new DOMParser();
const imgDoc = parser.parseFromString(imgElementStr, "text/html");
if (imgDoc.getElementsByTagName("img").length !== 1 || !imgDoc.querySelector("img")?.src.startsWith("blob:") || imgDoc.childNodes.length !== 1) {
console.log("Failed to handle pasted content as Safari inserted content");
// Fallback to internal onPaste handler
return false;
}
const imgSrc = imgDoc.querySelector("img").src;
fetch(imgSrc).then(response => {
response.blob().then(imgBlob => {
const type = imgBlob.type;
const safetype = (0, _blobs.getBlobSafeMimeType)(type);
const ext = type.split("/")[1];
const parts = response.url.split("/");
const filename = parts[parts.length - 1];
const file = new File([imgBlob], filename + "." + ext, {
type: safetype
});
_ContentMessages.default.sharedInstance().sendContentToRoom(file, this.props.room.roomId, this.props.relation, this.props.mxClient, this.context.replyToEvent);
}, error => {
console.log(error);
});
}, error => {
console.log(error);
});
// Skip internal onPaste handler
return true;
}
return false;
});
(0, _defineProperty2.default)(this, "onChange", (selection, inputType, diff) => {
// We call this in here rather than onKeyDown as that would trip it on global shortcuts e.g. Ctrl-k also
if (!!diff) {
this.prepareToEncrypt?.();
}
this.props.onChange?.(this.model);
});
(0, _defineProperty2.default)(this, "focusComposer", () => {
this.editorRef.current?.focus();
});
if (this.props.mxClient.isCryptoEnabled() && this.props.mxClient.isRoomEncrypted(this.props.room.roomId)) {
this.prepareToEncrypt = (0, _lodash.throttle)(() => {
this.props.mxClient.getCrypto()?.prepareToEncrypt(this.props.room);
}, 60000, {
leading: true,
trailing: false
});
}
window.addEventListener("beforeunload", this.saveStoredEditorState);
const partCreator = new _parts2.CommandPartCreator(this.props.room, this.props.mxClient);
const _parts = this.restoreStoredEditorState(partCreator) || [];
this.model = new _model.default(_parts, partCreator);
this.dispatcherRef = _dispatcher.default.register(this.onAction);
this.sendHistoryManager = new _SendHistoryManager.default(this.props.room.roomId, "mx_cider_history_");
}
componentDidUpdate(prevProps) {
const replyingToThread = this.props.relation?.key === _matrix.THREAD_RELATION_TYPE.name;
const differentEventTarget = this.props.relation?.event_id !== prevProps.relation?.event_id;
const threadChanged = replyingToThread && differentEventTarget;
if (threadChanged) {
const partCreator = new _parts2.CommandPartCreator(this.props.room, this.props.mxClient);
const parts = this.restoreStoredEditorState(partCreator) || [];
this.model.reset(parts);
this.editorRef.current?.focus();
}
}
// we keep sent messages/commands in a separate history (separate from undo history)
// so you can alt+up/down in them
selectSendHistory(up) {
const delta = up ? -1 : 1;
// True if we are not currently selecting history, but composing a message
if (this.sendHistoryManager.currentIndex === this.sendHistoryManager.history.length) {
// We can't go any further - there isn't any more history, so nop.
if (!up) {
return false;
}
this.currentlyComposedEditorState = this.model.serializeParts();
} else if (this.currentlyComposedEditorState && this.sendHistoryManager.currentIndex + delta === this.sendHistoryManager.history.length) {
// True when we return to the message being composed currently
this.model.reset(this.currentlyComposedEditorState);
this.sendHistoryManager.currentIndex = this.sendHistoryManager.history.length;
return true;
}
const {
parts,
replyEventId
} = this.sendHistoryManager.getItem(delta);
_dispatcher.default.dispatch({
action: "reply_to_event",
event: replyEventId ? this.props.room.findEventById(replyEventId) : null,
context: this.context.timelineRenderingType
});
if (parts) {
this.model.reset(parts);
this.editorRef.current?.focus();
}
return true;
}
sendQuickReaction() {
const timeline = this.context.liveTimeline;
if (!timeline) return;
const events = timeline.getEvents();
const reaction = this.model.parts[1].text;
for (let i = events.length - 1; i >= 0; i--) {
if (events[i].getType() === _matrix.EventType.RoomMessage) {
let shouldReact = true;
const lastMessage = events[i];
const userId = _MatrixClientPeg.MatrixClientPeg.safeGet().getSafeUserId();
const messageReactions = this.props.room.relations.getChildEventsForEvent(lastMessage.getId(), _matrix.RelationType.Annotation, _matrix.EventType.Reaction);
// if we have already sent this reaction, don't redact but don't re-send
if (messageReactions) {
const myReactionEvents = messageReactions.getAnnotationsBySender()?.[userId] || new Set();
const myReactionKeys = [...myReactionEvents].filter(event => !event.isRedacted()).map(event => event.getRelation()?.key);
shouldReact = !myReactionKeys.includes(reaction);
}
if (shouldReact) {
_MatrixClientPeg.MatrixClientPeg.safeGet().sendEvent(lastMessage.getRoomId(), _matrix.EventType.Reaction, {
"m.relates_to": {
rel_type: _matrix.RelationType.Annotation,
event_id: lastMessage.getId(),
key: reaction
}
});
_dispatcher.default.dispatch({
action: "message_sent"
});
}
break;
}
}
}
async sendMessage() {
const model = this.model;
if (model.isEmpty) {
return;
}
const posthogEvent = {
eventName: "Composer",
isEditing: false,
messageType: "Text",
isReply: !!this.props.replyToEvent,
inThread: this.props.relation?.rel_type === _matrix.THREAD_RELATION_TYPE.name
};
if (posthogEvent.inThread && this.props.relation.event_id) {
const threadRoot = this.props.room.findEventById(this.props.relation.event_id);
posthogEvent.startsThread = threadRoot?.getThread()?.events.length === 1;
}
_PosthogAnalytics.PosthogAnalytics.instance.trackEvent(posthogEvent);
// Replace emoticon at the end of the message
if (_SettingsStore.default.getValue("MessageComposerInput.autoReplaceEmoji")) {
const indexOfLastPart = model.parts.length - 1;
const positionInLastPart = model.parts[indexOfLastPart].text.length;
this.editorRef.current?.replaceEmoticon(new _position.default(indexOfLastPart, positionInLastPart), _BasicMessageComposer.REGEX_EMOTICON);
}
const replyToEvent = this.props.replyToEvent;
let shouldSend = true;
let content = null;
if (!(0, _serialize.containsEmote)(model) && (0, _commands.isSlashCommand)(this.model)) {
const [cmd, args, commandText] = (0, _commands.getSlashCommand)(this.model);
if (cmd) {
const threadId = this.props.relation?.rel_type === _matrix.THREAD_RELATION_TYPE.name ? this.props.relation?.event_id : null;
let commandSuccessful;
[content, commandSuccessful] = await (0, _commands.runSlashCommand)(_MatrixClientPeg.MatrixClientPeg.safeGet(), cmd, args, this.props.room.roomId, threadId ?? null);
if (!commandSuccessful) {
return; // errored
}
if (content && [_SlashCommands.CommandCategories.messages, _SlashCommands.CommandCategories.effects].includes(cmd.category)) {
// Attach any mentions which might be contained in the command content.
attachMentions(this.props.mxClient.getSafeUserId(), content, model, replyToEvent);
attachRelation(content, this.props.relation);
if (replyToEvent) {
(0, _Reply.addReplyToMessageContent)(content, replyToEvent, {
permalinkCreator: this.props.permalinkCreator,
// Exclude the legacy fallback for custom event types such as those used by /fireworks
includeLegacyFallback: content.msgtype?.startsWith("m.") ?? true
});
}
} else {
shouldSend = false;
}
} else {
const sendAnyway = await (0, _commands.shouldSendAnyway)(commandText);
// re-focus the composer after QuestionDialog is closed
_dispatcher.default.dispatch({
action: _actions.Action.FocusAComposer,
context: this.context.timelineRenderingType
});
// if !sendAnyway bail to let the user edit the composer and try again
if (!sendAnyway) return;
}
}
if (isQuickReaction(model)) {
shouldSend = false;
this.sendQuickReaction();
}
if (shouldSend) {
const {
roomId
} = this.props.room;
if (!content) {
content = createMessageContent(this.props.mxClient.getSafeUserId(), model, replyToEvent, this.props.relation, this.props.permalinkCreator, this.props.includeReplyLegacyFallback);
}
// don't bother sending an empty message
if (!content.body.trim()) return;
if (_SettingsStore.default.getValue("Performance.addSendMessageTimingMetadata")) {
(0, _sendTimePerformanceMetrics.decorateStartSendingTime)(content);
}
const threadId = this.props.relation?.rel_type === _matrix.THREAD_RELATION_TYPE.name ? this.props.relation.event_id : null;
const prom = (0, _localRoom.doMaybeLocalRoomAction)(roomId, actualRoomId => this.props.mxClient.sendMessage(actualRoomId, threadId ?? null, content), this.props.mxClient);
if (replyToEvent) {
// Clear reply_to_event as we put the message into the queue
// if the send fails, retry will handle resending.
_dispatcher.default.dispatch({
action: "reply_to_event",
event: null,
context: this.context.timelineRenderingType
});
}
_dispatcher.default.dispatch({
action: "message_sent"
});
_effects.CHAT_EFFECTS.forEach(effect => {
if ((0, _utils.containsEmoji)(content, effect.emojis)) {
// For initial threads launch, chat effects are disabled
// see #19731
const isNotThread = this.props.relation?.rel_type !== _matrix.THREAD_RELATION_TYPE.name;
if (isNotThread) {
_dispatcher.default.dispatch({
action: `effects.${effect.command}`
});
}
}
});
if (_SettingsStore.default.getValue("Performance.addSendMessageTimingMetadata")) {
prom.then(resp => {
(0, _sendTimePerformanceMetrics.sendRoundTripMetric)(this.props.mxClient, roomId, resp.event_id);
});
}
}
this.sendHistoryManager.save(model, replyToEvent);
// clear composer
model.reset([]);
this.editorRef.current?.clearUndoHistory();
this.editorRef.current?.focus();
this.clearStoredEditorState();
if (shouldSend && _SettingsStore.default.getValue("scrollToBottomOnMessageSent")) {
_dispatcher.default.dispatch({
action: "scroll_to_bottom",
timelineRenderingType: this.context.timelineRenderingType
});
}
}
componentWillUnmount() {
_dispatcher.default.unregister(this.dispatcherRef);
window.removeEventListener("beforeunload", this.saveStoredEditorState);
this.saveStoredEditorState();
}
get editorStateKey() {
let key = EDITOR_STATE_STORAGE_PREFIX + this.props.room.roomId;
if (this.props.relation?.rel_type === _matrix.THREAD_RELATION_TYPE.name) {
key += `_${this.props.relation.event_id}`;
}
return key;
}
clearStoredEditorState() {
localStorage.removeItem(this.editorStateKey);
}
restoreStoredEditorState(partCreator) {
const replyingToThread = this.props.relation?.key === _matrix.THREAD_RELATION_TYPE.name;
if (replyingToThread) {
return null;
}
const json = localStorage.getItem(this.editorStateKey);
if (json) {
try {
const {
parts: serializedParts,
replyEventId
} = JSON.parse(json);
const parts = serializedParts.map(p => partCreator.deserializePart(p));
if (replyEventId) {
_dispatcher.default.dispatch({
action: "reply_to_event",
event: this.props.room.findEventById(replyEventId),
context: this.context.timelineRenderingType
});
}
return parts;
} catch (e) {
_logger.logger.error(e);
}
}
return null;
}
render() {
const threadId = this.props.relation?.rel_type === _matrix.THREAD_RELATION_TYPE.name ? this.props.relation.event_id : undefined;
return /*#__PURE__*/_react.default.createElement("div", {
className: "mx_SendMessageComposer",
onClick: this.focusComposer,
onKeyDown: this.onKeyDown
}, /*#__PURE__*/_react.default.createElement(_BasicMessageComposer.default, {
onChange: this.onChange,
ref: this.editorRef,
model: this.model,
room: this.props.room,
threadId: threadId,
label: this.props.placeholder,
placeholder: this.props.placeholder,
onPaste: this.onPaste,
disabled: this.props.disabled
}));
}
}
exports.SendMessageComposer = SendMessageComposer;
(0, _defineProperty2.default)(SendMessageComposer, "contextType", _RoomContext.default);
(0, _defineProperty2.default)(SendMessageComposer, "defaultProps", {
includeReplyLegacyFallback: true
});
const SendMessageComposerWithMatrixClient = (0, _MatrixClientContext.withMatrixClientHOC)(SendMessageComposer);
var _default = exports.default = SendMessageComposerWithMatrixClient;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVhY3QiLCJfaW50ZXJvcFJlcXVpcmVXaWxkY2FyZCIsInJlcXVpcmUiLCJfbWF0cml4IiwiX2xvZGFzaCIsIl9sb2dnZXIiLCJfZGlzcGF0Y2hlciIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJfbW9kZWwiLCJfc2VyaWFsaXplIiwiX0Jhc2ljTWVzc2FnZUNvbXBvc2VyIiwiX3BhcnRzMiIsIl9FdmVudFV0aWxzIiwiX1NlbmRIaXN0b3J5TWFuYWdlciIsIl9TbGFzaENvbW1hbmRzIiwiX0NvbnRlbnRNZXNzYWdlcyIsIl9NYXRyaXhDbGllbnRDb250ZXh0IiwiX2FjdGlvbnMiLCJfdXRpbHMiLCJfZWZmZWN0cyIsIl9NYXRyaXhDbGllbnRQZWciLCJfS2V5QmluZGluZ3NNYW5hZ2VyIiwiX1NldHRpbmdzU3RvcmUiLCJfc2VuZFRpbWVQZXJmb3JtYW5jZU1ldHJpY3MiLCJfUm9vbUNvbnRleHQiLCJfcG9zaXRpb24iLCJfQ29tcG9zZXJJbnNlcnRQYXlsb2FkIiwiX2NvbW1hbmRzIiwiX0tleWJvYXJkU2hvcnRjdXRzIiwiX1Bvc3Rob2dBbmFseXRpY3MiLCJfUmVwbHkiLCJfbG9jYWxSb29tIiwiX2Jsb2JzIiwiX0h0bWxVdGlscyIsIl9nZXRSZXF1aXJlV2lsZGNhcmRDYWNoZSIsImUiLCJXZWFrTWFwIiwiciIsInQiLCJfX2VzTW9kdWxlIiwiZGVmYXVsdCIsImhhcyIsImdldCIsIm4iLCJfX3Byb3RvX18iLCJhIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IiLCJ1IiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiaSIsInNldCIsIm93bktleXMiLCJrZXlzIiwiZ2V0T3duUHJvcGVydHlTeW1ib2xzIiwibyIsImZpbHRlciIsImVudW1lcmFibGUiLCJwdXNoIiwiYXBwbHkiLCJfb2JqZWN0U3ByZWFkIiwiYXJndW1lbnRzIiwibGVuZ3RoIiwiZm9yRWFjaCIsIl9kZWZpbmVQcm9wZXJ0eTIiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzIiwiZGVmaW5lUHJvcGVydGllcyIsIkVESVRPUl9TVEFURV9TVE9SQUdFX1BSRUZJWCIsImV4cG9ydHMiLCJhdHRhY2hNZW50aW9ucyIsInNlbmRlciIsImNvbnRlbnQiLCJtb2RlbCIsInJlcGx5VG9FdmVudCIsImVkaXRlZENvbnRlbnQiLCJtZW50aW9ucyIsInVzZXJNZW50aW9ucyIsIlNldCIsInJvb21NZW50aW9uIiwiYWRkIiwidXNlcklkIiwidXNlcklkcyIsImdldENvbnRlbnQiLCJ1c2VyX2lkcyIsIkFycmF5IiwiaXNBcnJheSIsInBhcnQiLCJwYXJ0cyIsInR5cGUiLCJUeXBlIiwiVXNlclBpbGwiLCJyZXNvdXJjZUlkIiwiQXRSb29tUGlsbCIsImRlbGV0ZSIsIm5ld0NvbnRlbnQiLCJuZXdNZW50aW9ucyIsInNpemUiLCJyb29tIiwicHJldk1lbnRpb25zIiwiYXR0YWNoUmVsYXRpb24iLCJyZWxhdGlvbiIsImNyZWF0ZU1lc3NhZ2VDb250ZW50IiwicGVybWFsaW5rQ3JlYXRvciIsImluY2x1ZGVSZXBseUxlZ2FjeUZhbGxiYWNrIiwiaXNFbW90ZSIsImNvbnRhaW5zRW1vdGUiLCJzdHJpcEVtb3RlQ29tbWFuZCIsInN0YXJ0c1dpdGgiLCJzdHJpcFByZWZpeCIsInVuZXNjYXBlTWVzc2FnZSIsImJvZHkiLCJ0ZXh0U2VyaWFsaXplIiwibXNndHlwZSIsIk1zZ1R5cGUiLCJFbW90ZSIsIlRleHQiLCJmb3JtYXR0ZWRCb2R5IiwiaHRtbFNlcmlhbGl6ZUlmTmVlZGVkIiwiZm9yY2VIVE1MIiwidXNlTWFya2Rvd24iLCJTZXR0aW5nc1N0b3JlIiwiZ2V0VmFsdWUiLCJmb3JtYXQiLCJmb3JtYXR0ZWRfYm9keSIsImFkZFJlcGx5VG9NZXNzYWdlQ29udGVudCIsImluY2x1ZGVMZWdhY3lGYWxsYmFjayIsImlzUXVpY2tSZWFjdGlvbiIsInRleHQiLCJoYXNTaG9ydGN1dCIsImVtb2ppTWF0Y2giLCJtYXRjaCIsIkVNT0pJX1JFR0VYIiwic3Vic3RyaW5nIiwiU2VuZE1lc3NhZ2VDb21wb3NlciIsIlJlYWN0IiwiQ29tcG9uZW50IiwiY29uc3RydWN0b3IiLCJwcm9wcyIsImNvbnRleHQiLCJjcmVhdGVSZWYiLCJldmVudCIsImVkaXRvclJlZiIsImN1cnJlbnQiLCJpc0NvbXBvc2luZyIsInJlcGx5aW5nVG9UaHJlYWQiLCJrZXkiLCJUSFJFQURfUkVMQVRJT05fVFlQRSIsIm5hbWUiLCJhY3Rpb24iLCJnZXRLZXlCaW5kaW5nc01hbmFnZXIiLCJnZXRNZXNzYWdlQ29tcG9zZXJBY3Rpb24iLCJLZXlCaW5kaW5nQWN0aW9uIiwiU2VuZE1lc3NhZ2UiLCJzZW5kTWVzc2FnZSIsInByZXZlbnREZWZhdWx0IiwiU2VsZWN0UHJldlNlbmRIaXN0b3J5IiwiU2VsZWN0TmV4dFNlbmRIaXN0b3J5Iiwic2VsZWN0ZWQiLCJzZWxlY3RTZW5kSGlzdG9yeSIsIlNob3dTdGlja2VyUGlja2VyIiwidG9nZ2xlU3RpY2tlclBpY2tlck9wZW4iLCJFZGl0UHJldk1lc3NhZ2UiLCJpc1NlbGVjdGlvbkNvbGxhcHNlZCIsImlzQ2FyZXRBdFN0YXJ0IiwiZXZlbnRzIiwibGl2ZVRpbWVsaW5lIiwiZ2V0RXZlbnRzIiwiY29uY2F0IiwiZ2V0UGVuZGluZ0V2ZW50cyIsImVkaXRFdmVudCIsImZpbmRFZGl0YWJsZUV2ZW50IiwiaXNGb3J3YXJkIiwibWF0cml4Q2xpZW50IiwiTWF0cml4Q2xpZW50UGVnIiwic2FmZUdldCIsInVuZGVmaW5lZCIsImRpcyIsImRpc3BhdGNoIiwiQWN0aW9uIiwiRWRpdEV2ZW50IiwidGltZWxpbmVSZW5kZXJpbmdUeXBlIiwiQ2FuY2VsUmVwbHlPckVkaXQiLCJzdG9wUHJvcGFnYXRpb24iLCJpc0VtcHR5Iiwic2hvdWxkU2F2ZVN0b3JlZEVkaXRvclN0YXRlIiwiaXRlbSIsIlNlbmRIaXN0b3J5TWFuYWdlciIsImNyZWF0ZUl0ZW0iLCJsb2NhbFN0b3JhZ2UiLCJzZXRJdGVtIiwiZWRpdG9yU3RhdGVLZXkiLCJKU09OIiwic3RyaW5naWZ5IiwiY2xlYXJTdG9yZWRFZGl0b3JTdGF0ZSIsInBheWxvYWQiLCJkaXNhYmxlZCIsIkZvY3VzU2VuZE1lc3NhZ2VDb21wb3NlciIsIlRpbWVsaW5lUmVuZGVyaW5nVHlwZSIsIlJvb20iLCJmb2N1cyIsIkNvbXBvc2VySW5zZXJ0IiwiY29tcG9zZXJUeXBlIiwiQ29tcG9zZXJUeXBlIiwiU2VuZCIsImluc2VydE1lbnRpb24iLCJpbnNlcnRRdW90ZWRNZXNzYWdlIiwiaW5zZXJ0UGxhaW50ZXh0IiwiZGF0YSIsImZpbGVzIiwidHlwZXMiLCJpbmNsdWRlcyIsIkNvbnRlbnRNZXNzYWdlcyIsInNoYXJlZEluc3RhbmNlIiwic2VuZENvbnRlbnRMaXN0VG9Sb29tIiwiZnJvbSIsInJvb21JZCIsIm14Q2xpZW50IiwiaW1nRWxlbWVudFN0ciIsImdldERhdGEiLCJwYXJzZXIiLCJET01QYXJzZXIiLCJpbWdEb2MiLCJwYXJzZUZyb21TdHJpbmciLCJnZXRFbGVtZW50c0J5VGFnTmFtZSIsInF1ZXJ5U2VsZWN0b3IiLCJzcmMiLCJjaGlsZE5vZGVzIiwiY29uc29sZSIsImxvZyIsImltZ1NyYyIsImZldGNoIiwidGhlbiIsInJlc3BvbnNlIiwiYmxvYiIsImltZ0Jsb2IiLCJzYWZldHlwZSIsImdldEJsb2JTYWZlTWltZVR5cGUiLCJleHQiLCJzcGxpdCIsInVybCIsImZpbGVuYW1lIiwiZmlsZSIsIkZpbGUiLCJzZW5kQ29udGVudFRvUm9vbSIsImVycm9yIiwic2VsZWN0aW9uIiwiaW5wdXRUeXBlIiwiZGlmZiIsInByZXBhcmVUb0VuY3J5cHQiLCJvbkNoYW5nZSIsImlzQ3J5cHRvRW5hYmxlZCIsImlzUm9vbUVuY3J5cHRlZCIsInRocm90dGxlIiwiZ2V0Q3J5cHRvIiwibGVhZGluZyIsInRyYWlsaW5nIiwid2luZG93IiwiYWRkRXZlbnRMaXN0ZW5lciIsInNhdmVTdG9yZWRFZGl0b3JTdGF0ZSIsInBhcnRDcmVhdG9yIiwiQ29tbWFuZFBhcnRDcmVhdG9yIiwicmVzdG9yZVN0b3JlZEVkaXRvclN0YXRlIiwiRWRpdG9yTW9kZWwiLCJkaXNwYXRjaGVyUmVmIiwicmVnaXN0ZXIiLCJvbkFjdGlvbiIsInNlbmRIaXN0b3J5TWFuYWdlciIsImNvbXBvbmVudERpZFVwZGF0ZSIsInByZXZQcm9wcyIsImRpZmZlcmVudEV2ZW50VGFyZ2V0IiwiZXZlbnRfaWQiLCJ0aHJlYWRDaGFuZ2VkIiwicmVzZXQiLCJ1cCIsImRlbHRhIiwiY3VycmVudEluZGV4IiwiaGlzdG9yeSIsImN1cnJlbnRseUNvbXBvc2VkRWRpdG9yU3RhdGUiLCJzZXJpYWxpemVQYXJ0cyIsInJlcGx5RXZlbnRJZCIsImdldEl0ZW0iLCJmaW5kRXZlbnRCeUlkIiwic2VuZFF1aWNrUmVhY3Rpb24iLCJ0aW1lbGluZSIsInJlYWN0aW9uIiwiZ2V0VHlwZSIsIkV2ZW50VHlwZSIsIlJvb21NZXNzYWdlIiwic2hvdWxkUmVhY3QiLCJsYXN0TWVzc2FnZSIsImdldFNhZmVVc2VySWQiLCJtZXNzYWdlUmVhY3Rpb25zIiwicmVsYXRpb25zIiwiZ2V0Q2hpbGRFdmVudHNGb3JFdmVudCIsImdldElkIiwiUmVsYXRpb25UeXBlIiwiQW5ub3RhdGlvbiIsIlJlYWN0aW9uIiwibXlSZWFjdGlvbkV2ZW50cyIsImdldEFubm90YXRpb25zQnlTZW5kZXIiLCJteVJlYWN0aW9uS2V5cyIsImlzUmVkYWN0ZWQiLCJtYXAiLCJnZXRSZWxhdGlvbiIsInNlbmRFdmVudCIsImdldFJvb21JZCIsInJlbF90eXBlIiwicG9zdGhvZ0V2ZW50IiwiZXZlbnROYW1lIiwiaXNFZGl0aW5nIiwibWVzc2FnZVR5cGUiLCJpc1JlcGx5IiwiaW5UaHJlYWQiLCJ0aHJlYWRSb290Iiwic3RhcnRzVGhyZWFkIiwiZ2V0VGhyZWFkIiwiUG9zdGhvZ0FuYWx5dGljcyIsImluc3RhbmNlIiwidHJhY2tFdmVudCIsImluZGV4T2ZMYXN0UGFydCIsInBvc2l0aW9uSW5MYXN0UGFydCIsInJlcGxhY2VFbW90aWNvbiIsIkRvY3VtZW50UG9zaXRpb24iLCJSRUdFWF9FTU9USUNPTiIsInNob3VsZFNlbmQiLCJpc1NsYXNoQ29tbWFuZCIsImNtZCIsImFyZ3MiLCJjb21tYW5kVGV4dCIsImdldFNsYXNoQ29tbWFuZCIsInRocmVhZElkIiwiY29tbWFuZFN1Y2Nlc3NmdWwiLCJydW5TbGFzaENvbW1hbmQiLCJDb21tYW5kQ2F0ZWdvcmllcyIsIm1lc3NhZ2VzIiwiZWZmZWN0cyIsImNhdGVnb3J5Iiwic2VuZEFueXdheSIsInNob3VsZFNlbmRBbnl3YXkiLCJGb2N1c0FDb21wb3NlciIsInRyaW0iLCJkZWNvcmF0ZVN0YXJ0U2VuZGluZ1RpbWUiLCJwcm9tIiwiZG9NYXliZUxvY2FsUm9vbUFjdGlvbiIsImFjdHVhbFJvb21JZCIsIkNIQVRfRUZGRUNUUyIsImVmZmVjdCIsImNvbnRhaW5zRW1vamkiLCJlbW9qaXMiLCJpc05vdFRocmVhZCIsImNvbW1hbmQiLCJyZXNwIiwic2VuZFJvdW5kVHJpcE1ldHJpYyIsInNhdmUiLCJjbGVhclVuZG9IaXN0b3J5IiwiY29tcG9uZW50V2lsbFVubW91bnQiLCJ1bnJlZ2lzdGVyIiwicmVtb3ZlRXZlbnRMaXN0ZW5lciIsInJlbW92ZUl0ZW0iLCJqc29uIiwic2VyaWFsaXplZFBhcnRzIiwicGFyc2UiLCJwIiwiZGVzZXJpYWxpemVQYXJ0IiwibG9nZ2VyIiwicmVuZGVyIiwiY3JlYXRlRWxlbWVudCIsImNsYXNzTmFtZSIsIm9uQ2xpY2siLCJmb2N1c0NvbXBvc2VyIiwib25LZXlEb3duIiwicmVmIiwibGFiZWwiLCJwbGFjZWhvbGRlciIsIm9uUGFzdGUiLCJSb29tQ29udGV4dCIsIlNlbmRNZXNzYWdlQ29tcG9zZXJXaXRoTWF0cml4Q2xpZW50Iiwid2l0aE1hdHJpeENsaWVudEhPQyIsIl9kZWZhdWx0Il0sInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvdmlld3Mvcm9vbXMvU2VuZE1lc3NhZ2VDb21wb3Nlci50c3giXSwic291cmNlc0NvbnRlbnQiOlsiLypcbkNvcHlyaWdodCAyMDI0IE5ldyBWZWN0b3IgTHRkLlxuQ29weXJpZ2h0IDIwMTktMjAyMyBUaGUgTWF0cml4Lm9yZyBGb3VuZGF0aW9uIEMuSS5DLlxuXG5TUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQUdQTC0zLjAtb25seSBPUiBHUEwtMy4wLW9ubHlcblBsZWFzZSBzZWUgTElDRU5TRSBmaWxlcyBpbiB0aGUgcmVwb3NpdG9yeSByb290IGZvciBmdWxsIGRldGFpbHMuXG4qL1xuXG5pbXBvcnQgUmVhY3QsIHsgY3JlYXRlUmVmLCBLZXlib2FyZEV2ZW50LCBTeW50aGV0aWNFdmVudCB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHtcbiAgICBJQ29udGVudCxcbiAgICBNYXRyaXhFdmVudCxcbiAgICBJRXZlbnRSZWxhdGlvbixcbiAgICBJTWVudGlvbnMsXG4gICAgUm9vbSxcbiAgICBFdmVudFR5cGUsXG4gICAgTXNnVHlwZSxcbiAgICBSZWxhdGlvblR5cGUsXG4gICAgVEhSRUFEX1JFTEFUSU9OX1RZUEUsXG59IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy9tYXRyaXhcIjtcbmltcG9ydCB7IERlYm91bmNlZEZ1bmMsIHRocm90dGxlIH0gZnJvbSBcImxvZGFzaFwiO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL2xvZ2dlclwiO1xuaW1wb3J0IHsgQ29tcG9zZXIgYXMgQ29tcG9zZXJFdmVudCB9IGZyb20gXCJAbWF0cml4LW9yZy9hbmFseXRpY3MtZXZlbnRzL3R5cGVzL3R5cGVzY3JpcHQvQ29tcG9zZXJcIjtcbmltcG9ydCB7IFJvb21NZXNzYWdlRXZlbnRDb250ZW50IH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL3R5cGVzXCI7XG5cbmltcG9ydCBkaXMgZnJvbSBcIi4uLy4uLy4uL2Rpc3BhdGNoZXIvZGlzcGF0Y2hlclwiO1xuaW1wb3J0IEVkaXRvck1vZGVsIGZyb20gXCIuLi8uLi8uLi9lZGl0b3IvbW9kZWxcIjtcbmltcG9ydCB7XG4gICAgY29udGFpbnNFbW90ZSxcbiAgICBodG1sU2VyaWFsaXplSWZOZWVkZWQsXG4gICAgc3RhcnRzV2l0aCxcbiAgICBzdHJpcEVtb3RlQ29tbWFuZCxcbiAgICBzdHJpcFByZWZpeCxcbiAgICB0ZXh0U2VyaWFsaXplLFxuICAgIHVuZXNjYXBlTWVzc2FnZSxcbn0gZnJvbSBcIi4uLy4uLy4uL2VkaXRvci9zZXJpYWxpemVcIjtcbmltcG9ydCBCYXNpY01lc3NhZ2VDb21wb3NlciwgeyBSRUdFWF9FTU9USUNPTiB9IGZyb20gXCIuL0Jhc2ljTWVzc2FnZUNvbXBvc2VyXCI7XG5pbXBvcnQgeyBDb21tYW5kUGFydENyZWF0b3IsIFBhcnQsIFBhcnRDcmVhdG9yLCBTZXJpYWxpemVkUGFydCwgVHlwZSB9IGZyb20gXCIuLi8uLi8uLi9lZGl0b3IvcGFydHNcIjtcbmltcG9ydCB7IGZpbmRFZGl0YWJsZUV2ZW50IH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL0V2ZW50VXRpbHNcIjtcbmltcG9ydCBTZW5kSGlzdG9yeU1hbmFnZXIgZnJvbSBcIi4uLy4uLy4uL1NlbmRIaXN0b3J5TWFuYWdlclwiO1xuaW1wb3J0IHsgQ29tbWFuZENhdGVnb3JpZXMgfSBmcm9tIFwiLi4vLi4vLi4vU2xhc2hDb21tYW5kc1wiO1xuaW1wb3J0IENvbnRlbnRNZXNzYWdlcyBmcm9tIFwiLi4vLi4vLi4vQ29udGVudE1lc3NhZ2VzXCI7XG5pbXBvcnQgeyB3aXRoTWF0cml4Q2xpZW50SE9DLCBNYXRyaXhDbGllbnRQcm9wcyB9IGZyb20gXCIuLi8uLi8uLi9jb250ZXh0cy9NYXRyaXhDbGllbnRDb250ZXh0XCI7XG5pbXBvcnQgeyBBY3Rpb24gfSBmcm9tIFwiLi4vLi4vLi4vZGlzcGF0Y2hlci9hY3Rpb25zXCI7XG5pbXBvcnQgeyBjb250YWluc0Vtb2ppIH0gZnJvbSBcIi4uLy4uLy4uL2VmZmVjdHMvdXRpbHNcIjtcbmltcG9ydCB7IENIQVRfRUZGRUNUUyB9IGZyb20gXCIuLi8uLi8uLi9lZmZlY3RzXCI7XG5pbXBvcnQgeyBNYXRyaXhDbGllbnRQZWcgfSBmcm9tIFwiLi4vLi4vLi4vTWF0cml4Q2xpZW50UGVnXCI7XG5pbXBvcnQgeyBnZXRLZXlCaW5kaW5nc01hbmFnZXIgfSBmcm9tIFwiLi4vLi4vLi4vS2V5QmluZGluZ3NNYW5hZ2VyXCI7XG5pbXBvcnQgU2V0dGluZ3NTdG9yZSBmcm9tIFwiLi4vLi4vLi4vc2V0dGluZ3MvU2V0dGluZ3NTdG9yZVwiO1xuaW1wb3J0IHsgUm9vbVBlcm1hbGlua0NyZWF0b3IgfSBmcm9tIFwiLi4vLi4vLi4vdXRpbHMvcGVybWFsaW5rcy9QZXJtYWxpbmtzXCI7XG5pbXBvcnQgeyBBY3Rpb25QYXlsb2FkIH0gZnJvbSBcIi4uLy4uLy4uL2Rpc3BhdGNoZXIvcGF5bG9hZHNcIjtcbmltcG9ydCB7IGRlY29yYXRlU3RhcnRTZW5kaW5nVGltZSwgc2VuZFJvdW5kVHJpcE1ldHJpYyB9IGZyb20gXCIuLi8uLi8uLi9zZW5kVGltZVBlcmZvcm1hbmNlTWV0cmljc1wiO1xuaW1wb3J0IFJvb21Db250ZXh0LCB7IFRpbWVsaW5lUmVuZGVyaW5nVHlwZSB9IGZyb20gXCIuLi8uLi8uLi9jb250ZXh0cy9Sb29tQ29udGV4dFwiO1xuaW1wb3J0IERvY3VtZW50UG9zaXRpb24gZnJvbSBcIi4uLy4uLy4uL2VkaXRvci9wb3NpdGlvblwiO1xuaW1wb3J0IHsgQ29tcG9zZXJUeXBlIH0gZnJvbSBcIi4uLy4uLy4uL2Rpc3BhdGNoZXIvcGF5bG9hZHMvQ29tcG9zZXJJbnNlcnRQYXlsb2FkXCI7XG5pbXBvcnQgeyBnZXRTbGFzaENvbW1hbmQsIGlzU2xhc2hDb21tYW5kLCBydW5TbGFzaENvbW1hbmQsIHNob3VsZFNlbmRBbnl3YXkgfSBmcm9tIFwiLi4vLi4vLi4vZWRpdG9yL2NvbW1hbmRzXCI7XG5pbXBvcnQgeyBLZXlCaW5kaW5nQWN0aW9uIH0gZnJvbSBcIi4uLy4uLy4uL2FjY2Vzc2liaWxpdHkvS2V5Ym9hcmRTaG9ydGN1dHNcIjtcbmltcG9ydCB7IFBvc3Rob2dBbmFseXRpY3MgfSBmcm9tIFwiLi4vLi4vLi4vUG9zdGhvZ0FuYWx5dGljc1wiO1xuaW1wb3J0IHsgYWRkUmVwbHlUb01lc3NhZ2VDb250ZW50IH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL1JlcGx5XCI7XG5pbXBvcnQgeyBkb01heWJlTG9jYWxSb29tQWN0aW9uIH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL2xvY2FsLXJvb21cIjtcbmltcG9ydCB7IENhcmV0IH0gZnJvbSBcIi4uLy4uLy4uL2VkaXRvci9jYXJldFwiO1xuaW1wb3J0IHsgSURpZmYgfSBmcm9tIFwiLi4vLi4vLi4vZWRpdG9yL2RpZmZcIjtcbmltcG9ydCB7IGdldEJsb2JTYWZlTWltZVR5cGUgfSBmcm9tIFwiLi4vLi4vLi4vdXRpbHMvYmxvYnNcIjtcbmltcG9ydCB7IEVNT0pJX1JFR0VYIH0gZnJvbSBcIi4uLy4uLy4uL0h0bWxVdGlsc1wiO1xuXG4vLyBUaGUgcHJlZml4IHVzZWQgd2hlbiBwZXJzaXN0aW5nIGVkaXRvciBkcmFmdHMgdG8gbG9jYWxzdG9yYWdlLlxuZXhwb3J0IGNvbnN0IEVESVRPUl9TVEFURV9TVE9SQUdFX1BSRUZJWCA9IFwibXhfY2lkZXJfc3RhdGVfXCI7XG5cbi8qKlxuICogQnVpbGQgdGhlIG1lbnRpb25zIGluZm9ybWF0aW9uIGJhc2VkIG9uIHRoZSBlZGl0b3IgbW9kZWwgKGFuZCBhbnkgcmVsYXRlZCBldmVudHMpOlxuICpcbiAqIDEuIFNlYXJjaCB0aGUgbW9kZWwgcGFydHMgZm9yIHJvb20gb3IgdXNlciBwaWxscyBhbmQgZmlsbCBpbiB0aGUgbWVudGlvbnMgb2JqZWN0LlxuICogMi4gSWYgdGhpcyBpcyBhIHJlcGx5IHRvIGFub3RoZXIgZXZlbnQsIGluY2x1ZGUgYW55IHVzZXIgbWVudGlvbnMgZnJvbSB0aGF0XG4gKiAgICAoYnV0IGRvIG5vdCBpbmNsdWRlIGEgcm9vbSBtZW50aW9uKS5cbiAqXG4gKiBAcGFyYW0gc2VuZGVyIC0gVGhlIE1hdHJpeCBJRCBvZiB0aGUgdXNlciBzZW5kaW5nIHRoZSBldmVudC5cbiAqIEBwYXJhbSBjb250ZW50IC0gVGhlIGV2ZW50IGNvbnRlbnQuXG4gKiBAcGFyYW0gbW9kZWwgLSBUaGUgZWRpdG9yIG1vZGVsIHRvIHNlYXJjaCBmb3IgbWVudGlvbnMsIG51bGwgaWYgdGhlcmUgaXMgbm8gZWRpdG9yLlxuICogQHBhcmFtIHJlcGx5VG9FdmVudCAtIFRoZSBldmVudCBiZWluZyByZXBsaWVkIHRvIG9yIHVuZGVmaW5lZCBpZiBpdCBpcyBub3QgYSByZXBseS5cbiAqIEBwYXJhbSBlZGl0ZWRDb250ZW50IC0gVGhlIGNvbnRlbnQgb2YgdGhlIHBhcmVudCBldmVudCBiZWluZyBlZGl0ZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhdHRhY2hNZW50aW9ucyhcbiAgICBzZW5kZXI6IHN0cmluZyxcbiAgICBjb250ZW50OiBJQ29udGVudCxcbiAgICBtb2RlbDogRWRpdG9yTW9kZWwgfCBudWxsLFxuICAgIHJlcGx5VG9FdmVudDogTWF0cml4RXZlbnQgfCB1bmRlZmluZWQsXG4gICAgZWRpdGVkQ29udGVudDogSUNvbnRlbnQgfCBudWxsID0gbnVsbCxcbik6IHZvaWQge1xuICAgIC8vIFdlIGFsd2F5cyBhdHRhY2ggdGhlIG1lbnRpb25zIGV2ZW4gaWYgdGhlIGhvbWUgc2VydmVyIGRvZXNuJ3QgeWV0IHN1cHBvcnRcbiAgICAvLyBpbnRlbnRpb25hbCBtZW50aW9ucy4gVGhpcyBpcyBzYWZlIGJlY2F1c2UgbS5tZW50aW9ucyBpcyBhbiBhZGRpdGl2ZSBjaGFuZ2VcbiAgICAvLyB0aGF0IHNob3VsZCBzaW1wbHkgYmUgaWdub3JlZCBieSBpbmNhcGFibGUgaG9tZSBzZXJ2ZXJzLlxuXG4gICAgLy8gVGhlIG1lbnRpb25zIHByb3BlcnR5ICphbHdheXMqIGdldHMgaW5jbHVkZWQgdG8gZGlzYWJsZSBsZWdhY3kgcHVzaCBydWxlcy5cbiAgICBjb25zdCBtZW50aW9uczogSU1lbnRpb25zID0gKGNvbnRlbnRbXCJtLm1lbnRpb25zXCJdID0ge30pO1xuXG4gICAgY29uc3QgdXNlck1lbnRpb25zID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gICAgbGV0IHJvb21NZW50aW9uID0gZmFsc2U7XG5cbiAgICAvLyBJZiB0aGVyZSdzIGEgcmVwbHksIGluaXRpYWxpemUgdGhlIG1lbnRpb25lZCB1c2VycyBhcyB0aGUgc2VuZGVyIG9mIHRoYXRcbiAgICAvLyBldmVudCArIGFueSBtZW50aW9uZWQgdXNlcnMgaW4gdGhhdCBldmVudC5cbiAgICBpZiAocmVwbHlUb0V2ZW50KSB7XG4gICAgICAgIHVzZXJNZW50aW9ucy5hZGQocmVwbHlUb0V2ZW50LnNlbmRlciEudXNlcklkKTtcbiAgICAgICAgLy8gVE9ETyBXaGF0IGRvIHdlIGRvIGlmIHRoZSByZXBseSBldmVudCAqZG9lZXNuJ3QqIGhhdmUgdGhpcyBwcm9wZXJ0eT9cbiAgICAgICAgLy8gVHJ5IHRvIGZpc2ggb3V0IHJlcGxpZXMgZnJvbSB0aGUgY29udGVudHM/XG4gICAgICAgIGNvbnN0IHVzZXJJZHMgPSByZXBseVRvRXZlbnQuZ2V0Q29udGVudCgpW1wibS5tZW50aW9uc1wiXT8udXNlcl9pZHM7XG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KHVzZXJJZHMpKSB7XG4gICAgICAgICAgICB1c2VySWRzLmZvckVhY2goKHVzZXJJZCkgPT4gdXNlck1lbnRpb25zLmFkZCh1c2VySWQpKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vIElmIHVzZXIgcHJvdmlkZWQgY29udGVudCBpcyBhdmFpbGFibGUsIGNoZWNrIHRvIHNlZSBpZiBhbnkgdXNlcnMgYXJlIG1lbnRpb25lZC5cbiAgICBpZiAobW9kZWwpIHtcbiAgICAgICAgLy8gQWRkIGFueSBtZW50aW9uZWQgdXNlcnMgaW4gdGhlIGN1cnJlbnQgY29udGVudC5cbiAgICAgICAgZm9yIChjb25zdCBwYXJ0IG9mIG1vZGVsLnBhcnRzKSB7XG4gICAgICAgICAgICBpZiAocGFydC50eXBlID09PSBUeXBlLlVzZXJQaWxsKSB7XG4gICAgICAgICAgICAgICAgdXNlck1lbnRpb25zLmFkZChwYXJ0LnJlc291cmNlSWQpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChwYXJ0LnR5cGUgPT09IFR5cGUuQXRSb29tUGlsbCkge1xuICAgICAgICAgICAgICAgIHJvb21NZW50aW9uID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vIEVuc3VyZSB0aGUgKmN1cnJlbnQqIHVzZXIgaXNuJ3QgbGlzdGVkIGluIHRoZSBtZW50aW9uZWQgdXNlcnMuXG4gICAgdXNlck1lbnRpb25zLmRlbGV0ZShzZW5kZXIpO1xuXG4gICAgLy8gRmluYWxseSwgaWYgdGhpcyBldmVudCBpcyBlZGl0aW5nIGEgcHJldmlvdXMgZXZlbnQsIG9ubHkgaW5jbHVkZSB1c2VycyB3aG9cbiAgICAvLyB3ZXJlIG5vdCBwcmV2aW91c2x5IG1lbnRpb25lZCBhbmQgYSByb29tIG1lbnRpb24gaWYgdGhlIHByZXZpb3VzIGV2ZW50IHdhc1xuICAgIC8vIG5vdCBhIHJvb20gbWVudGlvbi5cbiAgICBpZiAoZWRpdGVkQ29udGVudCkge1xuICAgICAgICAvLyBGaXJzdCwgdGhlIG5ldyBldmVudCBjb250ZW50IGdldHMgdGhlICpmdWxsKiBzZXQgb2YgdXNlcnMuXG4gICAgICAgIGNvbnN0IG5ld0NvbnRlbnQgPSBjb250ZW50W1wibS5uZXdfY29udGVudFwiXTtcbiAgICAgICAgY29uc3QgbmV3TWVudGlvbnM6IElNZW50aW9ucyA9IChuZXdDb250ZW50W1wibS5tZW50aW9uc1wiXSA9IHt9KTtcblxuICAgICAgICAvLyBPbmx5IGluY2x1ZGUgdGhlIHVzZXJzL3Jvb20gaWYgdGhlcmUgaXMgYW55IGNvbnRlbnQuXG4gICAgICAgIGlmICh1c2VyTWVudGlvbnMuc2l6ZSkge1xuICAgICAgICAgICAgbmV3TWVudGlvbnMudXNlcl9pZHMgPSBbLi4udXNlck1lbnRpb25zXTtcbiAgICAgICAgfVxuICAgICAgICBpZiAocm9vbU1lbnRpb24pIHtcbiAgICAgICAgICAgIG5ld01lbnRpb25zLnJvb20gPSB0cnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gRmV0Y2ggdGhlIG1lbnRpb25zIGZyb20gdGhlIG9yaWdpbmFsIGV2ZW50IGFuZCByZW1vdmUgYW55IHByZXZpb3VzbHlcbiAgICAgICAgLy8gbWVudGlvbmVkIHVzZXJzLlxuICAgICAgICBjb25zdCBwcmV2TWVudGlvbnMgPSBlZGl0ZWRDb250ZW50W1wibS5tZW50aW9uc1wiXTtcbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkocHJldk1lbnRpb25zPy51c2VyX2lkcykpIHtcbiAgICAgICAgICAgIHByZXZNZW50aW9ucyEudXNlcl9pZHMuZm9yRWFjaCgodXNlcklkKSA9PiB1c2VyTWVudGlvbnMuZGVsZXRlKHVzZXJJZCkpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gSWYgdGhlIG9yaWdpbmFsIGV2ZW50IG1lbnRpb25lZCB0aGUgcm9vbSwgbm90aGluZyB0byBkbyBoZXJlLlxuICAgICAgICBpZiAocHJldk1lbnRpb25zPy5yb29tKSB7XG4gICAgICAgICAgICByb29tTWVudGlvbiA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8gT25seSBpbmNsdWRlIHRoZSB1c2Vycy9yb29tIGlmIHRoZXJlIGlzIGFueSBjb250ZW50LlxuICAgIGlmICh1c2VyTWVudGlvbnMuc2l6ZSkge1xuICAgICAgICBtZW50aW9ucy51c2VyX2lkcyA9IFsuLi51c2VyTWVudGlvbnNdO1xuICAgIH1cbiAgICBpZiAocm9vbU1lbnRpb24pIHtcbiAgICAgICAgbWVudGlvbnMucm9vbSA9IHRydWU7XG4gICAgfVxufVxuXG4vLyBNZXJnZXMgZmF2b3VyaW5nIHRoZSBnaXZlbiByZWxhdGlvblxuZXhwb3J0IGZ1bmN0aW9uIGF0dGFjaFJlbGF0aW9uKGNvbnRlbnQ6IElDb250ZW50LCByZWxhdGlvbj86IElFdmVudFJlbGF0aW9uKTogdm9pZCB7XG4gICAgaWYgKHJlbGF0aW9uKSB7XG4gICAgICAgIGNvbnRlbnRbXCJtLnJlbGF0ZXNfdG9cIl0gPSB7XG4gICAgICAgICAgICAuLi4oY29udGVudFtcIm0ucmVsYXRlc190b1wiXSB8fCB7fSksXG4gICAgICAgICAgICAuLi5yZWxhdGlvbixcbiAgICAgICAgfTtcbiAgICB9XG59XG5cbi8vIGV4cG9ydGVkIGZvciB0ZXN0c1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZU1lc3NhZ2VDb250ZW50KFxuICAgIHNlbmRlcjogc3RyaW5nLFxuICAgIG1vZGVsOiBFZGl0b3JNb2RlbCxcbiAgICByZXBseVRvRXZlbnQ6IE1hdHJpeEV2ZW50IHwgdW5kZWZpbmVkLFxuICAgIHJlbGF0aW9uOiBJRXZlbnRSZWxhdGlvbiB8IHVuZGVmaW5lZCxcbiAgICBwZXJtYWxpbmtDcmVhdG9yPzogUm9vbVBlcm1hbGlua0NyZWF0b3IsXG4gICAgaW5jbHVkZVJlcGx5TGVnYWN5RmFsbGJhY2sgPSB0cnVlLFxuKTogUm9vbU1lc3NhZ2VFdmVudENvbnRlbnQge1xuICAgIGNvbnN0IGlzRW1vdGUgPSBjb250YWluc0Vtb3RlKG1vZGVsKTtcbiAgICBpZiAoaXNFbW90ZSkge1xuICAgICAgICBtb2RlbCA9IHN0cmlwRW1vdGVDb21tYW5kKG1vZGVsKTtcbiAgICB9XG4gICAgaWYgKHN0YXJ0c1dpdGgobW9kZWwsIFwiLy9cIikpIHtcbiAgICAgICAgbW9kZWwgPSBzdHJpcFByZWZpeChtb2RlbCwgXCIvXCIpO1xuICAgIH1cbiAgICBtb2RlbCA9IHVuZXNjYXBlTWVzc2FnZShtb2RlbCk7XG5cbiAgICBjb25zdCBib2R5ID0gdGV4dFNlcmlhbGl6ZShtb2RlbCk7XG5cbiAgICBjb25zdCBjb250ZW50OiBSb29tTWVzc2FnZUV2ZW50Q29udGVudCA9IHtcbiAgICAgICAgbXNndHlwZTogaXNFbW90ZSA/IE1zZ1R5cGUuRW1vdGUgOiBNc2dUeXBlLlRleHQsXG4gICAgICAgIGJvZHk6IGJvZHksXG4gICAgfTtcbiAgICBjb25zdCBmb3JtYXR0ZWRCb2R5ID0gaHRtbFNlcmlhbGl6ZUlmTmVlZGVkKG1vZGVsLCB7XG4gICAgICAgIGZvcmNlSFRNTDogISFyZXBseVRvRXZlbnQsXG4gICAgICAgIHVzZU1hcmtkb3duOiBTZXR0aW5nc1N0b3JlLmdldFZhbHVlKFwiTWVzc2FnZUNvbXBvc2VySW5wdXQudXNlTWFya2Rvd25cIiksXG4gICAgfSk7XG4gICAgaWYgKGZvcm1hdHRlZEJvZHkpIHtcbiAgICAgICAgY29udGVudC5mb3JtYXQgPSBcIm9yZy5tYXRyaXguY3VzdG9tLmh0bWxcIjtcbiAgICAgICAgY29udGVudC5mb3JtYXR0ZWRfYm9keSA9IGZvcm1hdHRlZEJvZHk7XG4gICAgfVxuXG4gICAgLy8gQnVpbGQgdGhlIG1lbnRpb25zIHByb3BlcnR5IGFuZCBhZGQgaXQgdG8gdGhlIGV2ZW50IGNvbnRlbnQuXG4gICAgYXR0YWNoTWVudGlvbnMoc2VuZGVyLCBjb250ZW50LCBtb2RlbCwgcmVwbHlUb0V2ZW50KTtcblxuICAgIGF0dGFjaFJlbGF0aW9uKGNvbnRlbnQsIHJlbGF0aW9uKTtcbiAgICBpZiAocmVwbHlUb0V2ZW50KSB7XG4gICAgICAgIGFkZFJlcGx5VG9NZXNzYWdlQ29udGVudChjb250ZW50LCByZXBseVRvRXZlbnQsIHtcbiAgICAgICAgICAgIHBlcm1hbGlua0NyZWF0b3IsXG4gICAgICAgICAgICBpbmNsdWRlTGVnYWN5RmFsbGJhY2s6IGluY2x1ZGVSZXBseUxlZ2FjeUZhbGxiYWNrLFxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gY29udGVudDtcbn1cblxuLy8gZXhwb3J0ZWQgZm9yIHRlc3RzXG5leHBvcnQgZnVuY3Rpb24gaXNRdWlja1JlYWN0aW9uKG1vZGVsOiBFZGl0b3JNb2RlbCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IHBhcnRzID0gbW9kZWwucGFydHM7XG4gICAgaWYgKHBhcnRzLmxlbmd0aCA9PSAwKSByZXR1cm4gZmFsc2U7XG4gICAgY29uc3QgdGV4dCA9IHRleHRTZXJpYWxpemUobW9kZWwpO1xuICAgIC8vIHNob3J0Y3V0IHRha2VzIHRoZSBmb3JtIFwiKzplbW9qaTpcIiBvciBcIisgOmVtb2ppOlwiXCJcbiAgICAvLyBjYW4gYmUgaW4gMSBvciAyIHBhcnRzXG4gICAgaWYgKHBhcnRzLmxlbmd0aCA8PSAyKSB7XG4gICAgICAgIGNvbnN0IGhhc1Nob3J0Y3V0ID0gdGV4dC5zdGFydHNXaXRoKFwiK1wiKSB8fCB0ZXh0LnN0YXJ0c1dpdGgoXCIrIFwiKTtcbiAgICAgICAgY29uc3QgZW1vamlNYXRjaCA9IHRleHQubWF0Y2goRU1PSklfUkVHRVgpO1xuICAgICAgICBpZiAoaGFzU2hvcnRjdXQgJiYgZW1vamlNYXRjaCAmJiBlbW9qaU1hdGNoLmxlbmd0aCA9PSAxKSB7XG4gICAgICAgICAgICByZXR1cm4gZW1vamlNYXRjaFswXSA9PT0gdGV4dC5zdWJzdHJpbmcoMSkgfHwgZW1vamlNYXRjaFswXSA9PT0gdGV4dC5zdWJzdHJpbmcoMik7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xufVxuXG5pbnRlcmZhY2UgSVNlbmRNZXNzYWdlQ29tcG9zZXJQcm9wcyBleHRlbmRzIE1hdHJpeENsaWVudFByb3BzIHtcbiAgICByb29tOiBSb29tO1xuICAgIHBsYWNlaG9sZGVyPzogc3RyaW5nO1xuICAgIHBlcm1hbGlua0N