diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 81a403bacf..c07cdf332b 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -6,9 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import React, { type JSX, createRef, type ReactNode, type SyntheticEvent } from "react"; -import classNames from "classnames"; -import { RoomMember, type Room, MatrixError, EventType } from "matrix-js-sdk/src/matrix"; +import React, { createRef, type JSX, type ReactNode, type SyntheticEvent } from "react"; +import { EventType, MatrixError, type Room, RoomMember } from "matrix-js-sdk/src/matrix"; import { KnownMembership } from "matrix-js-sdk/src/types"; import { type MatrixCall } from "matrix-js-sdk/src/webrtc/call"; import { logger } from "matrix-js-sdk/src/logger"; @@ -1260,30 +1259,89 @@ export default class InviteDialog extends React.PureComponent 0 || (!!this.state.filterText && this.state.filterText.includes("@")); + } + + /** + * Render the "suggestions" section, which shows a list of people you might want to invite, together with any + * errors from the previous iteration. + */ + private renderSuggestions(): JSX.Element { + // If we're starting a DM, add a footer which showing our matrix.to link, for copying & pasting. + let footer; + if (this.props.kind === InviteKind.Dm) { + const link = makeUserPermalink(MatrixClientPeg.safeGet().getSafeUserId()); + footer = ( +
+

{_t("invite|send_link_prompt")}

+ makeUserPermalink(MatrixClientPeg.safeGet().getSafeUserId())}> + + {link} + + +
+ ); + } + + let results: React.ReactNode | null = null; + let onlyOneThreepidNote: React.ReactNode | null = null; + + if (!this.canInviteMore() || (this.hasFilterAtLeastOneEmail() && !this.canInviteThirdParty())) { + // We are in DM case here, because of the checks in canInviteMore() / canInviteThirdParty(). + // Show a note saying "Invites by email can only be sent one at a time". + onlyOneThreepidNote =
{_t("invite|email_limit_one")}
; + } else { + let extraSection; + if (this.props.kind === InviteKind.Dm) { + // Some extra words saying "Some suggestions may be hidden for privacy" + extraSection = ( +
+ {_t("invite|suggestions_disclaimer")} +

{_t("invite|suggestions_disclaimer_prompt")}

+
+ ); + } + + results = ( +
+ {this.renderSection("recents")} + {this.renderSection("suggestions")} + {extraSection} +
+ ); + } + + return ( + + {this.renderIdentityServerWarning()} +
{this.state.errorText}
+ {onlyOneThreepidNote} + {results} + {footer} +
+ ); + } + + /** + * Render content of the common "users" tab that is shown whether we have a regular invite dialog or a + * "CallTransfer" one. + */ + private renderMainTab(): JSX.Element { let spinner: JSX.Element | undefined; if (this.state.busy) { spinner = ; } - let title; let helpText; let buttonText; let goButtonFn: (() => Promise) | null = null; - let consultConnectSection; - let extraSection; - let footer; const identityServersEnabled = SettingsStore.getValue(UIFeature.IdentityServer); - const hasSelection = - this.state.targets.length > 0 || (this.state.filterText && this.state.filterText.includes("@")); - const cli = MatrixClientPeg.safeGet(); const userId = cli.getUserId()!; if (this.props.kind === InviteKind.Dm) { - title = _t("space|add_existing_room_space|dm_heading"); - if (identityServersEnabled) { helpText = _t( "invite|start_conversation_name_email_mxid_prompt", @@ -1316,34 +1374,10 @@ export default class InviteDialog extends React.PureComponent - {_t("invite|suggestions_disclaimer")} -

{_t("invite|suggestions_disclaimer_prompt")}

- - ); - const link = makeUserPermalink(MatrixClientPeg.safeGet().getSafeUserId()); - footer = ( -
-

{_t("invite|send_link_prompt")}

- makeUserPermalink(MatrixClientPeg.safeGet().getSafeUserId())}> - - {link} - - -
- ); } else if (this.props.kind === InviteKind.Invite) { const roomId = this.props.roomId; const room = MatrixClientPeg.get()?.getRoom(roomId); const isSpace = room?.isSpaceRoom(); - title = isSpace - ? _t("invite|to_space", { - spaceName: room?.name || _t("common|unnamed_space"), - }) - : _t("invite|to_room", { - roomName: room?.name || _t("common|unnamed_room"), - }); let helpTextUntranslated; if (isSpace) { @@ -1384,31 +1418,6 @@ export default class InviteDialog extends React.PureComponent - - - {_t("action|cancel")} - - - {_t("action|transfer")} - - - ); } const goButton = @@ -1417,29 +1426,13 @@ export default class InviteDialog extends React.PureComponent {buttonText} ); - let results: React.ReactNode | null = null; - let onlyOneThreepidNote: React.ReactNode | null = null; - - if (!this.canInviteMore() || (this.hasFilterAtLeastOneEmail() && !this.canInviteThirdParty())) { - // We are in DM case here, because of the checks in canInviteMore() / canInviteThirdParty(). - onlyOneThreepidNote =
{_t("invite|email_limit_one")}
; - } else { - results = ( -
- {this.renderSection("recents")} - {this.renderSection("suggestions")} - {extraSection} -
- ); - } - - const usersSection = ( + return (

{helpText}

@@ -1449,102 +1442,155 @@ export default class InviteDialog extends React.PureComponent
- {this.renderIdentityServerWarning()} -
{this.state.errorText}
- {onlyOneThreepidNote} - {results} - {footer} + {this.renderSuggestions()}
); + } - let dialogContent; - if (this.props.kind === InviteKind.CallTransfer) { - const tabs: NonEmptyArray> = [ - new Tab( - TabId.UserDirectory, - _td("invite|transfer_user_directory_tab"), - "mx_InviteDialog_userDirectoryIcon", - usersSection, - ), - ]; - - const backspaceButton = ; - - // Only show the backspace button if the field has content - let dialPadField; - if (this.state.dialPadValue.length !== 0) { - dialPadField = ( - - ); - } else { - dialPadField = ( - - ); - } - - const dialPadSection = ( -
-
{dialPadField}
- -
- ); - tabs.push( - new Tab( - TabId.DialPad, - _td("invite|transfer_dial_pad_tab"), - "mx_InviteDialog_dialPadIcon", - dialPadSection, - ), - ); - dialogContent = ( - - - tabs={tabs} - activeTabId={this.state.currentTabId} - tabLocation={TabLocation.TOP} - onChange={this.onTabChange} - /> - {consultConnectSection} - - ); - } else { - dialogContent = ( - - {usersSection} - {consultConnectSection} - - ); + /** + * Render the complete dialog, given this is not a call transfer dialog. + * + * See also: {@link renderCallTransferDialog}. + */ + private renderRegularDialog(): React.ReactNode { + let title; + if (this.props.kind === InviteKind.Dm) { + title = _t("space|add_existing_room_space|dm_heading"); + } else if (this.props.kind === InviteKind.Invite) { + const roomId = this.props.roomId; + const room = MatrixClientPeg.get()?.getRoom(roomId); + const isSpace = room?.isSpaceRoom(); + title = isSpace + ? _t("invite|to_space", { + spaceName: room?.name || _t("common|unnamed_space"), + }) + : _t("invite|to_room", { + roomName: room?.name || _t("common|unnamed_room"), + }); } return ( +
{this.renderMainTab()}
+
+ ); + } + + /** + * Render the complete call transfer dialog. + * + * See also: {@link renderRegularDialog}. + */ + private renderCallTransferDialog(): React.ReactNode { + const usersSection = this.renderMainTab(); + + const tabs: NonEmptyArray> = [ + new Tab( + TabId.UserDirectory, + _td("invite|transfer_user_directory_tab"), + "mx_InviteDialog_userDirectoryIcon", + usersSection, + ), + ]; + + const backspaceButton = ; + + // Only show the backspace button if the field has content + let dialPadField; + if (this.state.dialPadValue.length !== 0) { + dialPadField = ( + + ); + } else { + dialPadField = ( + + ); + } + + const dialPadSection = ( +
+
{dialPadField}
+ +
+ ); + tabs.push( + new Tab(TabId.DialPad, _td("invite|transfer_dial_pad_tab"), "mx_InviteDialog_dialPadIcon", dialPadSection), + ); + + const consultConnectSection = ( +
+ + + {_t("action|cancel")} + + + {_t("action|transfer")} + +
+ ); + + const dialogContent = ( + + + tabs={tabs} + activeTabId={this.state.currentTabId} + tabLocation={TabLocation.TOP} + onChange={this.onTabChange} + /> + {consultConnectSection} + + ); + + return ( +
{dialogContent}
); } + + public render(): React.ReactNode { + if (this.props.kind === InviteKind.CallTransfer) { + return this.renderCallTransferDialog(); + } else { + return this.renderRegularDialog(); + } + } }