Implement MSC3827: Filtering of `/publicRooms` by room type (#8866)

pull/28217/head
Šimon Brandner 2022-06-24 17:08:00 +02:00 committed by GitHub
parent 18c21d77cd
commit 663bca559f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 96 additions and 21 deletions

View File

@ -168,6 +168,11 @@ limitations under the License.
justify-content: space-between;
align-items: center;
margin-bottom: $spacing-8;
.mx_SpotlightDialog_options {
display: flex;
gap: $spacing-4;
}
}
& + .mx_SpotlightDialog_section {

View File

@ -18,7 +18,7 @@ import classNames from "classnames";
import { capitalize, sum } from "lodash";
import { WebSearch as WebSearchEvent } from "@matrix-org/analytics-events/types/typescript/WebSearch";
import { IHierarchyRoom } from "matrix-js-sdk/src/@types/spaces";
import { IPublicRoomsChunkRoom, MatrixClient, RoomMember } from "matrix-js-sdk/src/matrix";
import { IPublicRoomsChunkRoom, MatrixClient, RoomMember, RoomType } from "matrix-js-sdk/src/matrix";
import { Room } from "matrix-js-sdk/src/models/room";
import { normalize } from "matrix-js-sdk/src/utils";
import React, {
@ -89,6 +89,8 @@ import { Option } from "./Option";
import { PublicRoomResultDetails } from "./PublicRoomResultDetails";
import { RoomResultDetails } from "./RoomResultDetails";
import { TooltipOption } from "./TooltipOption";
import LabelledCheckbox from "../../elements/LabelledCheckbox";
import { useFeatureEnabled } from "../../../../hooks/useSettings";
const MAX_RECENT_SEARCHES = 10;
const SECTION_LIMIT = 50; // only show 50 results per section for performance reasons
@ -103,6 +105,18 @@ function refIsForRecentlyViewed(ref: RefObject<HTMLElement>): boolean {
return ref.current?.id?.startsWith("mx_SpotlightDialog_button_recentlyViewed_") === true;
}
function getRoomTypes(showRooms: boolean, showSpaces: boolean): Set<RoomType | null> | null {
const roomTypes = new Set<RoomType | null>();
// This is what servers not implementing MSC3827 are expecting
if (showRooms && !showSpaces) return null;
if (showRooms) roomTypes.add(null);
if (showSpaces) roomTypes.add(RoomType.Space);
return roomTypes;
}
enum Section {
People,
Rooms,
@ -277,14 +291,19 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
const [inviteLinkCopied, setInviteLinkCopied] = useState<boolean>(false);
const trimmedQuery = useMemo(() => query.trim(), [query]);
const exploringPublicSpacesEnabled = useFeatureEnabled("feature_exploring_public_spaces");
const { loading: publicRoomsLoading, publicRooms, protocols, config, setConfig, search: searchPublicRooms } =
usePublicRoomDirectory();
const [showRooms, setShowRooms] = useState(true);
const [showSpaces, setShowSpaces] = useState(false);
const { loading: peopleLoading, users, search: searchPeople } = useUserDirectory();
const { loading: profileLoading, profile, search: searchProfileInfo } = useProfileInfo();
const searchParams: [IDirectoryOpts] = useMemo(() => ([{
query: trimmedQuery,
roomTypes: getRoomTypes(showRooms, showSpaces),
limit: SECTION_LIMIT,
}]), [trimmedQuery]);
}]), [trimmedQuery, showRooms, showSpaces]);
useDebouncedCallback(
filter === Filter.PublicRooms,
searchPublicRooms,
@ -624,15 +643,32 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
<div className="mx_SpotlightDialog_section mx_SpotlightDialog_results" role="group">
<div className="mx_SpotlightDialog_sectionHeader">
<h4>{ _t("Suggestions") }</h4>
<NetworkDropdown
protocols={protocols}
config={config ?? null}
setConfig={setConfig}
/>
</div>
<div>
{ results[Section.PublicRooms].slice(0, SECTION_LIMIT).map(resultMapper) }
<div className="mx_SpotlightDialog_options">
{ exploringPublicSpacesEnabled && <>
<LabelledCheckbox
label={_t("Show rooms")}
value={showRooms}
onChange={setShowRooms}
/>
<LabelledCheckbox
label={_t("Show spaces")}
value={showSpaces}
onChange={setShowSpaces}
/>
</> }
<NetworkDropdown
protocols={protocols}
config={config ?? null}
setConfig={setConfig}
/>
</div>
</div>
<div> { (showRooms || showSpaces)
? results[Section.PublicRooms].slice(0, SECTION_LIMIT).map(resultMapper)
: <div className="mx_SpotlightDialog_otherSearches_messageSearchText">
{ _t("You cannot search for rooms that are neither a room nor a space") }
</div>
} </div>
</div>
);
}

View File

@ -49,23 +49,31 @@ export class LabsSettingToggle extends React.Component<ILabsSettingToggleProps>
interface IState {
showHiddenReadReceipts: boolean;
showJumpToDate: boolean;
showExploringPublicSpaces: boolean;
}
export default class LabsUserSettingsTab extends React.Component<{}, IState> {
constructor(props: {}) {
super(props);
MatrixClientPeg.get().doesServerSupportUnstableFeature("org.matrix.msc2285").then((showHiddenReadReceipts) => {
const cli = MatrixClientPeg.get();
cli.doesServerSupportUnstableFeature("org.matrix.msc2285").then((showHiddenReadReceipts) => {
this.setState({ showHiddenReadReceipts });
});
MatrixClientPeg.get().doesServerSupportUnstableFeature("org.matrix.msc3030").then((showJumpToDate) => {
cli.doesServerSupportUnstableFeature("org.matrix.msc3030").then((showJumpToDate) => {
this.setState({ showJumpToDate });
});
cli.doesServerSupportUnstableFeature("org.matrix.msc3827").then((showExploringPublicSpaces) => {
this.setState({ showExploringPublicSpaces });
});
this.state = {
showHiddenReadReceipts: false,
showJumpToDate: false,
showExploringPublicSpaces: false,
};
}
@ -133,6 +141,16 @@ export default class LabsUserSettingsTab extends React.Component<{}, IState> {
);
}
if (this.state.showExploringPublicSpaces) {
groups.getOrCreate(LabGroup.Spaces, []).push(
<SettingsFlag
key="feature_exploring_public_spaces"
name="feature_exploring_public_spaces"
level={SettingLevel.DEVICE}
/>,
);
}
labsSections = <>
{ sortBy(Array.from(groups.entries()), "0").map(([group, flags]) => (
<div className="mx_SettingsTab_section" key={group}>

View File

@ -18,7 +18,7 @@ import React, { ComponentProps, RefObject, SyntheticEvent, KeyboardEvent, useCon
import classNames from "classnames";
import { RoomType } from "matrix-js-sdk/src/@types/event";
import { ICreateRoomOpts } from "matrix-js-sdk/src/@types/requests";
import { HistoryVisibility, Preset } from "matrix-js-sdk/src/@types/partials";
import { HistoryVisibility, Preset, Visibility } from "matrix-js-sdk/src/@types/partials";
import { logger } from "matrix-js-sdk/src/logger";
import { _t } from "../../../languageHandler";
@ -37,6 +37,7 @@ import GenericFeatureFeedbackDialog from "../dialogs/GenericFeatureFeedbackDialo
import SettingsStore from "../../../settings/SettingsStore";
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
export const createSpace = async (
name: string,
@ -51,6 +52,9 @@ export const createSpace = async (
createOpts: {
name,
preset: isPublic ? Preset.PublicChat : Preset.PrivateChat,
visibility: (isPublic && await MatrixClientPeg.get().doesServerSupportUnstableFeature("org.matrix.msc3827"))
? Visibility.Public
: Visibility.Private,
power_level_content_override: {
// Only allow Admins to write to the timeline to prevent hidden sync spam
events_default: 100,
@ -80,11 +84,6 @@ const SpaceCreateMenuType = ({ title, description, className, onClick }) => {
);
};
enum Visibility {
Public,
Private,
}
const spaceNameValidator = withValidation({
rules: [
{

View File

@ -29,6 +29,7 @@ import { useLocalEcho } from "../../../hooks/useLocalEcho";
import JoinRuleSettings from "../settings/JoinRuleSettings";
import { useRoomState } from "../../../hooks/useRoomState";
import SettingsFieldset from "../settings/SettingsFieldset";
import { useAsyncMemo } from "../../../hooks/useAsyncMemo";
interface IProps {
matrixClient: MatrixClient;
@ -38,6 +39,9 @@ interface IProps {
const SpaceSettingsVisibilityTab = ({ matrixClient: cli, space, closeSettingsFn }: IProps) => {
const [error, setError] = useState("");
const serverSupportsExploringSpaces = useAsyncMemo<boolean>(async () => {
return cli.doesServerSupportUnstableFeature("org.matrix.msc3827");
}, [cli], false);
const userId = cli.getUserId();
@ -103,7 +107,7 @@ const SpaceSettingsVisibilityTab = ({ matrixClient: cli, space, closeSettingsFn
canSetCanonicalAlias={canSetCanonical}
canSetAliases={true}
canonicalAliasEvent={canonicalAliasEv}
hidePublishSetting={true}
hidePublishSetting={!serverSupportsExploringSpaces}
/>
</>;
}

View File

@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { RoomType } from "matrix-js-sdk/src/@types/event";
import { IRoomDirectoryOptions } from "matrix-js-sdk/src/@types/requests";
import { IProtocol, IPublicRoomsChunkRoom } from "matrix-js-sdk/src/client";
import { useCallback, useEffect, useState } from "react";
@ -32,6 +33,7 @@ const LAST_INSTANCE_KEY = "mx_last_room_directory_instance";
export interface IPublicRoomsOpts {
limit: number;
query?: string;
roomTypes?: Set<RoomType | null>;
}
let thirdParty: Protocols;
@ -72,6 +74,7 @@ export const usePublicRoomDirectory = () => {
const search = useCallback(async ({
limit = 20,
query,
roomTypes,
}: IPublicRoomsOpts): Promise<boolean> => {
const opts: IRoomDirectoryOptions = { limit };
@ -85,9 +88,10 @@ export const usePublicRoomDirectory = () => {
opts.third_party_instance_id = config.instanceId;
}
if (query) {
if (query || roomTypes) {
opts.filter = {
generic_search_term: query,
"generic_search_term": query,
"org.matrix.msc3827.room_types": roomTypes ? Array.from<RoomType | null>(roomTypes) : null,
};
}

View File

@ -856,6 +856,7 @@
"Can I use text chat alongside the video call?": "Can I use text chat alongside the video call?",
"Yes, the chat timeline is displayed alongside the video.": "Yes, the chat timeline is displayed alongside the video.",
"Thank you for trying the beta, please go into as much detail as you can so we can improve it.": "Thank you for trying the beta, please go into as much detail as you can so we can improve it.",
"Explore public spaces in the new search dialog": "Explore public spaces in the new search dialog",
"Let moderators hide messages pending moderation.": "Let moderators hide messages pending moderation.",
"Report to moderators prototype. In rooms that support moderation, the `report` button will let you report abuse to room moderators": "Report to moderators prototype. In rooms that support moderation, the `report` button will let you report abuse to room moderators",
"Render LaTeX maths in messages": "Render LaTeX maths in messages",
@ -2803,6 +2804,9 @@
"Use \"%(query)s\" to search": "Use \"%(query)s\" to search",
"Search for": "Search for",
"Spaces you're in": "Spaces you're in",
"Show rooms": "Show rooms",
"Show spaces": "Show spaces",
"You cannot search for rooms that are neither a room nor a space": "You cannot search for rooms that are neither a room nor a space",
"Other rooms in %(spaceName)s": "Other rooms in %(spaceName)s",
"Join %(roomAddress)s": "Join %(roomAddress)s",
"Some results may be hidden for privacy": "Some results may be hidden for privacy",

View File

@ -220,6 +220,11 @@ export const SETTINGS: {[setting: string]: ISetting} = {
requiresRefresh: true,
},
},
"feature_exploring_public_spaces": {
displayName: _td("Explore public spaces in the new search dialog"),
supportedLevels: LEVELS_FEATURE,
default: false,
},
"feature_msc3531_hide_messages_pending_moderation": {
isFeature: true,
labsGroup: LabGroup.Moderation,