UNPKG

matrix-react-sdk

Version:
905 lines (881 loc) 132 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.hasText = hasText; exports.textForEvent = textForEvent; exports.textForLocationEvent = textForLocationEvent; var _react = _interopRequireDefault(require("react")); var _matrix = require("matrix-js-sdk/src/matrix"); var _types = require("matrix-js-sdk/src/types"); var _logger = require("matrix-js-sdk/src/logger"); var _utils = require("matrix-js-sdk/src/utils"); var _languageHandler = require("./languageHandler"); var Roles = _interopRequireWildcard(require("./Roles")); var _RoomInvite = require("./RoomInvite"); var _SettingsStore = _interopRequireDefault(require("./settings/SettingsStore")); var _BanList = require("./mjolnir/BanList"); var _WidgetLayoutStore = require("./stores/widgets/WidgetLayoutStore"); var _RightPanelStorePhases = require("./stores/right-panel/RightPanelStorePhases"); var _dispatcher = _interopRequireDefault(require("./dispatcher/dispatcher")); var _RoomSettingsDialog = require("./components/views/dialogs/RoomSettingsDialog"); var _AccessibleButton = _interopRequireDefault(require("./components/views/elements/AccessibleButton")); var _RightPanelStore = _interopRequireDefault(require("./stores/right-panel/RightPanelStore")); var _EventUtils = require("./utils/EventUtils"); var _Call = require("./models/Call"); var _voiceBroadcast = require("./voice-broadcast"); var _getSenderName = require("./utils/event/getSenderName"); var _PosthogTrackers = _interopRequireDefault(require("./PosthogTrackers.ts")); 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; } /* Copyright 2024 New Vector Ltd. Copyright 2015-2022 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. */ function getRoomMemberDisplayname(client, event, userId = event.getSender()) { const roomId = event.getRoomId(); const member = client.getRoom(roomId)?.getMember(userId); return member?.name || member?.rawDisplayName || userId || (0, _languageHandler._t)("common|someone"); } function textForCallEvent(event, client) { const roomName = client.getRoom(event.getRoomId())?.name; const isSupported = client.supportsVoip(); return isSupported ? () => (0, _languageHandler._t)("timeline|m.call|video_call_started", { roomName }) : () => (0, _languageHandler._t)("timeline|m.call|video_call_started_unsupported", { roomName }); } // These functions are frequently used just to check whether an event has // any text to display at all. For this reason they return deferred values // to avoid the expense of looking up translations when they're not needed. function textForCallInviteEvent(event, client) { const senderName = (0, _getSenderName.getSenderName)(event); // FIXME: Find a better way to determine this from the event? const isVoice = !event.getContent().offer?.sdp?.includes("m=video"); const isSupported = client.supportsVoip(); // This ladder could be reduced down to a couple string variables, however other languages // can have a hard time translating those strings. In an effort to make translations easier // and more accurate, we break out the string-based variables to a couple booleans. if (isVoice && isSupported) { return () => (0, _languageHandler._t)("timeline|m.call.invite|voice_call", { senderName }); } else if (isVoice && !isSupported) { return () => (0, _languageHandler._t)("timeline|m.call.invite|voice_call_unsupported", { senderName }); } else if (!isVoice && isSupported) { return () => (0, _languageHandler._t)("timeline|m.call.invite|video_call", { senderName }); } else if (!isVoice && !isSupported) { return () => (0, _languageHandler._t)("timeline|m.call.invite|video_call_unsupported", { senderName }); } return null; } var Modification = /*#__PURE__*/function (Modification) { Modification[Modification["None"] = 0] = "None"; Modification[Modification["Unset"] = 1] = "Unset"; Modification[Modification["Set"] = 2] = "Set"; Modification[Modification["Changed"] = 3] = "Changed"; return Modification; }(Modification || {}); function getModification(prev, value) { if (prev && value && prev !== value) { return Modification.Changed; } if (prev && !value) { return Modification.Unset; } if (!prev && value) { return Modification.Set; } return Modification.None; } function textForMemberEvent(ev, client, allowJSX, showHiddenEvents) { // XXX: SYJS-16 "sender is sometimes null for join messages" const senderName = ev.sender?.name || getRoomMemberDisplayname(client, ev); const targetName = ev.target?.name || getRoomMemberDisplayname(client, ev, ev.getStateKey()); const prevContent = ev.getPrevContent(); const content = ev.getContent(); const reason = content.reason; switch (content.membership) { case _types.KnownMembership.Invite: { const threePidContent = content.third_party_invite; if (threePidContent) { if (threePidContent.display_name) { return () => (0, _languageHandler._t)("timeline|m.room.member|accepted_3pid_invite", { targetName, displayName: threePidContent.display_name }); } else { return () => (0, _languageHandler._t)("timeline|m.room.member|accepted_invite", { targetName }); } } else { return () => (0, _languageHandler._t)("timeline|m.room.member|invite", { senderName, targetName }); } } case _types.KnownMembership.Ban: return () => reason ? (0, _languageHandler._t)("timeline|m.room.member|ban_reason", { senderName, targetName, reason }) : (0, _languageHandler._t)("timeline|m.room.member|ban", { senderName, targetName }); case _types.KnownMembership.Join: if (prevContent && prevContent.membership === _types.KnownMembership.Join) { const modDisplayname = getModification(prevContent.displayname, content.displayname); const modAvatarUrl = getModification(prevContent.avatar_url, content.avatar_url); if (modDisplayname !== Modification.None && modAvatarUrl !== Modification.None) { // Compromise to provide the user with more context without needing 16 translations return () => (0, _languageHandler._t)("timeline|m.room.member|change_name_avatar", { // We're taking the display namke directly from the event content here so we need // to strip direction override chars which the js-sdk would normally do when // calculating the display name oldDisplayName: (0, _utils.removeDirectionOverrideChars)(prevContent.displayname) }); } else if (modDisplayname === Modification.Changed) { return () => (0, _languageHandler._t)("timeline|m.room.member|change_name", { // We're taking the display name directly from the event content here so we need // to strip direction override chars which the js-sdk would normally do when // calculating the display name oldDisplayName: (0, _utils.removeDirectionOverrideChars)(prevContent.displayname), displayName: (0, _utils.removeDirectionOverrideChars)(content.displayname) }); } else if (modDisplayname === Modification.Set) { return () => (0, _languageHandler._t)("timeline|m.room.member|set_name", { senderName: ev.getSender(), displayName: (0, _utils.removeDirectionOverrideChars)(content.displayname) }); } else if (modDisplayname === Modification.Unset) { return () => (0, _languageHandler._t)("timeline|m.room.member|remove_name", { senderName, oldDisplayName: (0, _utils.removeDirectionOverrideChars)(prevContent.displayname) }); } else if (modAvatarUrl === Modification.Unset) { return () => (0, _languageHandler._t)("timeline|m.room.member|remove_avatar", { senderName }); } else if (modAvatarUrl === Modification.Changed) { return () => (0, _languageHandler._t)("timeline|m.room.member|change_avatar", { senderName }); } else if (modAvatarUrl === Modification.Set) { return () => (0, _languageHandler._t)("timeline|m.room.member|set_avatar", { senderName }); } else if (showHiddenEvents ?? _SettingsStore.default.getValue("showHiddenEventsInTimeline")) { // This is a null rejoin, it will only be visible if using 'show hidden events' (labs) return () => (0, _languageHandler._t)("timeline|m.room.member|no_change", { senderName }); } else { return null; } } else { if (!ev.target) _logger.logger.warn("Join message has no target! -- " + ev.getContent().state_key); return () => (0, _languageHandler._t)("timeline|m.room.member|join", { targetName }); } case _types.KnownMembership.Leave: if (ev.getSender() === ev.getStateKey()) { if (prevContent.membership === _types.KnownMembership.Invite) { return () => (0, _languageHandler._t)("timeline|m.room.member|reject_invite", { targetName }); } else { return () => reason ? (0, _languageHandler._t)("timeline|m.room.member|left_reason", { targetName, reason }) : (0, _languageHandler._t)("timeline|m.room.member|left", { targetName }); } } else if (prevContent.membership === _types.KnownMembership.Ban) { return () => (0, _languageHandler._t)("timeline|m.room.member|unban", { senderName, targetName }); } else if (prevContent.membership === _types.KnownMembership.Invite) { return () => reason ? (0, _languageHandler._t)("timeline|m.room.member|withdrew_invite_reason", { senderName, targetName, reason }) : (0, _languageHandler._t)("timeline|m.room.member|withdrew_invite", { senderName, targetName }); } else if (prevContent.membership === _types.KnownMembership.Join) { return () => reason ? (0, _languageHandler._t)("timeline|m.room.member|kick_reason", { senderName, targetName, reason }) : (0, _languageHandler._t)("timeline|m.room.member|kick", { senderName, targetName }); } else { return null; } } return null; } function textForTopicEvent(ev) { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); return () => (0, _languageHandler._t)("timeline|m.room.topic", { senderDisplayName, topic: ev.getContent().topic }); } function textForRoomAvatarEvent(ev) { const senderDisplayName = ev?.sender?.name || ev.getSender(); return () => (0, _languageHandler._t)("timeline|m.room.avatar|changed", { senderDisplayName }); } function textForRoomNameEvent(ev) { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); if (!ev.getContent().name || ev.getContent().name.trim().length === 0) { return () => (0, _languageHandler._t)("timeline|m.room.name|remove", { senderDisplayName }); } if (ev.getPrevContent().name) { return () => (0, _languageHandler._t)("timeline|m.room.name|change", { senderDisplayName, oldRoomName: ev.getPrevContent().name, newRoomName: ev.getContent().name }); } return () => (0, _languageHandler._t)("timeline|m.room.name|set", { senderDisplayName, roomName: ev.getContent().name }); } function textForTombstoneEvent(ev) { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); return () => (0, _languageHandler._t)("timeline|m.room.tombstone", { senderDisplayName }); } const onViewJoinRuleSettingsClick = () => { _dispatcher.default.dispatch({ action: "open_room_settings", initial_tab_id: _RoomSettingsDialog.RoomSettingsTab.Security }); }; function textForJoinRulesEvent(ev, client, allowJSX) { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); switch (ev.getContent().join_rule) { case _matrix.JoinRule.Public: return () => (0, _languageHandler._t)("timeline|m.room.join_rules|public", { senderDisplayName }); case _matrix.JoinRule.Invite: return () => (0, _languageHandler._t)("timeline|m.room.join_rules|invite", { senderDisplayName }); case _matrix.JoinRule.Knock: return () => (0, _languageHandler._t)("timeline|m.room.join_rules|knock", { senderDisplayName }); case _matrix.JoinRule.Restricted: if (allowJSX) { return () => /*#__PURE__*/_react.default.createElement("span", null, (0, _languageHandler._t)("timeline|m.room.join_rules|restricted_settings", { senderDisplayName }, { a: sub => /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, { kind: "link_inline", onClick: onViewJoinRuleSettingsClick }, sub) })); } return () => (0, _languageHandler._t)("timeline|m.room.join_rules|restricted", { senderDisplayName }); default: // The spec supports "knock" and "private", however nothing implements these. return () => (0, _languageHandler._t)("timeline|m.room.join_rules|unknown", { senderDisplayName, rule: ev.getContent().join_rule }); } } function textForGuestAccessEvent(ev) { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); switch (ev.getContent().guest_access) { case _matrix.GuestAccess.CanJoin: return () => (0, _languageHandler._t)("timeline|m.room.guest_access|can_join", { senderDisplayName }); case _matrix.GuestAccess.Forbidden: return () => (0, _languageHandler._t)("timeline|m.room.guest_access|forbidden", { senderDisplayName }); default: // There's no other options we can expect, however just for safety's sake we'll do this. return () => (0, _languageHandler._t)("timeline|m.room.guest_access|unknown", { senderDisplayName, rule: ev.getContent().guest_access }); } } function textForServerACLEvent(ev) { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); const prevContent = ev.getPrevContent(); const current = ev.getContent(); const prev = { deny: Array.isArray(prevContent.deny) ? prevContent.deny : [], allow: Array.isArray(prevContent.allow) ? prevContent.allow : [], allow_ip_literals: prevContent.allow_ip_literals !== false }; let getText; if (prev.deny.length === 0 && prev.allow.length === 0) { getText = () => (0, _languageHandler._t)("timeline|m.room.server_acl|set", { senderDisplayName }); } else { getText = () => (0, _languageHandler._t)("timeline|m.room.server_acl|changed", { senderDisplayName }); } if (!Array.isArray(current.allow)) { current.allow = []; } // If we know for sure everyone is banned, mark the room as obliterated if (current.allow.length === 0) { return () => getText() + " " + (0, _languageHandler._t)("timeline|m.room.server_acl|all_servers_banned"); } return getText; } function textForMessageEvent(ev, client) { if ((0, _EventUtils.isLocationEvent)(ev)) { return textForLocationEvent(ev); } return () => { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); let message = ev.getContent().body; if (ev.isRedacted()) { message = textForRedactedPollAndMessageEvent(ev, client); } if (ev.getContent().msgtype === _matrix.MsgType.Emote) { message = "* " + senderDisplayName + " " + message; } else if (ev.getContent().msgtype === _matrix.MsgType.Image) { message = (0, _languageHandler._t)("timeline|m.image|sent", { senderDisplayName }); } else if (ev.getType() == _matrix.EventType.Sticker) { message = (0, _languageHandler._t)("timeline|m.sticker", { senderDisplayName }); } else { // in this case, parse it as a plain text message message = senderDisplayName + ": " + message; } return message; }; } function textForCanonicalAliasEvent(ev) { const senderName = (0, _getSenderName.getSenderName)(ev); const oldAlias = ev.getPrevContent().alias; const oldAltAliases = ev.getPrevContent().alt_aliases || []; const newAlias = ev.getContent().alias; const newAltAliases = ev.getContent().alt_aliases || []; const removedAltAliases = oldAltAliases.filter(alias => !newAltAliases.includes(alias)); const addedAltAliases = newAltAliases.filter(alias => !oldAltAliases.includes(alias)); if (!removedAltAliases.length && !addedAltAliases.length) { if (newAlias) { return () => (0, _languageHandler._t)("timeline|m.room.canonical_alias|set", { senderName, address: ev.getContent().alias }); } else if (oldAlias) { return () => (0, _languageHandler._t)("timeline|m.room.canonical_alias|removed", { senderName }); } } else if (newAlias === oldAlias) { if (addedAltAliases.length && !removedAltAliases.length) { return () => (0, _languageHandler._t)("timeline|m.room.canonical_alias|alt_added", { senderName, addresses: addedAltAliases.join(", "), count: addedAltAliases.length }); } if (removedAltAliases.length && !addedAltAliases.length) { return () => (0, _languageHandler._t)("timeline|m.room.canonical_alias|alt_removed", { senderName, addresses: removedAltAliases.join(", "), count: removedAltAliases.length }); } if (removedAltAliases.length && addedAltAliases.length) { return () => (0, _languageHandler._t)("timeline|m.room.canonical_alias|changed_alternative", { senderName }); } } else { // both alias and alt_aliases where modified return () => (0, _languageHandler._t)("timeline|m.room.canonical_alias|changed_main_and_alternative", { senderName }); } // in case there is no difference between the two events, // say something as we can't simply hide the tile from here return () => (0, _languageHandler._t)("timeline|m.room.canonical_alias|changed", { senderName }); } function textForThreePidInviteEvent(event) { const senderName = (0, _getSenderName.getSenderName)(event); if (!(0, _RoomInvite.isValid3pidInvite)(event)) { return () => (0, _languageHandler._t)("timeline|m.room.third_party_invite|revoked", { senderName, targetDisplayName: event.getPrevContent().display_name || (0, _languageHandler._t)("common|someone") }); } return () => (0, _languageHandler._t)("timeline|m.room.third_party_invite|sent", { senderName, targetDisplayName: event.getContent().display_name }); } function textForHistoryVisibilityEvent(event) { const senderName = (0, _getSenderName.getSenderName)(event); switch (event.getContent().history_visibility) { case _matrix.HistoryVisibility.Invited: return () => (0, _languageHandler._t)("timeline|m.room.history_visibility|invited", { senderName }); case _matrix.HistoryVisibility.Joined: return () => (0, _languageHandler._t)("timeline|m.room.history_visibility|joined", { senderName }); case _matrix.HistoryVisibility.Shared: return () => (0, _languageHandler._t)("timeline|m.room.history_visibility|shared", { senderName }); case _matrix.HistoryVisibility.WorldReadable: return () => (0, _languageHandler._t)("timeline|m.room.history_visibility|world_readable", { senderName }); default: return () => (0, _languageHandler._t)("timeline|m.room.history_visibility|unknown", { senderName, visibility: event.getContent().history_visibility }); } } // Currently will only display a change if a user's power level is changed function textForPowerEvent(event, client) { const senderName = (0, _getSenderName.getSenderName)(event); if (!event.getPrevContent()?.users || !event.getContent()?.users) { return null; } const previousUserDefault = event.getPrevContent().users_default || 0; const currentUserDefault = event.getContent().users_default || 0; // Construct set of userIds const users = []; Object.keys(event.getContent().users).forEach(userId => { if (users.indexOf(userId) === -1) users.push(userId); }); Object.keys(event.getPrevContent().users).forEach(userId => { if (users.indexOf(userId) === -1) users.push(userId); }); const diffs = []; users.forEach(userId => { // Previous power level let from = event.getPrevContent().users[userId]; if (!Number.isInteger(from)) { from = previousUserDefault; } // Current power level let to = event.getContent().users[userId]; if (!Number.isInteger(to)) { to = currentUserDefault; } if (from === previousUserDefault && to === currentUserDefault) { return; } if (to !== from) { const name = getRoomMemberDisplayname(client, event, userId); diffs.push({ userId, name, from, to }); } }); if (!diffs.length) { return null; } // XXX: This is also surely broken for i18n return () => (0, _languageHandler._t)("timeline|m.room.power_levels|changed", { senderName, powerLevelDiffText: diffs.map(diff => (0, _languageHandler._t)("timeline|m.room.power_levels|user_from_to", { userId: diff.name, fromPowerLevel: Roles.textualPowerLevel(diff.from, previousUserDefault), toPowerLevel: Roles.textualPowerLevel(diff.to, currentUserDefault) })).join(", ") }); } const onPinnedMessagesClick = () => { _PosthogTrackers.default.trackInteraction("PinnedMessageStateEventClick"); _RightPanelStore.default.instance.setCard({ phase: _RightPanelStorePhases.RightPanelPhases.PinnedMessages }, false); }; function textForPinnedEvent(event, client, allowJSX) { const senderName = (0, _getSenderName.getSenderName)(event); const roomId = event.getRoomId(); const pinned = event.getContent().pinned ?? []; const previouslyPinned = event.getPrevContent().pinned ?? []; const newlyPinned = pinned.filter(item => previouslyPinned.indexOf(item) < 0); const newlyUnpinned = previouslyPinned.filter(item => pinned.indexOf(item) < 0); if (newlyPinned.length === 1 && newlyUnpinned.length === 0) { // A single message was pinned, include a link to that message. if (allowJSX) { const messageId = newlyPinned.pop(); return () => /*#__PURE__*/_react.default.createElement("span", null, (0, _languageHandler._t)("timeline|m.room.pinned_events|pinned_link", { senderName }, { a: sub => /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, { kind: "link_inline", onClick: () => { _PosthogTrackers.default.trackInteraction("PinnedMessageStateEventClick"); (0, _EventUtils.highlightEvent)(roomId, messageId); } }, sub), b: sub => /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, { kind: "link_inline", onClick: onPinnedMessagesClick }, sub) })); } return () => (0, _languageHandler._t)("timeline|m.room.pinned_events|pinned", { senderName }); } if (newlyUnpinned.length === 1 && newlyPinned.length === 0) { // A single message was unpinned, include a link to that message. if (allowJSX) { const messageId = newlyUnpinned.pop(); return () => /*#__PURE__*/_react.default.createElement("span", null, (0, _languageHandler._t)("timeline|m.room.pinned_events|unpinned_link", { senderName }, { a: sub => /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, { kind: "link_inline", onClick: () => { _PosthogTrackers.default.trackInteraction("PinnedMessageStateEventClick"); (0, _EventUtils.highlightEvent)(roomId, messageId); } }, sub), b: sub => /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, { kind: "link_inline", onClick: onPinnedMessagesClick }, sub) })); } return () => (0, _languageHandler._t)("timeline|m.room.pinned_events|unpinned", { senderName }); } if (allowJSX) { return () => /*#__PURE__*/_react.default.createElement("span", null, (0, _languageHandler._t)("timeline|m.room.pinned_events|changed_link", { senderName }, { a: sub => /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, { kind: "link_inline", onClick: onPinnedMessagesClick }, sub) })); } return () => (0, _languageHandler._t)("timeline|m.room.pinned_events|changed", { senderName }); } function textForWidgetEvent(event) { const senderName = (0, _getSenderName.getSenderName)(event); const { name: prevName, type: prevType, url: prevUrl } = event.getPrevContent(); const { name, type, url } = event.getContent() || {}; let widgetName = name || prevName || type || prevType || ""; // Apply sentence case to widget name if (widgetName && widgetName.length > 0) { widgetName = widgetName[0].toUpperCase() + widgetName.slice(1); } // If the widget was removed, its content should be {}, but this is sufficiently // equivalent to that condition. if (url) { if (prevUrl) { return () => (0, _languageHandler._t)("timeline|m.widget|modified", { widgetName, senderName }); } else { return () => (0, _languageHandler._t)("timeline|m.widget|added", { widgetName, senderName }); } } else { return () => (0, _languageHandler._t)("timeline|m.widget|removed", { widgetName, senderName }); } } function textForWidgetLayoutEvent(event) { const senderName = (0, _getSenderName.getSenderName)(event); return () => (0, _languageHandler._t)("timeline|io.element.widgets.layout", { senderName }); } function textForMjolnirEvent(event) { const senderName = (0, _getSenderName.getSenderName)(event); const { entity: prevEntity } = event.getPrevContent(); const { entity, recommendation, reason } = event.getContent(); // Rule removed if (!entity) { if (_BanList.USER_RULE_TYPES.includes(event.getType())) { return () => (0, _languageHandler._t)("timeline|mjolnir|removed_rule_users", { senderName, glob: prevEntity }); } else if (_BanList.ROOM_RULE_TYPES.includes(event.getType())) { return () => (0, _languageHandler._t)("timeline|mjolnir|removed_rule_rooms", { senderName, glob: prevEntity }); } else if (_BanList.SERVER_RULE_TYPES.includes(event.getType())) { return () => (0, _languageHandler._t)("timeline|mjolnir|removed_rule_servers", { senderName, glob: prevEntity }); } // Unknown type. We'll say something, but we shouldn't end up here. return () => (0, _languageHandler._t)("timeline|mjolnir|removed_rule", { senderName, glob: prevEntity }); } // Invalid rule if (!recommendation || !reason) return () => (0, _languageHandler._t)("timeline|mjolnir|updated_invalid_rule", { senderName }); // Rule updated if (entity === prevEntity) { if (_BanList.USER_RULE_TYPES.includes(event.getType())) { return () => (0, _languageHandler._t)("timeline|mjolnir|updated_rule_users", { senderName, glob: entity, reason }); } else if (_BanList.ROOM_RULE_TYPES.includes(event.getType())) { return () => (0, _languageHandler._t)("timeline|mjolnir|updated_rule_rooms", { senderName, glob: entity, reason }); } else if (_BanList.SERVER_RULE_TYPES.includes(event.getType())) { return () => (0, _languageHandler._t)("timeline|mjolnir|updated_rule_servers", { senderName, glob: entity, reason }); } // Unknown type. We'll say something but we shouldn't end up here. return () => (0, _languageHandler._t)("timeline|mjolnir|updated_rule", { senderName, glob: entity, reason }); } // New rule if (!prevEntity) { if (_BanList.USER_RULE_TYPES.includes(event.getType())) { return () => (0, _languageHandler._t)("timeline|mjolnir|created_rule_users", { senderName, glob: entity, reason }); } else if (_BanList.ROOM_RULE_TYPES.includes(event.getType())) { return () => (0, _languageHandler._t)("timeline|mjolnir|created_rule_rooms", { senderName, glob: entity, reason }); } else if (_BanList.SERVER_RULE_TYPES.includes(event.getType())) { return () => (0, _languageHandler._t)("timeline|mjolnir|created_rule_servers", { senderName, glob: entity, reason }); } // Unknown type. We'll say something but we shouldn't end up here. return () => (0, _languageHandler._t)("timeline|mjolnir|created_rule", { senderName, glob: entity, reason }); } // else the entity !== prevEntity - count as a removal & add if (_BanList.USER_RULE_TYPES.includes(event.getType())) { return () => (0, _languageHandler._t)("timeline|mjolnir|changed_rule_users", { senderName, oldGlob: prevEntity, newGlob: entity, reason }); } else if (_BanList.ROOM_RULE_TYPES.includes(event.getType())) { return () => (0, _languageHandler._t)("timeline|mjolnir|changed_rule_rooms", { senderName, oldGlob: prevEntity, newGlob: entity, reason }); } else if (_BanList.SERVER_RULE_TYPES.includes(event.getType())) { return () => (0, _languageHandler._t)("timeline|mjolnir|changed_rule_servers", { senderName, oldGlob: prevEntity, newGlob: entity, reason }); } // Unknown type. We'll say something but we shouldn't end up here. return () => (0, _languageHandler._t)("timeline|mjolnir|changed_rule_glob", { senderName, oldGlob: prevEntity, newGlob: entity, reason }); } function textForLocationEvent(event) { return () => (0, _languageHandler._t)("timeline|m.location|full", { senderName: (0, _getSenderName.getSenderName)(event) }); } function textForRedactedPollAndMessageEvent(ev, client) { let message = (0, _languageHandler._t)("timeline|self_redaction"); const unsigned = ev.getUnsigned(); const redactedBecauseUserId = unsigned?.redacted_because?.sender; if (redactedBecauseUserId && redactedBecauseUserId !== ev.getSender()) { const room = client.getRoom(ev.getRoomId()); const sender = room?.getMember(redactedBecauseUserId); message = (0, _languageHandler._t)("timeline|redaction", { name: sender?.name || redactedBecauseUserId }); } return message; } function textForPollStartEvent(event, client) { return () => { let message = ""; if (event.isRedacted()) { message = textForRedactedPollAndMessageEvent(event, client); const senderDisplayName = event.sender?.name ?? event.getSender(); message = senderDisplayName + ": " + message; } else { message = (0, _languageHandler._t)("timeline|m.poll.start", { senderName: (0, _getSenderName.getSenderName)(event), pollQuestion: event.unstableExtensibleEvent?.question?.text }); } return message; }; } function textForPollEndEvent(event) { return () => (0, _languageHandler._t)("timeline|m.poll.end|sender_ended", { senderName: (0, _getSenderName.getSenderName)(event) }); } const handlers = { [_matrix.EventType.RoomMessage]: textForMessageEvent, [_matrix.EventType.Sticker]: textForMessageEvent, [_matrix.EventType.CallInvite]: textForCallInviteEvent, [_matrix.M_POLL_START.name]: textForPollStartEvent, [_matrix.M_POLL_END.name]: textForPollEndEvent, [_matrix.M_POLL_START.altName]: textForPollStartEvent, [_matrix.M_POLL_END.altName]: textForPollEndEvent }; const stateHandlers = { [_matrix.EventType.RoomCanonicalAlias]: textForCanonicalAliasEvent, [_matrix.EventType.RoomName]: textForRoomNameEvent, [_matrix.EventType.RoomTopic]: textForTopicEvent, [_matrix.EventType.RoomMember]: textForMemberEvent, [_matrix.EventType.RoomAvatar]: textForRoomAvatarEvent, [_matrix.EventType.RoomThirdPartyInvite]: textForThreePidInviteEvent, [_matrix.EventType.RoomHistoryVisibility]: textForHistoryVisibilityEvent, [_matrix.EventType.RoomPowerLevels]: textForPowerEvent, [_matrix.EventType.RoomPinnedEvents]: textForPinnedEvent, [_matrix.EventType.RoomServerAcl]: textForServerACLEvent, [_matrix.EventType.RoomTombstone]: textForTombstoneEvent, [_matrix.EventType.RoomJoinRules]: textForJoinRulesEvent, [_matrix.EventType.RoomGuestAccess]: textForGuestAccessEvent, // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) "im.vector.modular.widgets": textForWidgetEvent, [_WidgetLayoutStore.WIDGET_LAYOUT_EVENT_TYPE]: textForWidgetLayoutEvent, [_voiceBroadcast.VoiceBroadcastInfoEventType]: _voiceBroadcast.textForVoiceBroadcastStoppedEvent }; // Add all the Mjolnir stuff to the renderer for (const evType of _BanList.ALL_RULE_TYPES) { stateHandlers[evType] = textForMjolnirEvent; } // Add both stable and unstable m.call events for (const evType of _Call.ElementCall.CALL_EVENT_TYPE.names) { stateHandlers[evType] = textForCallEvent; } /** * Determines whether the given event has text to display. * * @param client The Matrix Client instance for the logged-in user * @param ev The event * @param showHiddenEvents An optional cached setting value for showHiddenEventsInTimeline * to avoid hitting the settings store */ function hasText(ev, client, showHiddenEvents) { const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()]; return Boolean(handler?.(ev, client, false, showHiddenEvents)); } /** * Gets the textual content of the given event. * * @param ev The event * @param client The Matrix Client instance for the logged-in user * @param allowJSX Whether to output rich JSX content * @param showHiddenEvents An optional cached setting value for showHiddenEventsInTimeline * to avoid hitting the settings store */ function textForEvent(ev, client, allowJSX = false, showHiddenEvents) { const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()]; return handler?.(ev, client, allowJSX, showHiddenEvents)?.() || ""; } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVhY3QiLCJfaW50ZXJvcFJlcXVpcmVEZWZhdWx0IiwicmVxdWlyZSIsIl9tYXRyaXgiLCJfdHlwZXMiLCJfbG9nZ2VyIiwiX3V0aWxzIiwiX2xhbmd1YWdlSGFuZGxlciIsIlJvbGVzIiwiX2ludGVyb3BSZXF1aXJlV2lsZGNhcmQiLCJfUm9vbUludml0ZSIsIl9TZXR0aW5nc1N0b3JlIiwiX0Jhbkxpc3QiLCJfV2lkZ2V0TGF5b3V0U3RvcmUiLCJfUmlnaHRQYW5lbFN0b3JlUGhhc2VzIiwiX2Rpc3BhdGNoZXIiLCJfUm9vbVNldHRpbmdzRGlhbG9nIiwiX0FjY2Vzc2libGVCdXR0b24iLCJfUmlnaHRQYW5lbFN0b3JlIiwiX0V2ZW50VXRpbHMiLCJfQ2FsbCIsIl92b2ljZUJyb2FkY2FzdCIsIl9nZXRTZW5kZXJOYW1lIiwiX1Bvc3Rob2dUcmFja2VycyIsIl9nZXRSZXF1aXJlV2lsZGNhcmRDYWNoZSIsImUiLCJXZWFrTWFwIiwiciIsInQiLCJfX2VzTW9kdWxlIiwiZGVmYXVsdCIsImhhcyIsImdldCIsIm4iLCJfX3Byb3RvX18iLCJhIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IiLCJ1IiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiaSIsInNldCIsImdldFJvb21NZW1iZXJEaXNwbGF5bmFtZSIsImNsaWVudCIsImV2ZW50IiwidXNlcklkIiwiZ2V0U2VuZGVyIiwicm9vbUlkIiwiZ2V0Um9vbUlkIiwibWVtYmVyIiwiZ2V0Um9vbSIsImdldE1lbWJlciIsIm5hbWUiLCJyYXdEaXNwbGF5TmFtZSIsIl90IiwidGV4dEZvckNhbGxFdmVudCIsInJvb21OYW1lIiwiaXNTdXBwb3J0ZWQiLCJzdXBwb3J0c1ZvaXAiLCJ0ZXh0Rm9yQ2FsbEludml0ZUV2ZW50Iiwic2VuZGVyTmFtZSIsImdldFNlbmRlck5hbWUiLCJpc1ZvaWNlIiwiZ2V0Q29udGVudCIsIm9mZmVyIiwic2RwIiwiaW5jbHVkZXMiLCJNb2RpZmljYXRpb24iLCJnZXRNb2RpZmljYXRpb24iLCJwcmV2IiwidmFsdWUiLCJDaGFuZ2VkIiwiVW5zZXQiLCJTZXQiLCJOb25lIiwidGV4dEZvck1lbWJlckV2ZW50IiwiZXYiLCJhbGxvd0pTWCIsInNob3dIaWRkZW5FdmVudHMiLCJzZW5kZXIiLCJ0YXJnZXROYW1lIiwidGFyZ2V0IiwiZ2V0U3RhdGVLZXkiLCJwcmV2Q29udGVudCIsImdldFByZXZDb250ZW50IiwiY29udGVudCIsInJlYXNvbiIsIm1lbWJlcnNoaXAiLCJLbm93bk1lbWJlcnNoaXAiLCJJbnZpdGUiLCJ0aHJlZVBpZENvbnRlbnQiLCJ0aGlyZF9wYXJ0eV9pbnZpdGUiLCJkaXNwbGF5X25hbWUiLCJkaXNwbGF5TmFtZSIsIkJhbiIsIkpvaW4iLCJtb2REaXNwbGF5bmFtZSIsImRpc3BsYXluYW1lIiwibW9kQXZhdGFyVXJsIiwiYXZhdGFyX3VybCIsIm9sZERpc3BsYXlOYW1lIiwicmVtb3ZlRGlyZWN0aW9uT3ZlcnJpZGVDaGFycyIsIlNldHRpbmdzU3RvcmUiLCJnZXRWYWx1ZSIsImxvZ2dlciIsIndhcm4iLCJzdGF0ZV9rZXkiLCJMZWF2ZSIsInRleHRGb3JUb3BpY0V2ZW50Iiwic2VuZGVyRGlzcGxheU5hbWUiLCJ0b3BpYyIsInRleHRGb3JSb29tQXZhdGFyRXZlbnQiLCJ0ZXh0Rm9yUm9vbU5hbWVFdmVudCIsInRyaW0iLCJsZW5ndGgiLCJvbGRSb29tTmFtZSIsIm5ld1Jvb21OYW1lIiwidGV4dEZvclRvbWJzdG9uZUV2ZW50Iiwib25WaWV3Sm9pblJ1bGVTZXR0aW5nc0NsaWNrIiwiZGVmYXVsdERpc3BhdGNoZXIiLCJkaXNwYXRjaCIsImFjdGlvbiIsImluaXRpYWxfdGFiX2lkIiwiUm9vbVNldHRpbmdzVGFiIiwiU2VjdXJpdHkiLCJ0ZXh0Rm9ySm9pblJ1bGVzRXZlbnQiLCJqb2luX3J1bGUiLCJKb2luUnVsZSIsIlB1YmxpYyIsIktub2NrIiwiUmVzdHJpY3RlZCIsImNyZWF0ZUVsZW1lbnQiLCJzdWIiLCJraW5kIiwib25DbGljayIsInJ1bGUiLCJ0ZXh0Rm9yR3Vlc3RBY2Nlc3NFdmVudCIsImd1ZXN0X2FjY2VzcyIsIkd1ZXN0QWNjZXNzIiwiQ2FuSm9pbiIsIkZvcmJpZGRlbiIsInRleHRGb3JTZXJ2ZXJBQ0xFdmVudCIsImN1cnJlbnQiLCJkZW55IiwiQXJyYXkiLCJpc0FycmF5IiwiYWxsb3ciLCJhbGxvd19pcF9saXRlcmFscyIsImdldFRleHQiLCJ0ZXh0Rm9yTWVzc2FnZUV2ZW50IiwiaXNMb2NhdGlvbkV2ZW50IiwidGV4dEZvckxvY2F0aW9uRXZlbnQiLCJtZXNzYWdlIiwiYm9keSIsImlzUmVkYWN0ZWQiLCJ0ZXh0Rm9yUmVkYWN0ZWRQb2xsQW5kTWVzc2FnZUV2ZW50IiwibXNndHlwZSIsIk1zZ1R5cGUiLCJFbW90ZSIsIkltYWdlIiwiZ2V0VHlwZSIsIkV2ZW50VHlwZSIsIlN0aWNrZXIiLCJ0ZXh0Rm9yQ2Fub25pY2FsQWxpYXNFdmVudCIsIm9sZEFsaWFzIiwiYWxpYXMiLCJvbGRBbHRBbGlhc2VzIiwiYWx0X2FsaWFzZXMiLCJuZXdBbGlhcyIsIm5ld0FsdEFsaWFzZXMiLCJyZW1vdmVkQWx0QWxpYXNlcyIsImZpbHRlciIsImFkZGVkQWx0QWxpYXNlcyIsImFkZHJlc3MiLCJhZGRyZXNzZXMiLCJqb2luIiwiY291bnQiLCJ0ZXh0Rm9yVGhyZWVQaWRJbnZpdGVFdmVudCIsImlzVmFsaWQzcGlkSW52aXRlIiwidGFyZ2V0RGlzcGxheU5hbWUiLCJ0ZXh0Rm9ySGlzdG9yeVZpc2liaWxpdHlFdmVudCIsImhpc3RvcnlfdmlzaWJpbGl0eSIsIkhpc3RvcnlWaXNpYmlsaXR5IiwiSW52aXRlZCIsIkpvaW5lZCIsIlNoYXJlZCIsIldvcmxkUmVhZGFibGUiLCJ2aXNpYmlsaXR5IiwidGV4dEZvclBvd2VyRXZlbnQiLCJ1c2VycyIsInByZXZpb3VzVXNlckRlZmF1bHQiLCJ1c2Vyc19kZWZhdWx0IiwiY3VycmVudFVzZXJEZWZhdWx0Iiwia2V5cyIsImZvckVhY2giLCJpbmRleE9mIiwicHVzaCIsImRpZmZzIiwiZnJvbSIsIk51bWJlciIsImlzSW50ZWdlciIsInRvIiwicG93ZXJMZXZlbERpZmZUZXh0IiwibWFwIiwiZGlmZiIsImZyb21Qb3dlckxldmVsIiwidGV4dHVhbFBvd2VyTGV2ZWwiLCJ0b1Bvd2VyTGV2ZWwiLCJvblBpbm5lZE1lc3NhZ2VzQ2xpY2siLCJQb3N0aG9nVHJhY2tlcnMiLCJ0cmFja0ludGVyYWN0aW9uIiwiUmlnaHRQYW5lbFN0b3JlIiwiaW5zdGFuY2UiLCJzZXRDYXJkIiwicGhhc2UiLCJSaWdodFBhbmVsUGhhc2VzIiwiUGlubmVkTWVzc2FnZXMiLCJ0ZXh0Rm9yUGlubmVkRXZlbnQiLCJwaW5uZWQiLCJwcmV2aW91c2x5UGlubmVkIiwibmV3bHlQaW5uZWQiLCJpdGVtIiwibmV3bHlVbnBpbm5lZCIsIm1lc3NhZ2VJZCIsInBvcCIsImhpZ2hsaWdodEV2ZW50IiwiYiIsInRleHRGb3JXaWRnZXRFdmVudCIsInByZXZOYW1lIiwidHlwZSIsInByZXZUeXBlIiwidXJsIiwicHJldlVybCIsIndpZGdldE5hbWUiLCJ0b1VwcGVyQ2FzZSIsInNsaWNlIiwidGV4dEZvcldpZGdldExheW91dEV2ZW50IiwidGV4dEZvck1qb2xuaXJFdmVudCIsImVudGl0eSIsInByZXZFbnRpdHkiLCJyZWNvbW1lbmRhdGlvbiIsIlVTRVJfUlVMRV9UWVBFUyIsImdsb2IiLCJST09NX1JVTEVfVFlQRVMiLCJTRVJWRVJfUlVMRV9UWVBFUyIsIm9sZEdsb2IiLCJuZXdHbG9iIiwidW5zaWduZWQiLCJnZXRVbnNpZ25lZCIsInJlZGFjdGVkQmVjYXVzZVVzZXJJZCIsInJlZGFjdGVkX2JlY2F1c2UiLCJyb29tIiwidGV4dEZvclBvbGxTdGFydEV2ZW50IiwicG9sbFF1ZXN0aW9uIiwidW5zdGFibGVFeHRlbnNpYmxlRXZlbnQiLCJxdWVzdGlvbiIsInRleHQiLCJ0ZXh0Rm9yUG9sbEVuZEV2ZW50IiwiaGFuZGxlcnMiLCJSb29tTWVzc2FnZSIsIkNhbGxJbnZpdGUiLCJNX1BPTExfU1RBUlQiLCJNX1BPTExfRU5EIiwiYWx0TmFtZSIsInN0YXRlSGFuZGxlcnMiLCJSb29tQ2Fub25pY2FsQWxpYXMiLCJSb29tTmFtZSIsIlJvb21Ub3BpYyIsIlJvb21NZW1iZXIiLCJSb29tQXZhdGFyIiwiUm9vbVRoaXJkUGFydHlJbnZpdGUiLCJSb29tSGlzdG9yeVZpc2liaWxpdHkiLCJSb29tUG93ZXJMZXZlbHMiLCJSb29tUGlubmVkRXZlbnRzIiwiUm9vbVNlcnZlckFjbCIsIlJvb21Ub21ic3RvbmUiLCJSb29tSm9pblJ1bGVzIiwiUm9vbUd1ZXN0QWNjZXNzIiwiV0lER0VUX0xBWU9VVF9FVkVOVF9UWVBFIiwiVm9pY2VCcm9hZGNhc3RJbmZvRXZlbnRUeXBlIiwidGV4dEZvclZvaWNlQnJvYWRjYXN0U3RvcHBlZEV2ZW50IiwiZXZUeXBlIiwiQUxMX1JVTEVfVFlQRVMiLCJFbGVtZW50Q2FsbCIsIkNBTExfRVZFTlRfVFlQRSIsIm5hbWVzIiwiaGFzVGV4dCIsImhhbmRsZXIiLCJpc1N0YXRlIiwiQm9vbGVhbiIsInRleHRGb3JFdmVudCJdLCJzb3VyY2VzIjpbIi4uL3NyYy9UZXh0Rm9yRXZlbnQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbIi8qXG5Db3B5cmlnaHQgMjAyNCBOZXcgVmVjdG9yIEx0ZC5cbkNvcHlyaWdodCAyMDE1LTIwMjIgVGhlIE1hdHJpeC5vcmcgRm91bmRhdGlvbiBDLkkuQy5cblxuU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFHUEwtMy4wLW9ubHkgT1IgR1BMLTMuMC1vbmx5XG5QbGVhc2Ugc2VlIExJQ0VOU0UgZmlsZXMgaW4gdGhlIHJlcG9zaXRvcnkgcm9vdCBmb3IgZnVsbCBkZXRhaWxzLlxuKi9cblxuaW1wb3J0IFJlYWN0IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHtcbiAgICBNYXRyaXhFdmVudCxcbiAgICBNYXRyaXhDbGllbnQsXG4gICAgR3Vlc3RBY2Nlc3MsXG4gICAgSGlzdG9yeVZpc2liaWxpdHksXG4gICAgSm9pblJ1bGUsXG4gICAgRXZlbnRUeXBlLFxuICAgIE1zZ1R5cGUsXG4gICAgTV9QT0xMX1NUQVJULFxuICAgIE1fUE9MTF9FTkQsXG59IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy9tYXRyaXhcIjtcbmltcG9ydCB7IEtub3duTWVtYmVyc2hpcCB9IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy90eXBlc1wiO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL2xvZ2dlclwiO1xuaW1wb3J0IHsgcmVtb3ZlRGlyZWN0aW9uT3ZlcnJpZGVDaGFycyB9IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy91dGlsc1wiO1xuaW1wb3J0IHsgUG9sbFN0YXJ0RXZlbnQgfSBmcm9tIFwibWF0cml4LWpzLXNkay9zcmMvZXh0ZW5zaWJsZV9ldmVudHNfdjEvUG9sbFN0YXJ0RXZlbnRcIjtcblxuaW1wb3J0IHsgX3QgfSBmcm9tIFwiLi9sYW5ndWFnZUhhbmRsZXJcIjtcbmltcG9ydCAqIGFzIFJvbGVzIGZyb20gXCIuL1JvbGVzXCI7XG5pbXBvcnQgeyBpc1ZhbGlkM3BpZEludml0ZSB9IGZyb20gXCIuL1Jvb21JbnZpdGVcIjtcbmltcG9ydCBTZXR0aW5nc1N0b3JlIGZyb20gXCIuL3NldHRpbmdzL1NldHRpbmdzU3RvcmVcIjtcbmltcG9ydCB7IEFMTF9SVUxFX1RZUEVTLCBST09NX1JVTEVfVFlQRVMsIFNFUlZFUl9SVUxFX1RZUEVTLCBVU0VSX1JVTEVfVFlQRVMgfSBmcm9tIFwiLi9tam9sbmlyL0Jhbkxpc3RcIjtcbmltcG9ydCB7IFdJREdFVF9MQVlPVVRfRVZFTlRfVFlQRSB9IGZyb20gXCIuL3N0b3Jlcy93aWRnZXRzL1dpZGdldExheW91dFN0b3JlXCI7XG5pbXBvcnQgeyBSaWdodFBhbmVsUGhhc2VzIH0gZnJvbSBcIi4vc3RvcmVzL3JpZ2h0LXBhbmVsL1JpZ2h0UGFuZWxTdG9yZVBoYXNlc1wiO1xuaW1wb3J0IGRlZmF1bHREaXNwYXRjaGVyIGZyb20gXCIuL2Rpc3BhdGNoZXIvZGlzcGF0Y2hlclwiO1xuaW1wb3J0IHsgUm9vbVNldHRpbmdzVGFiIH0gZnJvbSBcIi4vY29tcG9uZW50cy92aWV3cy9kaWFsb2dzL1Jvb21TZXR0aW5nc0RpYWxvZ1wiO1xuaW1wb3J0IEFjY2Vzc2libGVCdXR0b24gZnJvbSBcIi4vY29tcG9uZW50cy92aWV3cy9lbGVtZW50cy9BY2Nlc3NpYmxlQnV0dG9uXCI7XG5pbXBvcnQgUmlnaHRQYW5lbFN0b3JlIGZyb20gXCIuL3N0b3Jlcy9yaWdodC1wYW5lbC9SaWdodFBhbmVsU3RvcmVcIjtcbmltcG9ydCB7IGhpZ2hsaWdodEV2ZW50LCBpc0xvY2F0aW9uRXZlbnQgfSBmcm9tIFwiLi91dGlscy9FdmVudFV0aWxzXCI7XG5pbXBvcnQgeyBFbGVtZW50Q2FsbCB9IGZyb20gXCIuL21vZGVscy9DYWxsXCI7XG5pbXBvcnQgeyB0ZXh0Rm9yVm9pY2VCcm9hZGNhc3RTdG9wcGVkRXZlbnQsIFZvaWNlQnJvYWRjYXN0SW5mb0V2ZW50VHlwZSB9IGZyb20gXCIuL3ZvaWNlLWJyb2FkY2FzdFwiO1xuaW1wb3J0IHsgZ2V0U2VuZGVyTmFtZSB9IGZyb20gXCIuL3V0aWxzL2V2ZW50L2dldFNlbmRlck5hbWVcIjtcbmltcG9ydCBQb3N0aG9nVHJhY2tlcnMgZnJvbSBcIi4vUG9zdGhvZ1RyYWNrZXJzLnRzXCI7XG5cbmZ1bmN0aW9uIGdldFJvb21NZW1iZXJEaXNwbGF5bmFtZShjbGllbnQ6IE1hdHJpeENsaWVudCwgZXZlbnQ6IE1hdHJpeEV2ZW50LCB1c2VySWQgPSBldmVudC5nZXRTZW5kZXIoKSk6IHN0cmluZyB7XG4gICAgY29uc3Qgcm9vbUlkID0gZXZlbnQuZ2V0Um9vbUlkKCk7XG4gICAgY29uc3QgbWVtYmVyID0gY2xpZW50LmdldFJvb20ocm9vbUlkKT8uZ2V0TWVtYmVyKHVzZXJJZCEpO1xuICAgIHJldHVybiBtZW1iZXI/Lm5hbWUgfHwgbWVtYmVyPy5yYXdEaXNwbGF5TmFtZSB8fCB1c2VySWQgfHwgX3QoXCJjb21tb258c29tZW9uZVwiKTtcbn1cblxuZnVuY3Rpb24gdGV4dEZvckNhbGxFdmVudChldmVudDogTWF0cml4RXZlbnQsIGNsaWVudDogTWF0cml4Q2xpZW50KTogKCkgPT4gc3RyaW5nIHtcbiAgICBjb25zdCByb29tTmFtZSA9IGNsaWVudC5nZXRSb29tKGV2ZW50LmdldFJvb21JZCgpISk/Lm5hbWU7XG4gICAgY29uc3QgaXNTdXBwb3J0ZWQgPSBjbGllbnQuc3VwcG9ydHNWb2lwKCk7XG5cbiAgICByZXR1cm4gaXNTdXBwb3J0ZWRcbiAgICAgICAgPyAoKSA9PiBfdChcInRpbWVsaW5lfG0uY2FsbHx2aWRlb19jYWxsX3N0YXJ0ZWRcIiwgeyByb29tTmFtZSB9KVxuICAgICAgICA6ICgpID0+IF90KFwidGltZWxpbmV8bS5jYWxsfHZpZGVvX2NhbGxfc3RhcnRlZF91bnN1cHBvcnRlZFwiLCB7IHJvb21OYW1lIH0pO1xufVxuXG4vLyBUaGVzZSBmdW5jdGlvbnMgYXJlIGZyZXF1ZW50bHkgdXNlZCBqdXN0IHRvIGNoZWNrIHdoZXRoZXIgYW4gZXZlbnQgaGFzXG4vLyBhbnkgdGV4dCB0byBkaXNwbGF5IGF0IGFsbC4gRm9yIHRoaXMgcmVhc29uIHRoZXkgcmV0dXJuIGRlZmVycmVkIHZhbHVlc1xuLy8gdG8gYXZvaWQgdGhlIGV4cGVuc2Ugb2YgbG9va2luZyB1cCB0cmFuc2xhdGlvbnMgd2hlbiB0aGV5J3JlIG5vdCBuZWVkZWQuXG5cbmZ1bmN0aW9uIHRleHRGb3JDYWxsSW52aXRlRXZlbnQoZXZlbnQ6IE1hdHJpeEV2ZW50LCBjbGllbnQ6IE1hdHJpeENsaWVudCk6ICgoKSA9PiBzdHJpbmcpIHwgbnVsbCB7XG4gICAgY29uc3Qgc2VuZGVyTmFtZSA9IGdldFNlbmRlck5hbWUoZXZlbnQpO1xuICAgIC8vIEZJWE1FOiBGaW5kIGEgYmV0dGVyIHdheSB0byBkZXRlcm1pbmUgdGhpcyBmcm9tIHRoZSBldmVudD9cbiAgICBjb25zdCBpc1ZvaWNlID0gIWV2ZW50LmdldENvbnRlbnQoKS5vZmZlcj8uc2RwPy5pbmNsdWRlcyhcIm09dmlkZW9cIik7XG4gICAgY29uc3QgaXNTdXBwb3J0ZWQgPSBjbGllbnQuc3VwcG9ydHNWb2lwKCk7XG5cbiAgICAvLyBUaGlzIGxhZGRlciBjb3VsZCBiZSByZWR1Y2VkIGRvd24gdG8gYSBjb3VwbGUgc3RyaW5nIHZhcmlhYmxlcywgaG93ZXZlciBvdGhlciBsYW5ndWFnZXNcbiAgICAvLyBjYW4gaGF2ZSBhIGhhcmQgdGltZSB0cmFuc2xhdGluZyB0aG9zZSBzdHJpbmdzLiBJbiBhbiBlZmZvcnQgdG8gbWFrZSB0cmFuc2xhdGlvbnMgZWFzaWVyXG4gICAgLy8gYW5kIG1vcmUgYWNjdXJhdGUsIHdlIGJyZWFrIG91dCB0aGUgc3RyaW5nLWJhc2VkIHZhcmlhYmxlcyB0byBhIGNvdXBsZSBib29sZWFucy5cbiAgICBpZiAoaXNWb2ljZSAmJiBpc1N1cHBvcnRlZCkge1xuICAgICAgICByZXR1cm4gKCkgPT4gX3QoXCJ0aW1lbGluZXxtLmNhbGwuaW52aXRlfHZvaWNlX2NhbGxcIiwgeyBzZW5kZXJOYW1lIH0pO1xuICAgIH0gZWxzZSBpZiAoaXNWb2ljZSAmJiAhaXNTdXBwb3J0ZWQpIHtcbiAgICAgICAgcmV0dXJuICgpID0+IF90KFwidGltZWxpbmV8bS5jYWxsLmludml0ZXx2b2ljZV9jYWxsX3Vuc3VwcG9ydGVkXCIsIHsgc2VuZGVyTmFtZSB9KTtcbiAgICB9IGVsc2UgaWYgKCFpc1ZvaWNlICYmIGlzU3VwcG9ydGVkKSB7XG4gICAgICAgIHJldHVybiAoKSA9PiBfdChcInRpbWVsaW5lfG0uY2FsbC5pbnZpdGV8dmlkZW9fY2FsbFwiLCB7IHNlbmRlck5hbWUgfSk7XG4gICAgfSBlbHNlIGlmICghaXNWb2ljZSAmJiAhaXNTdXBwb3J0ZWQpIHtcbiAgICAgICAgcmV0dXJuICgpID0+IF90KFwidGltZWxpbmV8bS5jYWxsLmludml0ZXx2aWRlb19jYWxsX3Vuc3VwcG9ydGVkXCIsIHsgc2VuZGVyTmFtZSB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbn1cblxuZW51bSBNb2RpZmljYXRpb24ge1xuICAgIE5vbmUsXG4gICAgVW5zZXQsXG4gICAgU2V0LFxuICAgIENoYW5nZWQsXG59XG5cbmZ1bmN0aW9uIGdldE1vZGlmaWNhdGlvbihwcmV2Pzogc3RyaW5nLCB2YWx1ZT86IHN0cmluZyk6IE1vZGlmaWNhdGlvbiB7XG4gICAgaWYgKHByZXYgJiYgdmFsdWUgJiYgcHJldiAhPT0gdmFsdWUpIHtcbiAgICAgICAgcmV0dXJuIE1vZGlmaWNhdGlvbi5DaGFuZ2VkO1xuICAgIH1cbiAgICBpZiAocHJldiAmJiAhdmFsdWUpIHtcbiAgICAgICAgcmV0dXJuIE1vZGlmaWNhdGlvbi5VbnNldDtcbiAgICB9XG4gICAgaWYgKCFwcmV2ICYmIHZhbHVlKSB7XG4gICAgICAgIHJldHVybiBNb2RpZmljYXRpb24uU2V0O1xuICAgIH1cblxuICAgIHJldHVybiBNb2RpZmljYXRpb24uTm9uZTtcbn1cblxuZnVuY3Rpb24gdGV4dEZvck1lbWJlckV2ZW50KFxuICAgIGV2OiBNYXRyaXhFdmVudCxcbiAgICBjbGllbnQ6IE1hdHJpeENsaWVudCxcbiAgICBhbGxvd0pTWDogYm9vbGVhbixcbiAgICBzaG93SGlkZGVuRXZlbnRzPzogYm9vbGVhbixcbik6ICgoKSA9PiBzdHJpbmcpIHwgbnVsbCB7XG4gICAgLy8gWFhYOiBTWUpTLTE2IFwic2VuZGVyIGlzIHNvbWV0aW1lcyBudWxsIGZvciBqb2luIG1lc3NhZ2VzXCJcbiAgICBjb25zdCBzZW5kZXJOYW1lID0gZXYuc2VuZGVyPy5uYW1lIHx8IGdldFJvb21NZW1iZXJEaXNwbGF5bmFtZShjbGllbnQsIGV2KTtcbiAgICBjb25zdCB0YXJnZXROYW1lID0gZXYudGFyZ2V0Py5uYW1lIHx8IGdldFJvb21NZW1iZXJEaXNwbGF5bmFtZShjbGllbnQsIGV2LCBldi5nZXRTdGF0ZUtleSgpKTtcbiAgICBjb25zdCBwcmV2Q29udGVudCA9IGV2LmdldFByZXZDb250ZW50KCk7XG4gICAgY29uc3QgY29udGVudCA9IGV2LmdldENvbnRlbnQoKTtcbiAgICBjb25zdCByZWFzb24gPSBjb250ZW50LnJlYXNvbjtcblxuICAgIHN3aXRjaCAoY29udGVudC5tZW1iZXJzaGlwKSB7XG4gICAgICAgIGNhc2UgS25vd25NZW1iZXJzaGlwLkludml0ZToge1xuICAgICAgICAgICAgY29uc3QgdGhyZWVQaWRDb250ZW50ID0gY29udGVudC50aGlyZF9wYXJ0eV9pbnZpdGU7XG4gICAgICAgICAgICBpZiAodGhyZWVQaWRDb250ZW50KSB7XG4gICAgICAgICAgICAgICAgaWYgKHRocmVlUGlkQ29udGVudC5kaXNwbGF5X25hbWUpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuICgpID0+XG4gICAgICAgICAgICAgICAgICAgICAgICBfdChcInRpbWVsaW5lfG0ucm9vbS5tZW1iZXJ8YWNjZXB0ZWRfM3BpZF9pbnZpdGVcIiwge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdldE5hbWUsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzcGxheU5hbWU6IHRocmVlUGlkQ29udGVudC5kaXNwbGF5X25hbWUsXG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gKCkgPT4gX3QoXCJ0aW1lbGluZXxtLnJvb20ubWVtYmVyfGFjY2VwdGVkX2ludml0ZVwiLCB7IHRhcmdldE5hbWUgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gKCkgPT4gX3QoXCJ0aW1lbGluZXxtLnJvb20ubWVtYmVyfGludml0ZVwiLCB7IHNlbmRlck5hbWUsIHRhcmdldE5hbWUgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgY2FzZSBLbm93bk1lbWJlcnNoaXAuQmFuOlxuICAgICAgICAgICAgcmV0dXJuICgpID0+XG4gICAgICAgICAgICAgICAgcmVhc29uXG4gICAgICAgICAgICAgICAgICAgID8gX3QoXCJ0aW1lbGluZXxtLnJvb20ubWVtYmVyfGJhbl9yZWFzb25cIiwgeyBzZW5kZXJOYW1lLCB0YXJnZXROYW1lLCByZWFzb24gfSlcbiAgICAgICAgICAgICAgICAgICAgOiBfdChcInRpbWVsaW5lfG0ucm9vbS5tZW1iZXJ8YmFuXCIsIHsgc2VuZGVyTmFtZSwgdGFyZ2V0TmFtZSB9KTtcbiAgICAgICAgY2FzZSBLbm93bk1lbWJlcnNoaXAuSm9pbjpcbiAgICAgICAgICAgIGlmIChwcmV2Q29udGVudCAmJiBwcmV2Q29udGVudC5tZW1iZXJzaGlwID09PSBLbm93bk1lbWJlcnNoaXAuSm9pbikge1xuICAgICAgICAgICAgICAgIGNvbnN0IG1vZERpc3BsYXluYW1lID0gZ2V0TW9kaWZpY2F0aW9uKHByZXZDb250ZW50LmRpc3BsYXluYW1lLCBjb250ZW50LmRpc3BsYXluYW1lKTtcbiAgICAgICAgICAgICAgICBjb25zdCBtb2RBdmF0YXJVcmwgPSBnZXRNb2RpZmljYXRpb24ocHJldkNvbnRlbnQuYXZhdGFyX3VybCwgY29udGVudC5hdmF0YXJfdXJsKTtcblxuICAgICAgICAgICAgICAgIGlmIChtb2REaXNwbGF5bmFtZSAhPT0gTW9kaWZpY2F0aW9uLk5vbmUgJiYgbW9kQXZhdGFyVXJsICE9PSBNb2RpZmljYXRpb24uTm9uZSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBDb21wcm9taXNlIHRvIHByb3ZpZGUgdGhlIHVzZXIgd2l0aCBtb3JlIGNvbnRleHQgd2