Add a few more UIComponent flags, and ensure they are used in existing code (#7937)
* UIComponent flag: Explore rooms To disable the room directory access on the Home space. Can be controlled with the existing ComponentVisibilityCustomisation * Make "plus menu" respect component visibility * UIComponent flag: Add integrations To disable the widgets section of the room info card and addwidget slashcommand. Can be controlled with the existing ComponentVisibilityCustomisation * Make sure invite users component applies to space rooms too * Appease the linter
This commit is contained in:
parent
d304e24a45
commit
f882466329
7 changed files with 83 additions and 18 deletions
|
@ -22,14 +22,14 @@ import { User } from "matrix-js-sdk/src/models/user";
|
||||||
import { Direction } from 'matrix-js-sdk/src/models/event-timeline';
|
import { Direction } from 'matrix-js-sdk/src/models/event-timeline';
|
||||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
import * as ContentHelpers from 'matrix-js-sdk/src/content-helpers';
|
import * as ContentHelpers from 'matrix-js-sdk/src/content-helpers';
|
||||||
import { parseFragment as parseHtml, Element as ChildElement } from "parse5";
|
import { Element as ChildElement, parseFragment as parseHtml } from "parse5";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { IContent } from 'matrix-js-sdk/src/models/event';
|
import { IContent } from 'matrix-js-sdk/src/models/event';
|
||||||
import { SlashCommand as SlashCommandEvent } from "matrix-analytics-events/types/typescript/SlashCommand";
|
import { SlashCommand as SlashCommandEvent } from "matrix-analytics-events/types/typescript/SlashCommand";
|
||||||
|
|
||||||
import { MatrixClientPeg } from './MatrixClientPeg';
|
import { MatrixClientPeg } from './MatrixClientPeg';
|
||||||
import dis from './dispatcher/dispatcher';
|
import dis from './dispatcher/dispatcher';
|
||||||
import { _t, _td, newTranslatableError, ITranslatableError } from './languageHandler';
|
import { _t, _td, ITranslatableError, newTranslatableError } from './languageHandler';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import MultiInviter from './utils/MultiInviter';
|
import MultiInviter from './utils/MultiInviter';
|
||||||
import { linkifyAndSanitizeHtml } from './HtmlUtils';
|
import { linkifyAndSanitizeHtml } from './HtmlUtils';
|
||||||
|
@ -933,7 +933,7 @@ export const Commands = [
|
||||||
command: 'addwidget',
|
command: 'addwidget',
|
||||||
args: '<url | embed code | Jitsi url>',
|
args: '<url | embed code | Jitsi url>',
|
||||||
description: _td('Adds a custom widget by URL to the room'),
|
description: _td('Adds a custom widget by URL to the room'),
|
||||||
isEnabled: () => SettingsStore.getValue(UIFeature.Widgets),
|
isEnabled: () => SettingsStore.getValue(UIFeature.Widgets) && shouldShowComponent(UIComponent.AddIntegrations),
|
||||||
runFn: function(roomId, widgetUrl) {
|
runFn: function(roomId, widgetUrl) {
|
||||||
if (!widgetUrl) {
|
if (!widgetUrl) {
|
||||||
return reject(newTranslatableError("Please supply a widget URL or embed code"));
|
return reject(newTranslatableError("Please supply a widget URL or embed code"));
|
||||||
|
|
|
@ -44,6 +44,8 @@ import RoomBreadcrumbs from "../views/rooms/RoomBreadcrumbs";
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import UserMenu from "./UserMenu";
|
import UserMenu from "./UserMenu";
|
||||||
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
|
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
|
||||||
|
import { shouldShowComponent } from "../../customisations/helpers/UIComponents";
|
||||||
|
import { UIComponent } from "../../settings/UIFeature";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
isMinimized: boolean;
|
isMinimized: boolean;
|
||||||
|
@ -368,7 +370,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
let rightButton: JSX.Element;
|
let rightButton: JSX.Element;
|
||||||
if (this.state.showBreadcrumbs === BreadcrumbsMode.Labs) {
|
if (this.state.showBreadcrumbs === BreadcrumbsMode.Labs) {
|
||||||
rightButton = <RecentlyViewedButton />;
|
rightButton = <RecentlyViewedButton />;
|
||||||
} else if (this.state.activeSpace === MetaSpace.Home) {
|
} else if (this.state.activeSpace === MetaSpace.Home && shouldShowComponent(UIComponent.ExploreRooms)) {
|
||||||
rightButton = <AccessibleTooltipButton
|
rightButton = <AccessibleTooltipButton
|
||||||
className="mx_LeftPanel_exploreButton"
|
className="mx_LeftPanel_exploreButton"
|
||||||
onClick={this.onExplore}
|
onClick={this.onExplore}
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useCallback, useState, useEffect, useContext } from "react";
|
import React, { useCallback, useContext, useEffect, useState } from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
import WidgetStore, { IApp } from "../../../stores/WidgetStore";
|
import WidgetStore, { IApp } from "../../../stores/WidgetStore";
|
||||||
import { E2EStatus } from "../../../utils/ShieldUtils";
|
import { E2EStatus } from "../../../utils/ShieldUtils";
|
||||||
import RoomContext from "../../../contexts/RoomContext";
|
import RoomContext from "../../../contexts/RoomContext";
|
||||||
import { UIFeature } from "../../../settings/UIFeature";
|
import { UIComponent, UIFeature } from "../../../settings/UIFeature";
|
||||||
import { ChevronFace, ContextMenuTooltipButton, useContextMenu } from "../../structures/ContextMenu";
|
import { ChevronFace, ContextMenuTooltipButton, useContextMenu } from "../../structures/ContextMenu";
|
||||||
import WidgetContextMenu from "../context_menus/WidgetContextMenu";
|
import WidgetContextMenu from "../context_menus/WidgetContextMenu";
|
||||||
import { useRoomMemberCount } from "../../../hooks/useRoomMembers";
|
import { useRoomMemberCount } from "../../../hooks/useRoomMembers";
|
||||||
|
@ -50,6 +50,7 @@ import UIStore from "../../../stores/UIStore";
|
||||||
import ExportDialog from "../dialogs/ExportDialog";
|
import ExportDialog from "../dialogs/ExportDialog";
|
||||||
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||||
import PosthogTrackers from "../../../PosthogTrackers";
|
import PosthogTrackers from "../../../PosthogTrackers";
|
||||||
|
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -327,7 +328,11 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => {
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
{ SettingsStore.getValue(UIFeature.Widgets) && <AppsSection room={room} /> }
|
{
|
||||||
|
SettingsStore.getValue(UIFeature.Widgets)
|
||||||
|
&& shouldShowComponent(UIComponent.AddIntegrations)
|
||||||
|
&& <AppsSection room={room} />
|
||||||
|
}
|
||||||
</BaseCard>;
|
</BaseCard>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ const NewRoomIntro = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let buttons;
|
let buttons;
|
||||||
if (parentSpace) {
|
if (parentSpace && shouldShowComponent(UIComponent.InviteUsers)) {
|
||||||
buttons = <div className="mx_NewRoomIntro_buttons">
|
buttons = <div className="mx_NewRoomIntro_buttons">
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
className="mx_NewRoomIntro_inviteButton"
|
className="mx_NewRoomIntro_inviteButton"
|
||||||
|
|
|
@ -63,6 +63,8 @@ import { BetaPill } from "../beta/BetaCard";
|
||||||
import PosthogTrackers from "../../../PosthogTrackers";
|
import PosthogTrackers from "../../../PosthogTrackers";
|
||||||
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||||
import { useWebSearchMetrics } from "../dialogs/SpotlightDialog";
|
import { useWebSearchMetrics } from "../dialogs/SpotlightDialog";
|
||||||
|
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
||||||
|
import { UIComponent } from "../../../settings/UIFeature";
|
||||||
|
|
||||||
const contextMenuBelow = (elementRect: DOMRect) => {
|
const contextMenuBelow = (elementRect: DOMRect) => {
|
||||||
// align the context menu's icons with the icon which opened the context menu
|
// align the context menu's icons with the icon which opened the context menu
|
||||||
|
@ -210,6 +212,14 @@ const RoomListHeader = ({ spacePanelDisabled, onVisibilityChange }: IProps) => {
|
||||||
const communityId = CommunityPrototypeStore.instance.getSelectedCommunityId();
|
const communityId = CommunityPrototypeStore.instance.getSelectedCommunityId();
|
||||||
const canAddRooms = activeSpace?.currentState?.maySendStateEvent(EventType.SpaceChild, cli.getUserId());
|
const canAddRooms = activeSpace?.currentState?.maySendStateEvent(EventType.SpaceChild, cli.getUserId());
|
||||||
|
|
||||||
|
const canCreateRooms = shouldShowComponent(UIComponent.CreateRooms);
|
||||||
|
const canExploreRooms = shouldShowComponent(UIComponent.ExploreRooms);
|
||||||
|
|
||||||
|
// If the user can't do anything on the plus menu, don't show it. This aims to target the
|
||||||
|
// plus menu shown on the Home tab primarily: the user has options to use the menu for
|
||||||
|
// communities and spaces, but is at risk of no options on the Home tab.
|
||||||
|
const canShowPlusMenu = canCreateRooms || canExploreRooms || activeSpace || communityId;
|
||||||
|
|
||||||
let contextMenu: JSX.Element;
|
let contextMenu: JSX.Element;
|
||||||
if (mainMenuDisplayed) {
|
if (mainMenuDisplayed) {
|
||||||
let ContextMenuComponent;
|
let ContextMenuComponent;
|
||||||
|
@ -320,12 +330,12 @@ const RoomListHeader = ({ spacePanelDisabled, onVisibilityChange }: IProps) => {
|
||||||
</IconizedContextMenuOptionList>
|
</IconizedContextMenuOptionList>
|
||||||
</IconizedContextMenu>;
|
</IconizedContextMenu>;
|
||||||
} else if (plusMenuDisplayed) {
|
} else if (plusMenuDisplayed) {
|
||||||
contextMenu = <IconizedContextMenu
|
let startChatOpt: JSX.Element;
|
||||||
{...contextMenuBelow(plusMenuHandle.current.getBoundingClientRect())}
|
let createRoomOpt: JSX.Element;
|
||||||
onFinished={closePlusMenu}
|
let joinRoomOpt: JSX.Element;
|
||||||
compact
|
|
||||||
>
|
if (canCreateRooms) {
|
||||||
<IconizedContextMenuOptionList first>
|
startChatOpt = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
label={_t("Start new chat")}
|
label={_t("Start new chat")}
|
||||||
iconClassName="mx_RoomListHeader_iconStartChat"
|
iconClassName="mx_RoomListHeader_iconStartChat"
|
||||||
|
@ -336,6 +346,8 @@ const RoomListHeader = ({ spacePanelDisabled, onVisibilityChange }: IProps) => {
|
||||||
closePlusMenu();
|
closePlusMenu();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
);
|
||||||
|
createRoomOpt = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
label={_t("Create new room")}
|
label={_t("Create new room")}
|
||||||
iconClassName="mx_RoomListHeader_iconCreateRoom"
|
iconClassName="mx_RoomListHeader_iconCreateRoom"
|
||||||
|
@ -347,6 +359,10 @@ const RoomListHeader = ({ spacePanelDisabled, onVisibilityChange }: IProps) => {
|
||||||
closePlusMenu();
|
closePlusMenu();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (canExploreRooms) {
|
||||||
|
joinRoomOpt = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
label={_t("Join public room")}
|
label={_t("Join public room")}
|
||||||
iconClassName="mx_RoomListHeader_iconExplore"
|
iconClassName="mx_RoomListHeader_iconExplore"
|
||||||
|
@ -357,6 +373,18 @@ const RoomListHeader = ({ spacePanelDisabled, onVisibilityChange }: IProps) => {
|
||||||
closePlusMenu();
|
closePlusMenu();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
contextMenu = <IconizedContextMenu
|
||||||
|
{...contextMenuBelow(plusMenuHandle.current.getBoundingClientRect())}
|
||||||
|
onFinished={closePlusMenu}
|
||||||
|
compact
|
||||||
|
>
|
||||||
|
<IconizedContextMenuOptionList first>
|
||||||
|
{ startChatOpt }
|
||||||
|
{ createRoomOpt }
|
||||||
|
{ joinRoomOpt }
|
||||||
</IconizedContextMenuOptionList>
|
</IconizedContextMenuOptionList>
|
||||||
</IconizedContextMenu>;
|
</IconizedContextMenu>;
|
||||||
}
|
}
|
||||||
|
@ -397,13 +425,13 @@ const RoomListHeader = ({ spacePanelDisabled, onVisibilityChange }: IProps) => {
|
||||||
return <div className="mx_RoomListHeader">
|
return <div className="mx_RoomListHeader">
|
||||||
{ contextMenuButton }
|
{ contextMenuButton }
|
||||||
{ pendingRoomJoinSpinner }
|
{ pendingRoomJoinSpinner }
|
||||||
<ContextMenuTooltipButton
|
{ canShowPlusMenu && <ContextMenuTooltipButton
|
||||||
inputRef={plusMenuHandle}
|
inputRef={plusMenuHandle}
|
||||||
onClick={openPlusMenu}
|
onClick={openPlusMenu}
|
||||||
isExpanded={plusMenuDisplayed}
|
isExpanded={plusMenuDisplayed}
|
||||||
className="mx_RoomListHeader_plusButton"
|
className="mx_RoomListHeader_plusButton"
|
||||||
title={_t("Add")}
|
title={_t("Add")}
|
||||||
/>
|
/> }
|
||||||
|
|
||||||
{ contextMenu }
|
{ contextMenu }
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
@ -36,7 +36,33 @@ export enum UIFeature {
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum UIComponent {
|
export enum UIComponent {
|
||||||
|
/**
|
||||||
|
* Components that lead to a user being invited.
|
||||||
|
*/
|
||||||
InviteUsers = "UIComponent.sendInvites",
|
InviteUsers = "UIComponent.sendInvites",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Components that lead to a room being created that aren't already
|
||||||
|
* guarded by some other condition (ie: "only if you can edit this
|
||||||
|
* space" is *not* guarded by this component, but "start DM" is).
|
||||||
|
*/
|
||||||
CreateRooms = "UIComponent.roomCreation",
|
CreateRooms = "UIComponent.roomCreation",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Components that lead to a Space being created that aren't already
|
||||||
|
* guarded by some other condition (ie: "only if you can add subspaces"
|
||||||
|
* is *not* guarded by this component, but "create new space" is).
|
||||||
|
*/
|
||||||
CreateSpaces = "UIComponent.spaceCreation",
|
CreateSpaces = "UIComponent.spaceCreation",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Components that lead to the public room directory.
|
||||||
|
*/
|
||||||
|
ExploreRooms = "UIComponent.exploreRooms",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Components that lead to the user being able to easily add widgets
|
||||||
|
* and integrations to the room, such as from the room information card.
|
||||||
|
*/
|
||||||
|
AddIntegrations = "UIComponent.addIntegrations",
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,8 @@ import SpacePreferencesDialog, { SpacePreferenceTab } from "../components/views/
|
||||||
import PosthogTrackers from "../PosthogTrackers";
|
import PosthogTrackers from "../PosthogTrackers";
|
||||||
import { ButtonEvent } from "../components/views/elements/AccessibleButton";
|
import { ButtonEvent } from "../components/views/elements/AccessibleButton";
|
||||||
import { AfterLeaveRoomPayload } from "../dispatcher/payloads/AfterLeaveRoomPayload";
|
import { AfterLeaveRoomPayload } from "../dispatcher/payloads/AfterLeaveRoomPayload";
|
||||||
|
import { shouldShowComponent } from "../customisations/helpers/UIComponents";
|
||||||
|
import { UIComponent } from "../settings/UIFeature";
|
||||||
|
|
||||||
export const shouldShowSpaceSettings = (space: Room) => {
|
export const shouldShowSpaceSettings = (space: Room) => {
|
||||||
const userId = space.client.getUserId();
|
const userId = space.client.getUserId();
|
||||||
|
@ -110,8 +112,10 @@ export const showCreateNewRoom = async (space: Room): Promise<boolean> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const shouldShowSpaceInvite = (space: Room) =>
|
export const shouldShowSpaceInvite = (space: Room) =>
|
||||||
(space?.getMyMembership() === "join" && space.canInvite(space.client.getUserId())) ||
|
(
|
||||||
space.getJoinRule() === JoinRule.Public;
|
(space?.getMyMembership() === "join" && space.canInvite(space.client.getUserId())) ||
|
||||||
|
space.getJoinRule() === JoinRule.Public
|
||||||
|
) && shouldShowComponent(UIComponent.InviteUsers);
|
||||||
|
|
||||||
export const showSpaceInvite = (space: Room, initialText = ""): void => {
|
export const showSpaceInvite = (space: Room, initialText = ""): void => {
|
||||||
if (space.getJoinRule() === "public") {
|
if (space.getJoinRule() === "public") {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue