UNPKG

matrix-react-sdk

Version:
959 lines (949 loc) 192 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.useWebSearchMetrics = exports.default = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _classnames = _interopRequireDefault(require("classnames")); var _lodash = require("lodash"); var _matrix = require("matrix-js-sdk/src/matrix"); var _types = require("matrix-js-sdk/src/types"); var _utils = require("matrix-js-sdk/src/utils"); var _react = _interopRequireWildcard(require("react")); var _sanitizeHtml = _interopRequireDefault(require("sanitize-html")); var _KeyboardShortcuts = require("../../../../accessibility/KeyboardShortcuts"); var _RovingTabIndex = require("../../../../accessibility/RovingTabIndex"); var _Media = require("../../../../customisations/Media"); var _actions = require("../../../../dispatcher/actions"); var _dispatcher = _interopRequireDefault(require("../../../../dispatcher/dispatcher")); var _useDebouncedCallback = require("../../../../hooks/spotlight/useDebouncedCallback"); var _useRecentSearches = require("../../../../hooks/spotlight/useRecentSearches"); var _useProfileInfo = require("../../../../hooks/useProfileInfo"); var _usePublicRoomDirectory = require("../../../../hooks/usePublicRoomDirectory"); var _useSpaceResults = require("../../../../hooks/useSpaceResults"); var _useUserDirectory = require("../../../../hooks/useUserDirectory"); var _KeyBindingsManager = require("../../../../KeyBindingsManager"); var _languageHandler = require("../../../../languageHandler"); var _MatrixClientPeg = require("../../../../MatrixClientPeg"); var _PosthogAnalytics = require("../../../../PosthogAnalytics"); var _RoomAliasCache = require("../../../../RoomAliasCache"); var _RoomInvite = require("../../../../RoomInvite"); var _SettingLevel = require("../../../../settings/SettingLevel"); var _SettingsStore = _interopRequireDefault(require("../../../../settings/SettingsStore")); var _BreadcrumbsStore = require("../../../../stores/BreadcrumbsStore"); var _RoomNotificationStateStore = require("../../../../stores/notifications/RoomNotificationStateStore"); var _RecentAlgorithm = require("../../../../stores/room-list/algorithms/tag-sorting/RecentAlgorithm"); var _SDKContext = require("../../../../contexts/SDKContext"); var _spaces = require("../../../../stores/spaces"); var _SpaceStore = _interopRequireDefault(require("../../../../stores/spaces/SpaceStore")); var _directMessages = require("../../../../utils/direct-messages"); var _DMRoomMap = _interopRequireDefault(require("../../../../utils/DMRoomMap")); var _Permalinks = require("../../../../utils/permalinks/Permalinks"); var _SortMembers = require("../../../../utils/SortMembers"); var _strings = require("../../../../utils/strings"); var _BaseAvatar = _interopRequireDefault(require("../../avatars/BaseAvatar")); var _DecoratedRoomAvatar = _interopRequireDefault(require("../../avatars/DecoratedRoomAvatar")); var _SearchResultAvatar = require("../../avatars/SearchResultAvatar"); var _NetworkDropdown = require("../../directory/NetworkDropdown"); var _AccessibleButton = _interopRequireDefault(require("../../elements/AccessibleButton")); var _Spinner = _interopRequireDefault(require("../../elements/Spinner")); var _NotificationBadge = _interopRequireDefault(require("../../rooms/NotificationBadge")); var _BaseDialog = _interopRequireDefault(require("../BaseDialog")); var _Option = require("./Option"); var _PublicRoomResultDetails = require("./PublicRoomResultDetails"); var _RoomResultContextMenus = require("./RoomResultContextMenus"); var _RoomContextDetails = require("../../rooms/RoomContextDetails"); var _TooltipOption = require("./TooltipOption"); var _isLocalRoom = require("../../../../utils/localRoom/isLocalRoom"); var _RoomAvatar = _interopRequireDefault(require("../../avatars/RoomAvatar")); var _useSettings = require("../../../../hooks/useSettings"); var _arrays = require("../../../../utils/arrays"); var _SearchInput = require("../../../../utils/SearchInput"); var _Filter = require("./Filter"); 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 2021-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. */ const MAX_RECENT_SEARCHES = 10; const SECTION_LIMIT = 50; // only show 50 results per section for performance reasons const AVATAR_SIZE = "24px"; function refIsForRecentlyViewed(ref) { return ref?.current?.id?.startsWith("mx_SpotlightDialog_button_recentlyViewed_") === true; } function getRoomTypes(filter) { const roomTypes = new Set(); if (filter === _Filter.Filter.PublicRooms) roomTypes.add(null); if (filter === _Filter.Filter.PublicSpaces) roomTypes.add(_matrix.RoomType.Space); return roomTypes; } var Section = /*#__PURE__*/function (Section) { Section[Section["People"] = 0] = "People"; Section[Section["Rooms"] = 1] = "Rooms"; Section[Section["Spaces"] = 2] = "Spaces"; Section[Section["Suggestions"] = 3] = "Suggestions"; Section[Section["PublicRoomsAndSpaces"] = 4] = "PublicRoomsAndSpaces"; return Section; }(Section || {}); function filterToLabel(filter) { switch (filter) { case _Filter.Filter.People: return (0, _languageHandler._t)("common|people"); case _Filter.Filter.PublicRooms: return (0, _languageHandler._t)("spotlight_dialog|public_rooms_label"); case _Filter.Filter.PublicSpaces: return (0, _languageHandler._t)("spotlight_dialog|public_spaces_label"); } } const isRoomResult = result => !!result?.room; const isPublicRoomResult = result => !!result?.publicRoom; const isMemberResult = result => !!result?.member; const toPublicRoomResult = publicRoom => ({ publicRoom, section: Section.PublicRoomsAndSpaces, filter: [_Filter.Filter.PublicRooms, _Filter.Filter.PublicSpaces], query: (0, _arrays.filterBoolean)([publicRoom.room_id.toLowerCase(), publicRoom.canonical_alias?.toLowerCase(), publicRoom.name?.toLowerCase(), (0, _sanitizeHtml.default)(publicRoom.topic?.toLowerCase() ?? "", { allowedTags: [] }), ...(publicRoom.aliases?.map(it => it.toLowerCase()) || [])]) }); const toRoomResult = room => { const myUserId = _MatrixClientPeg.MatrixClientPeg.safeGet().getUserId(); const otherUserId = _DMRoomMap.default.shared().getUserIdForRoomId(room.roomId); if (otherUserId) { const otherMembers = room.getMembers().filter(it => it.userId !== myUserId); const query = [...otherMembers.map(it => it.name.toLowerCase()), ...otherMembers.map(it => it.userId.toLowerCase())].filter(Boolean); return { room, section: Section.People, filter: [_Filter.Filter.People], query }; } else if (room.isSpaceRoom()) { return { room, section: Section.Spaces, filter: [] }; } else { return { room, section: Section.Rooms, filter: [] }; } }; const toMemberResult = (member, alreadyFiltered) => ({ alreadyFiltered, member, section: Section.Suggestions, filter: [_Filter.Filter.People], query: [member.userId.toLowerCase(), member.name.toLowerCase()].filter(Boolean) }); const recentAlgorithm = new _RecentAlgorithm.RecentAlgorithm(); const useWebSearchMetrics = (numResults, queryLength, viaSpotlight) => { (0, _react.useEffect)(() => { if (!queryLength) return; // send metrics after a 1s debounce const timeoutId = window.setTimeout(() => { _PosthogAnalytics.PosthogAnalytics.instance.trackEvent({ eventName: "WebSearch", viaSpotlight, numResults, queryLength }); }, 1000); return () => { clearTimeout(timeoutId); }; }, [numResults, queryLength, viaSpotlight]); }; exports.useWebSearchMetrics = useWebSearchMetrics; const findVisibleRooms = (cli, msc3946ProcessDynamicPredecessor) => { return cli.getVisibleRooms(msc3946ProcessDynamicPredecessor).filter(room => { // Do not show local rooms if ((0, _isLocalRoom.isLocalRoom)(room)) return false; // TODO we may want to put invites in their own list return room.getMyMembership() === _types.KnownMembership.Join || room.getMyMembership() == _types.KnownMembership.Invite; }); }; const findVisibleRoomMembers = (visibleRooms, cli, filterDMs = true) => { return Object.values(visibleRooms.filter(room => !filterDMs || !_DMRoomMap.default.shared().getUserIdForRoomId(room.roomId)).reduce((members, room) => { for (const member of room.getJoinedMembers()) { members[member.userId] = member; } return members; }, {})).filter(it => it.userId !== cli.getUserId()); }; const roomAriaUnreadLabel = (room, notification) => { if (notification.hasMentions) { return (0, _languageHandler._t)("a11y|n_unread_messages_mentions", { count: notification.count }); } else if (notification.hasUnreadCount) { return (0, _languageHandler._t)("a11y|n_unread_messages", { count: notification.count }); } else if (notification.isUnread) { return (0, _languageHandler._t)("a11y|unread_messages"); } else { return undefined; } }; const canAskToJoin = joinRule => { return _SettingsStore.default.getValue("feature_ask_to_join") && _matrix.JoinRule.Knock === joinRule; }; const SpotlightDialog = ({ initialText = "", initialFilter = null, onFinished }) => { const inputRef = (0, _react.useRef)(null); const scrollContainerRef = (0, _react.useRef)(null); const cli = _MatrixClientPeg.MatrixClientPeg.safeGet(); const rovingContext = (0, _react.useContext)(_RovingTabIndex.RovingTabIndexContext); const [query, _setQuery] = (0, _react.useState)(initialText); const [recentSearches, clearRecentSearches] = (0, _useRecentSearches.useRecentSearches)(); const [filter, setFilterInternal] = (0, _react.useState)(initialFilter); const setFilter = (0, _react.useCallback)(filter => { setFilterInternal(filter); inputRef.current?.focus(); scrollContainerRef.current?.scrollTo?.({ top: 0 }); }, []); const memberComparator = (0, _react.useMemo)(() => { const activityScores = (0, _SortMembers.buildActivityScores)(cli); const memberScores = (0, _SortMembers.buildMemberScores)(cli); return (0, _SortMembers.compareMembers)(activityScores, memberScores); }, [cli]); const msc3946ProcessDynamicPredecessor = (0, _useSettings.useFeatureEnabled)("feature_dynamic_room_predecessors"); const ownInviteLink = (0, _Permalinks.makeUserPermalink)(cli.getUserId()); const [inviteLinkCopied, setInviteLinkCopied] = (0, _react.useState)(false); const trimmedQuery = (0, _react.useMemo)(() => query.trim(), [query]); const [supportsSpaceFiltering, setSupportsSpaceFiltering] = (0, _react.useState)(true); // assume it does until we find out it doesn't (0, _react.useEffect)(() => { cli.isVersionSupported("v1.4").then(supported => { return supported || cli.doesServerSupportUnstableFeature("org.matrix.msc3827.stable"); }).then(supported => { setSupportsSpaceFiltering(supported); }); }, [cli]); const { loading: publicRoomsLoading, publicRooms, protocols, config, setConfig, search: searchPublicRooms, error: publicRoomsError } = (0, _usePublicRoomDirectory.usePublicRoomDirectory)(); const { loading: peopleLoading, users: userDirectorySearchResults, search: searchPeople } = (0, _useUserDirectory.useUserDirectory)(); const { loading: profileLoading, profile, search: searchProfileInfo } = (0, _useProfileInfo.useProfileInfo)(); const searchParams = (0, _react.useMemo)(() => [{ query: trimmedQuery, roomTypes: getRoomTypes(filter), limit: SECTION_LIMIT }], [trimmedQuery, filter]); (0, _useDebouncedCallback.useDebouncedCallback)(filter === _Filter.Filter.PublicRooms || filter === _Filter.Filter.PublicSpaces, searchPublicRooms, searchParams); (0, _useDebouncedCallback.useDebouncedCallback)(filter === _Filter.Filter.People, searchPeople, searchParams); (0, _useDebouncedCallback.useDebouncedCallback)(filter === _Filter.Filter.People, searchProfileInfo, searchParams); const possibleResults = (0, _react.useMemo)(() => { const visibleRooms = findVisibleRooms(cli, msc3946ProcessDynamicPredecessor); const roomResults = visibleRooms.map(toRoomResult); const userResults = []; // If we already have a DM with the user we're looking for, we will show that DM instead of the user themselves const alreadyAddedUserIds = roomResults.reduce((userIds, result) => { const userId = _DMRoomMap.default.shared().getUserIdForRoomId(result.room.roomId); if (!userId) return userIds; if (result.room.getJoinedMemberCount() > 2) return userIds; userIds.set(userId, result); return userIds; }, new Map()); function addUserResults(users, alreadyFiltered) { for (const user of users) { // Make sure we don't have any user more than once if (alreadyAddedUserIds.has(user.userId)) { const result = alreadyAddedUserIds.get(user.userId); if (alreadyFiltered && isMemberResult(result) && !result.alreadyFiltered) { // But if they were added as not yet filtered then mark them as already filtered to avoid // culling this result based on local filtering. result.alreadyFiltered = true; } continue; } const result = toMemberResult(user, alreadyFiltered); alreadyAddedUserIds.set(user.userId, result); userResults.push(result); } } addUserResults(findVisibleRoomMembers(visibleRooms, cli), false); addUserResults(userDirectorySearchResults, true); if (profile) { addUserResults([new _directMessages.DirectoryMember(profile)], true); } return [..._SpaceStore.default.instance.enabledMetaSpaces.map(spaceKey => ({ section: Section.Spaces, filter: [], avatar: /*#__PURE__*/_react.default.createElement("div", { className: (0, _classnames.default)("mx_SpotlightDialog_metaspaceResult", `mx_SpotlightDialog_metaspaceResult_${spaceKey}`) }), name: (0, _spaces.getMetaSpaceName)(spaceKey, _SpaceStore.default.instance.allRoomsInHome), onClick() { _SpaceStore.default.instance.setActiveSpace(spaceKey); } })), ...roomResults, ...userResults, ...publicRooms.map(toPublicRoomResult)].filter(result => filter === null || result.filter.includes(filter)); }, [cli, userDirectorySearchResults, profile, publicRooms, filter, msc3946ProcessDynamicPredecessor]); const results = (0, _react.useMemo)(() => { const results = { [Section.People]: [], [Section.Rooms]: [], [Section.Spaces]: [], [Section.Suggestions]: [], [Section.PublicRoomsAndSpaces]: [] }; // Group results in their respective sections if (trimmedQuery) { const lcQuery = trimmedQuery.toLowerCase(); const normalizedQuery = (0, _utils.normalize)(trimmedQuery); possibleResults.forEach(entry => { if (isRoomResult(entry)) { // If the room is a DM with a user that is part of the user directory search results, // we can assume the user is a relevant result, so include the DM with them too. const userId = _DMRoomMap.default.shared().getUserIdForRoomId(entry.room.roomId); if (!userDirectorySearchResults.some(user => user.userId === userId)) { if (!entry.room.normalizedName?.includes(normalizedQuery) && !entry.room.getCanonicalAlias()?.toLowerCase().includes(lcQuery) && !entry.query?.some(q => q.includes(lcQuery))) { return; // bail, does not match query } } } else if (isMemberResult(entry)) { if (!entry.alreadyFiltered && !entry.query?.some(q => q.includes(lcQuery))) return; // bail, does not match query } else if (isPublicRoomResult(entry)) { if (!entry.query?.some(q => q.includes(lcQuery))) return; // bail, does not match query } else { if (!entry.name.toLowerCase().includes(lcQuery) && !entry.query?.some(q => q.includes(lcQuery))) return; // bail, does not match query } results[entry.section].push(entry); }); } else if (filter === _Filter.Filter.PublicRooms || filter === _Filter.Filter.PublicSpaces) { // return all results for public rooms if no query is given possibleResults.forEach(entry => { if (isPublicRoomResult(entry)) { results[entry.section].push(entry); } }); } else if (filter === _Filter.Filter.People) { // return all results for people if no query is given possibleResults.forEach(entry => { if (isMemberResult(entry)) { results[entry.section].push(entry); } }); } // Sort results by most recent activity const myUserId = cli.getSafeUserId(); for (const resultArray of Object.values(results)) { resultArray.sort((a, b) => { if (isRoomResult(a) || isRoomResult(b)) { // Room results should appear at the top of the list if (!isRoomResult(b)) return -1; if (!isRoomResult(a)) return -1; return recentAlgorithm.getLastTs(b.room, myUserId) - recentAlgorithm.getLastTs(a.room, myUserId); } else if (isMemberResult(a) || isMemberResult(b)) { // Member results should appear just after room results if (!isMemberResult(b)) return -1; if (!isMemberResult(a)) return -1; return memberComparator(a.member, b.member); } return 0; }); } return results; }, [trimmedQuery, filter, cli, possibleResults, userDirectorySearchResults, memberComparator]); const numResults = (0, _lodash.sum)(Object.values(results).map(it => it.length)); useWebSearchMetrics(numResults, query.length, true); const activeSpace = _SpaceStore.default.instance.activeSpaceRoom; const [spaceResults, spaceResultsLoading] = (0, _useSpaceResults.useSpaceResults)(activeSpace ?? undefined, query); const setQuery = e => { const newQuery = (0, _SearchInput.transformSearchTerm)(e.currentTarget.value); _setQuery(newQuery); }; (0, _react.useEffect)(() => { setTimeout(() => { const ref = rovingContext.state.refs[0]; if (ref) { rovingContext.dispatch({ type: _RovingTabIndex.Type.SetFocus, payload: { ref } }); ref.current?.scrollIntoView?.({ block: "nearest" }); } }); // we intentionally ignore changes to the rovingContext for the purpose of this hook // we only want to reset the focus whenever the results or filters change // eslint-disable-next-line }, [results, filter]); const viewRoom = (room, persist = false, viaKeyboard = false) => { if (persist) { const recents = new Set(_SettingsStore.default.getValue("SpotlightSearch.recentSearches", null).reverse()); // remove & add the room to put it at the end recents.delete(room.roomId); recents.add(room.roomId); _SettingsStore.default.setValue("SpotlightSearch.recentSearches", null, _SettingLevel.SettingLevel.ACCOUNT, Array.from(recents).reverse().slice(0, MAX_RECENT_SEARCHES)); } _dispatcher.default.dispatch({ action: _actions.Action.ViewRoom, metricsTrigger: "WebUnifiedSearch", metricsViaKeyboard: viaKeyboard, room_id: room.roomId, room_alias: room.roomAlias, auto_join: room.autoJoin && !canAskToJoin(room.joinRule), should_peek: room.shouldPeek, via_servers: room.viaServers }); if (canAskToJoin(room.joinRule)) { _dispatcher.default.dispatch({ action: _actions.Action.PromptAskToJoin }); } onFinished(); }; let otherSearchesSection; if (trimmedQuery || filter !== _Filter.Filter.PublicRooms && filter !== _Filter.Filter.PublicSpaces) { otherSearchesSection = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_otherSearches", role: "group", "aria-labelledby": "mx_SpotlightDialog_section_otherSearches" }, /*#__PURE__*/_react.default.createElement("h4", { id: "mx_SpotlightDialog_section_otherSearches" }, trimmedQuery ? (0, _languageHandler._t)("spotlight_dialog|heading_with_query", { query }) : (0, _languageHandler._t)("spotlight_dialog|heading_without_query")), /*#__PURE__*/_react.default.createElement("div", null, filter !== _Filter.Filter.PublicSpaces && supportsSpaceFiltering && /*#__PURE__*/_react.default.createElement(_Option.Option, { id: "mx_SpotlightDialog_button_explorePublicSpaces", className: "mx_SpotlightDialog_explorePublicSpaces", onClick: () => setFilter(_Filter.Filter.PublicSpaces) }, filterToLabel(_Filter.Filter.PublicSpaces)), filter !== _Filter.Filter.PublicRooms && /*#__PURE__*/_react.default.createElement(_Option.Option, { id: "mx_SpotlightDialog_button_explorePublicRooms", className: "mx_SpotlightDialog_explorePublicRooms", onClick: () => setFilter(_Filter.Filter.PublicRooms) }, filterToLabel(_Filter.Filter.PublicRooms)), filter !== _Filter.Filter.People && /*#__PURE__*/_react.default.createElement(_Option.Option, { id: "mx_SpotlightDialog_button_startChat", className: "mx_SpotlightDialog_startChat", onClick: () => setFilter(_Filter.Filter.People) }, filterToLabel(_Filter.Filter.People)))); } let content; if (trimmedQuery || filter !== null) { const resultMapper = result => { if (isRoomResult(result)) { const notification = _RoomNotificationStateStore.RoomNotificationStateStore.instance.getRoomState(result.room); const unreadLabel = roomAriaUnreadLabel(result.room, notification); const ariaProperties = { "aria-label": unreadLabel ? `${result.room.name} ${unreadLabel}` : result.room.name, "aria-describedby": `mx_SpotlightDialog_button_result_${result.room.roomId}_details` }; return /*#__PURE__*/_react.default.createElement(_Option.Option, (0, _extends2.default)({ id: `mx_SpotlightDialog_button_result_${result.room.roomId}`, key: `${Section[result.section]}-${result.room.roomId}`, onClick: ev => { viewRoom({ roomId: result.room.roomId }, true, ev?.type !== "click"); }, endAdornment: /*#__PURE__*/_react.default.createElement(_RoomResultContextMenus.RoomResultContextMenus, { room: result.room }) }, ariaProperties), /*#__PURE__*/_react.default.createElement(_DecoratedRoomAvatar.default, { room: result.room, size: AVATAR_SIZE, tooltipProps: { tabIndex: -1 } }), result.room.name, /*#__PURE__*/_react.default.createElement(_NotificationBadge.default, { notification: notification }), /*#__PURE__*/_react.default.createElement(_RoomContextDetails.RoomContextDetails, { id: `mx_SpotlightDialog_button_result_${result.room.roomId}_details`, className: "mx_SpotlightDialog_result_details", room: result.room })); } if (isMemberResult(result)) { return /*#__PURE__*/_react.default.createElement(_Option.Option, { id: `mx_SpotlightDialog_button_result_${result.member.userId}`, key: `${Section[result.section]}-${result.member.userId}`, onClick: () => { (0, _directMessages.startDmOnFirstMessage)(cli, [result.member]); onFinished(); }, "aria-label": result.member instanceof _matrix.RoomMember ? result.member.rawDisplayName : result.member.name, "aria-describedby": `mx_SpotlightDialog_button_result_${result.member.userId}_details` }, /*#__PURE__*/_react.default.createElement(_SearchResultAvatar.SearchResultAvatar, { user: result.member, size: AVATAR_SIZE }), result.member instanceof _matrix.RoomMember ? result.member.rawDisplayName : result.member.name, /*#__PURE__*/_react.default.createElement("div", { id: `mx_SpotlightDialog_button_result_${result.member.userId}_details`, className: "mx_SpotlightDialog_result_details" }, result.member.userId)); } if (isPublicRoomResult(result)) { const clientRoom = cli.getRoom(result.publicRoom.room_id); const joinRule = result.publicRoom.join_rule; // Element Web currently does not allow guests to join rooms, so we // instead show them view buttons for all rooms. If the room is not // world readable, a modal will appear asking you to register first. If // it is readable, the preview appears as normal. const showViewButton = clientRoom?.getMyMembership() === _types.KnownMembership.Join || result.publicRoom.world_readable && !canAskToJoin(joinRule) || cli.isGuest(); const listener = ev => { ev.stopPropagation(); const { publicRoom } = result; viewRoom({ roomAlias: publicRoom.canonical_alias || publicRoom.aliases?.[0], roomId: publicRoom.room_id, autoJoin: !result.publicRoom.world_readable && !cli.isGuest(), shouldPeek: result.publicRoom.world_readable || cli.isGuest(), viaServers: config ? [config.roomServer] : undefined, joinRule }, true, ev.type !== "click"); }; let buttonLabel; if (showViewButton) { buttonLabel = (0, _languageHandler._t)("action|view"); } else { buttonLabel = canAskToJoin(joinRule) ? (0, _languageHandler._t)("action|ask_to_join") : (0, _languageHandler._t)("action|join"); } return /*#__PURE__*/_react.default.createElement(_Option.Option, { id: `mx_SpotlightDialog_button_result_${result.publicRoom.room_id}`, className: "mx_SpotlightDialog_result_multiline", key: `${Section[result.section]}-${result.publicRoom.room_id}`, onClick: listener, endAdornment: /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, { kind: showViewButton ? "primary_outline" : "primary", onClick: listener, tabIndex: -1 }, buttonLabel), "aria-labelledby": `mx_SpotlightDialog_button_result_${result.publicRoom.room_id}_name`, "aria-describedby": `mx_SpotlightDialog_button_result_${result.publicRoom.room_id}_alias`, "aria-details": `mx_SpotlightDialog_button_result_${result.publicRoom.room_id}_details` }, /*#__PURE__*/_react.default.createElement(_RoomAvatar.default, { className: "mx_SearchResultAvatar", oobData: { roomId: result.publicRoom.room_id, name: result.publicRoom.name, avatarUrl: result.publicRoom.avatar_url, roomType: result.publicRoom.room_type }, size: AVATAR_SIZE }), /*#__PURE__*/_react.default.createElement(_PublicRoomResultDetails.PublicRoomResultDetails, { room: result.publicRoom, labelId: `mx_SpotlightDialog_button_result_${result.publicRoom.room_id}_name`, descriptionId: `mx_SpotlightDialog_button_result_${result.publicRoom.room_id}_alias`, detailsId: `mx_SpotlightDialog_button_result_${result.publicRoom.room_id}_details` })); } // IResult case return /*#__PURE__*/_react.default.createElement(_Option.Option, { id: `mx_SpotlightDialog_button_result_${result.name}`, key: `${Section[result.section]}-${result.name}`, onClick: result.onClick ?? null }, result.avatar, result.name, result.description); }; let peopleSection; if (results[Section.People].length) { peopleSection = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_results", role: "group", "aria-labelledby": "mx_SpotlightDialog_section_people" }, /*#__PURE__*/_react.default.createElement("h4", { id: "mx_SpotlightDialog_section_people" }, (0, _languageHandler._t)("invite|recents_section")), /*#__PURE__*/_react.default.createElement("div", null, results[Section.People].slice(0, SECTION_LIMIT).map(resultMapper))); } let suggestionsSection; if (results[Section.Suggestions].length && filter === _Filter.Filter.People) { suggestionsSection = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_results", role: "group", "aria-labelledby": "mx_SpotlightDialog_section_suggestions" }, /*#__PURE__*/_react.default.createElement("h4", { id: "mx_SpotlightDialog_section_suggestions" }, (0, _languageHandler._t)("common|suggestions")), /*#__PURE__*/_react.default.createElement("div", null, results[Section.Suggestions].slice(0, SECTION_LIMIT).map(resultMapper))); } let roomsSection; if (results[Section.Rooms].length) { roomsSection = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_results", role: "group", "aria-labelledby": "mx_SpotlightDialog_section_rooms" }, /*#__PURE__*/_react.default.createElement("h4", { id: "mx_SpotlightDialog_section_rooms" }, (0, _languageHandler._t)("common|rooms")), /*#__PURE__*/_react.default.createElement("div", null, results[Section.Rooms].slice(0, SECTION_LIMIT).map(resultMapper))); } let spacesSection; if (results[Section.Spaces].length) { spacesSection = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_results", role: "group", "aria-labelledby": "mx_SpotlightDialog_section_spaces" }, /*#__PURE__*/_react.default.createElement("h4", { id: "mx_SpotlightDialog_section_spaces" }, (0, _languageHandler._t)("spotlight_dialog|spaces_title")), /*#__PURE__*/_react.default.createElement("div", null, results[Section.Spaces].slice(0, SECTION_LIMIT).map(resultMapper))); } let publicRoomsSection; if (filter === _Filter.Filter.PublicRooms || filter === _Filter.Filter.PublicSpaces) { let content; if (publicRoomsError) { content = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_otherSearches_messageSearchText" }, filter === _Filter.Filter.PublicRooms ? (0, _languageHandler._t)("spotlight_dialog|failed_querying_public_rooms") : (0, _languageHandler._t)("spotlight_dialog|failed_querying_public_spaces")); } else { content = results[Section.PublicRoomsAndSpaces].slice(0, SECTION_LIMIT).map(resultMapper); } publicRoomsSection = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_results", role: "group", "aria-labelledby": "mx_SpotlightDialog_section_publicRooms" }, /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_sectionHeader" }, /*#__PURE__*/_react.default.createElement("h4", { id: "mx_SpotlightDialog_section_publicRooms" }, (0, _languageHandler._t)("common|suggestions")), /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_options" }, /*#__PURE__*/_react.default.createElement(_NetworkDropdown.NetworkDropdown, { protocols: protocols, config: config ?? null, setConfig: setConfig }))), /*#__PURE__*/_react.default.createElement("div", null, content)); } let spaceRoomsSection; if (spaceResults.length && activeSpace && filter === null) { spaceRoomsSection = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_results", role: "group", "aria-labelledby": "mx_SpotlightDialog_section_spaceRooms" }, /*#__PURE__*/_react.default.createElement("h4", { id: "mx_SpotlightDialog_section_spaceRooms" }, (0, _languageHandler._t)("spotlight_dialog|other_rooms_in_space", { spaceName: activeSpace.name })), /*#__PURE__*/_react.default.createElement("div", null, spaceResults.slice(0, SECTION_LIMIT).map(room => /*#__PURE__*/_react.default.createElement(_Option.Option, { id: `mx_SpotlightDialog_button_result_${room.room_id}`, key: room.room_id, onClick: ev => { viewRoom({ roomId: room.room_id }, true, ev?.type !== "click"); } }, /*#__PURE__*/_react.default.createElement(_BaseAvatar.default, { name: room.name, idName: room.room_id, url: room.avatar_url ? (0, _Media.mediaFromMxc)(room.avatar_url).getSquareThumbnailHttp(parseInt(AVATAR_SIZE, 10)) : null, size: AVATAR_SIZE }), room.name || room.canonical_alias, room.name && room.canonical_alias && /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_result_details" }, room.canonical_alias))), spaceResultsLoading && /*#__PURE__*/_react.default.createElement(_Spinner.default, null))); } let joinRoomSection; if (trimmedQuery.startsWith("#") && trimmedQuery.includes(":") && (!(0, _RoomAliasCache.getCachedRoomIDForAlias)(trimmedQuery) || !cli.getRoom((0, _RoomAliasCache.getCachedRoomIDForAlias)(trimmedQuery)))) { joinRoomSection = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_otherSearches", role: "group" }, /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Option.Option, { id: "mx_SpotlightDialog_button_joinRoomAlias", className: "mx_SpotlightDialog_joinRoomAlias", onClick: ev => { _dispatcher.default.dispatch({ action: _actions.Action.ViewRoom, room_alias: trimmedQuery, auto_join: true, metricsTrigger: "WebUnifiedSearch", metricsViaKeyboard: ev?.type !== "click" }); onFinished(); } }, (0, _languageHandler._t)("spotlight_dialog|join_button_text", { roomAddress: trimmedQuery })))); } let hiddenResultsSection; if (filter === _Filter.Filter.People) { hiddenResultsSection = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_hiddenResults", role: "group" }, /*#__PURE__*/_react.default.createElement("h4", null, (0, _languageHandler._t)("spotlight_dialog|result_may_be_hidden_privacy_warning")), /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_otherSearches_messageSearchText" }, (0, _languageHandler._t)("spotlight_dialog|cant_find_person_helpful_hint")), /*#__PURE__*/_react.default.createElement(_TooltipOption.TooltipOption, { id: "mx_SpotlightDialog_button_inviteLink", className: "mx_SpotlightDialog_inviteLink", onClick: () => { setInviteLinkCopied(true); (0, _strings.copyPlaintext)(ownInviteLink); }, onTooltipOpenChange: open => { if (!open) setInviteLinkCopied(false); }, title: inviteLinkCopied ? (0, _languageHandler._t)("common|copied") : (0, _languageHandler._t)("action|copy") }, /*#__PURE__*/_react.default.createElement("span", { className: "mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline" }, (0, _languageHandler._t)("spotlight_dialog|copy_link_text")))); } else if (trimmedQuery && (filter === _Filter.Filter.PublicRooms || filter === _Filter.Filter.PublicSpaces)) { hiddenResultsSection = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_hiddenResults", role: "group" }, /*#__PURE__*/_react.default.createElement("h4", null, (0, _languageHandler._t)("spotlight_dialog|result_may_be_hidden_warning")), /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_otherSearches_messageSearchText" }, (0, _languageHandler._t)("spotlight_dialog|cant_find_room_helpful_hint")), /*#__PURE__*/_react.default.createElement(_Option.Option, { id: "mx_SpotlightDialog_button_createNewRoom", className: "mx_SpotlightDialog_createRoom", onClick: () => _dispatcher.default.dispatch({ action: "view_create_room", public: true, defaultName: (0, _lodash.capitalize)(trimmedQuery) }) }, /*#__PURE__*/_react.default.createElement("span", { className: "mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline" }, (0, _languageHandler._t)("spotlight_dialog|create_new_room_button")))); } let groupChatSection; if (filter === _Filter.Filter.People) { groupChatSection = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_otherSearches", role: "group", "aria-labelledby": "mx_SpotlightDialog_section_groupChat" }, /*#__PURE__*/_react.default.createElement("h4", { id: "mx_SpotlightDialog_section_groupChat" }, (0, _languageHandler._t)("spotlight_dialog|group_chat_section_title")), /*#__PURE__*/_react.default.createElement(_Option.Option, { id: "mx_SpotlightDialog_button_startGroupChat", className: "mx_SpotlightDialog_startGroupChat", onClick: () => (0, _RoomInvite.showStartChatInviteDialog)(trimmedQuery) }, (0, _languageHandler._t)("spotlight_dialog|start_group_chat_button"))); } let messageSearchSection; if (filter === null) { messageSearchSection = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_otherSearches", role: "group", "aria-labelledby": "mx_SpotlightDialog_section_messageSearch" }, /*#__PURE__*/_react.default.createElement("h4", { id: "mx_SpotlightDialog_section_messageSearch" }, (0, _languageHandler._t)("spotlight_dialog|message_search_section_title")), /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_otherSearches_messageSearchText" }, (0, _languageHandler._t)("spotlight_dialog|search_messages_hint", {}, { icon: () => /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_otherSearches_messageSearchIcon" }) }))); } content = /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, peopleSection, suggestionsSection, roomsSection, spacesSection, spaceRoomsSection, publicRoomsSection, joinRoomSection, hiddenResultsSection, otherSearchesSection, groupChatSection, messageSearchSection); } else { let recentSearchesSection; if (recentSearches.length) { recentSearchesSection = /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_recentSearches", role: "group" // Firefox sometimes makes this element focusable due to overflow, // so force it out of tab order by default. , tabIndex: -1, "aria-labelledby": "mx_SpotlightDialog_section_recentSearches" }, /*#__PURE__*/_react.default.createElement("h4", null, /*#__PURE__*/_react.default.createElement("span", { id: "mx_SpotlightDialog_section_recentSearches" }, (0, _languageHandler._t)("spotlight_dialog|recent_searches_section_title")), /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, { kind: "link", onClick: clearRecentSearches }, (0, _languageHandler._t)("action|clear"))), /*#__PURE__*/_react.default.createElement("div", null, recentSearches.map(room => { const notification = _RoomNotificationStateStore.RoomNotificationStateStore.instance.getRoomState(room); const unreadLabel = roomAriaUnreadLabel(room, notification); const ariaProperties = { "aria-label": unreadLabel ? `${room.name} ${unreadLabel}` : room.name, "aria-describedby": `mx_SpotlightDialog_button_recentSearch_${room.roomId}_details` }; return /*#__PURE__*/_react.default.createElement(_Option.Option, (0, _extends2.default)({ id: `mx_SpotlightDialog_button_recentSearch_${room.roomId}`, key: room.roomId, onClick: ev => { viewRoom({ roomId: room.roomId }, true, ev?.type !== "click"); }, endAdornment: /*#__PURE__*/_react.default.createElement(_RoomResultContextMenus.RoomResultContextMenus, { room: room }) }, ariaProperties), /*#__PURE__*/_react.default.createElement(_DecoratedRoomAvatar.default, { room: room, size: AVATAR_SIZE, tooltipProps: { tabIndex: -1 } }), room.name, /*#__PURE__*/_react.default.createElement(_NotificationBadge.default, { notification: notification }), /*#__PURE__*/_react.default.createElement(_RoomContextDetails.RoomContextDetails, { id: `mx_SpotlightDialog_button_recentSearch_${room.roomId}_details`, className: "mx_SpotlightDialog_result_details", room: room })); }))); } content = /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_section mx_SpotlightDialog_recentlyViewed", role: "group", "aria-labelledby": "mx_SpotlightDialog_section_recentlyViewed" }, /*#__PURE__*/_react.default.createElement("h4", { id: "mx_SpotlightDialog_section_recentlyViewed" }, (0, _languageHandler._t)("spotlight_dialog|recently_viewed_section_title")), /*#__PURE__*/_react.default.createElement("div", null, _BreadcrumbsStore.BreadcrumbsStore.instance.rooms.filter(r => r.roomId !== _SDKContext.SdkContextClass.instance.roomViewStore.getRoomId()).map(room => /*#__PURE__*/_react.default.createElement(_TooltipOption.TooltipOption, { id: `mx_SpotlightDialog_button_recentlyViewed_${room.roomId}`, title: room.name, key: room.roomId, onClick: ev => { viewRoom({ roomId: room.roomId }, false, ev.type !== "click"); } }, /*#__PURE__*/_react.default.createElement(_DecoratedRoomAvatar.default, { room: room, size: "32px", tooltipProps: { tabIndex: -1 } }), room.name)))), recentSearchesSection, otherSearchesSection); } const onDialogKeyDown = ev => { const navigationAction = (0, _KeyBindingsManager.getKeyBindingsManager)().getNavigationAction(ev); switch (navigationAction) { case _KeyboardShortcuts.KeyBindingAction.FilterRooms: ev.stopPropagation(); ev.preventDefault(); onFinished(); break; } let ref; const accessibilityAction = (0, _KeyBindingsManager.getKeyBindingsManager)().getAccessibilityAction(ev); switch (accessibilityAction) { case _KeyboardShortcuts.KeyBindingAction.Escape: ev.stopPropagation(); ev.preventDefault(); onFinished(); break; case _KeyboardShortcuts.KeyBindingAction.ArrowUp: case _KeyboardShortcuts.KeyBindingAction.ArrowDown: ev.stopPropagation(); ev.preventDefault(); if (rovingContext.state.activeRef && rovingContext.state.refs.length > 0) { let refs = rovingContext.state.refs; if (!query && !filter !== null) { // If the current selection is not in the recently viewed row then only include the // first recently viewed so that is the target when the user is switching into recently viewed. const keptRecentlyViewedRef = refIsForRecentlyViewed(rovingContext.state.activeRef) ? rovingContext.state.activeRef : refs.find(refIsForRecentlyViewed); // exclude all other recently viewed items from the list so up/down arrows skip them refs = refs.filter(ref => ref === keptRecentlyViewedRef || !refIsForRecentlyViewed(ref)); } const idx = refs.indexOf(rovingContext.state.activeRef); ref = (0, _RovingTabIndex.findSiblingElement)(refs, idx + (accessibilityAction === _KeyboardShortcuts.KeyBindingAction.ArrowUp ? -1 : 1)); } break; case _KeyboardShortcuts.KeyBindingAction.ArrowLeft: case _KeyboardShortcuts.KeyBindingAction.ArrowRight: // only handle these keys when we are in the recently viewed row of options if (!query && !filter !== null && rovingContext.state.activeRef && rovingContext.state.refs.length > 0 && refIsForRecentlyViewed(rovingContext.state.activeRef)) { // we only intercept left/right arrows when the field is empty, and they'd do nothing anyway ev.stopPropagation(); ev.preventDefault(); const refs = rovingContext.state.refs.filter(refIsForRecentlyViewed); const idx = refs.indexOf(rovingContext.state.activeRef); ref = (0, _RovingTabIndex.findSiblingElement)(refs, idx + (accessibilityAction === _KeyboardShortcuts.KeyBindingAction.ArrowLeft ? -1 : 1)); } break; } if (ref) { rovingContext.dispatch({ type: _RovingTabIndex.Type.SetFocus, payload: { ref } }); ref.current?.scrollIntoView({ block: "nearest" }); } }; const onKeyDown = ev => { const action = (0, _KeyBindingsManager.getKeyBindingsManager)().getAccessibilityAction(ev); switch (action) { case _KeyboardShortcuts.KeyBindingAction.Backspace: if (!query && filter !== null) { ev.stopPropagation(); ev.preventDefault(); setFilter(null); } break; case _KeyboardShortcuts.KeyBindingAction.Enter: ev.stopPropagation(); ev.preventDefault(); rovingContext.state.activeRef?.current?.click(); break; } }; const activeDescendant = rovingContext.state.activeRef?.current?.id; return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", { id: "mx_SpotlightDialog_keyboardPrompt" }, (0, _languageHandler._t)("spotlight_dialog|keyboard_scroll_hint", {}, { arrows: () => /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("kbd", null, "\u2193"), /*#__PURE__*/_react.default.createElement("kbd", null, "\u2191"), !filter !== null && !query && /*#__PURE__*/_react.default.createElement("kbd", null, "\u2190"), !filter !== null && !query && /*#__PURE__*/_react.default.createElement("kbd", null, "\u2192")) })), /*#__PURE__*/_react.default.createElement(_BaseDialog.default, { className: "mx_SpotlightDialog", onFinished: onFinished, hasCancel: false, onKeyDown: onDialogKeyDown, screenName: "UnifiedSearch", "aria-label": (0, _languageHandler._t)("spotlight_dialog|search_dialog") }, /*#__PURE__*/_react.default.createElement("div", { className: "mx_SpotlightDialog_searchBox mx_textinput" }, filter !== null && /*#__PURE__*/_react.default.createElement("div", { className: (0, _classnames.default)("mx_SpotlightDialog_filter", { mx_SpotlightDialog_filterPeople: filter === _Filter.Filter.People, mx_SpotlightDialog_filterPublicRooms: filter === _Filter.Filter.PublicRooms, mx_SpotlightDialog_filterPublicSpaces: filter === _Filter.Filter.PublicSpaces }) }, /*#__PURE__*/_react.default.createElement("span", null, filterToLabel(filter)), /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, { tabIndex: -1, alt: (0, _languageHandler._t)("spotlight_dialog|remove_filter", { filter: filterToLabel(filter) }), className: "mx_SpotlightDialog_filter--close", onClick: () => setFilter(null) })), /*#__PURE__*/_react.default.createElement("input", { ref: inputRef, autoFocus: true, type: "text", autoComplete: "off", autoCapitalize: "off", autoCorrect: "off", spellCheck: "false", placeholder: (0, _languageHandler._t)("action|search"), value: query, onChange: setQuery, onKeyDown: onKeyDown, "aria-owns": "mx_SpotlightDialog_content", "aria-activedescendant": activeDescendant, "aria-label": (0, _languageHandler._t)("action|search"), "aria-describedby": "mx_SpotlightDialog_keyboardPrompt" }), (publicRoomsLoading || peopleLoading || profileLoading) && /*#__PURE__*/_react.default.createElement(_Spinner.default, { w: 24, h: 24 })), /*#__PURE__*/_react.default.createElement("div", { ref: scrollContainerRef, id: "mx_SpotlightDialog_content", role: "listb