Support using the InviteDialog for both DMs and invites
For https://github.com/vector-im/riot-web/issues/11201
This commit is contained in:
parent
73fc91aa20
commit
f350167408
3 changed files with 129 additions and 38 deletions
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017, 2018 New Vector Ltd
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -26,6 +27,7 @@ import dis from './dispatcher';
|
|||
import DMRoomMap from './utils/DMRoomMap';
|
||||
import { _t } from './languageHandler';
|
||||
import SettingsStore from "./settings/SettingsStore";
|
||||
import {KIND_DM, KIND_INVITE} from "./components/views/dialogs/InviteDialog";
|
||||
|
||||
/**
|
||||
* Invites multiple addresses to a room
|
||||
|
@ -46,7 +48,7 @@ export function showStartChatInviteDialog() {
|
|||
// This new dialog handles the room creation internally - we don't need to worry about it.
|
||||
const InviteDialog = sdk.getComponent("dialogs.InviteDialog");
|
||||
Modal.createTrackedDialog(
|
||||
'Start DM', '', InviteDialog, {},
|
||||
'Start DM', '', InviteDialog, {kind: KIND_DM},
|
||||
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
|
||||
);
|
||||
return;
|
||||
|
@ -72,6 +74,16 @@ export function showStartChatInviteDialog() {
|
|||
}
|
||||
|
||||
export function showRoomInviteDialog(roomId) {
|
||||
if (SettingsStore.isFeatureEnabled("feature_ftue_dms")) {
|
||||
// This new dialog handles the room creation internally - we don't need to worry about it.
|
||||
const InviteDialog = sdk.getComponent("dialogs.InviteDialog");
|
||||
Modal.createTrackedDialog(
|
||||
'Invite Users', '', InviteDialog, {kind: KIND_INVITE, roomId},
|
||||
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
||||
|
||||
Modal.createTrackedDialog('Chat Invite', '', AddressPickerDialog, {
|
||||
|
|
|
@ -19,7 +19,7 @@ import PropTypes from 'prop-types';
|
|||
import {_t} from "../../../languageHandler";
|
||||
import * as sdk from "../../../index";
|
||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||
import {makeUserPermalink} from "../../../utils/permalinks/Permalinks";
|
||||
import {makeRoomPermalink, makeUserPermalink} from "../../../utils/permalinks/Permalinks";
|
||||
import DMRoomMap from "../../../utils/DMRoomMap";
|
||||
import {RoomMember} from "matrix-js-sdk/src/matrix";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
|
@ -34,7 +34,8 @@ import {humanizeTime} from "../../../utils/humanize";
|
|||
import createRoom from "../../../createRoom";
|
||||
import {inviteMultipleToRoom} from "../../../RoomInvite";
|
||||
|
||||
// TODO: [TravisR] Make this generic for all kinds of invites
|
||||
export const KIND_DM = "dm";
|
||||
export const KIND_INVITE = "invite";
|
||||
|
||||
const INITIAL_ROOMS_SHOWN = 3; // Number of rooms to show at first
|
||||
const INCREMENT_ROOMS_SHOWN = 5; // Number of rooms to add when 'show more' is clicked
|
||||
|
@ -276,13 +277,28 @@ export default class InviteDialog extends React.PureComponent {
|
|||
static propTypes = {
|
||||
// Takes an array of user IDs/emails to invite.
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
|
||||
// The kind of invite being performed. Assumed to be KIND_DM if
|
||||
// not provided.
|
||||
kind: PropTypes.string,
|
||||
|
||||
// The room ID this dialog is for. Only required for KIND_INVITE.
|
||||
roomId: PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
kind: KIND_DM,
|
||||
};
|
||||
|
||||
_debounceTimer: number = null;
|
||||
_editorRef: any = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
if (props.kind === KIND_INVITE && !props.roomId) {
|
||||
throw new Error("When using KIND_INVITE a roomId is required for an InviteDialog");
|
||||
}
|
||||
|
||||
this.state = {
|
||||
targets: [], // array of Member objects (see interface above)
|
||||
|
@ -390,6 +406,21 @@ export default class InviteDialog extends React.PureComponent {
|
|||
return members.map(m => ({userId: m.member.userId, user: m.member}));
|
||||
}
|
||||
|
||||
_shouldAbortAfterInviteError(result): boolean {
|
||||
const failedUsers = Object.keys(result.states).filter(a => result.states[a] === 'error');
|
||||
if (failedUsers.length > 0) {
|
||||
console.log("Failed to invite users: ", result);
|
||||
this.setState({
|
||||
busy: false,
|
||||
errorText: _t("Failed to invite the following users to chat: %(csvUsers)s", {
|
||||
csvUsers: failedUsers.join(", "),
|
||||
}),
|
||||
});
|
||||
return true; // abort
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_startDm = () => {
|
||||
this.setState({busy: true});
|
||||
const targetIds = this.state.targets.map(t => t.userId);
|
||||
|
@ -417,15 +448,7 @@ export default class InviteDialog extends React.PureComponent {
|
|||
createRoomPromise = createRoom().then(roomId => {
|
||||
return inviteMultipleToRoom(roomId, targetIds);
|
||||
}).then(result => {
|
||||
const failedUsers = Object.keys(result.states).filter(a => result.states[a] === 'error');
|
||||
if (failedUsers.length > 0) {
|
||||
console.log("Failed to invite users: ", result);
|
||||
this.setState({
|
||||
busy: false,
|
||||
errorText: _t("Failed to invite the following users to chat: %(csvUsers)s", {
|
||||
csvUsers: failedUsers.join(", "),
|
||||
}),
|
||||
});
|
||||
if (this._shouldAbortAfterInviteError(result)) {
|
||||
return true; // abort
|
||||
}
|
||||
});
|
||||
|
@ -444,6 +467,33 @@ export default class InviteDialog extends React.PureComponent {
|
|||
});
|
||||
};
|
||||
|
||||
_inviteUsers = () => {
|
||||
this.setState({busy: true});
|
||||
const targetIds = this.state.targets.map(t => t.userId);
|
||||
|
||||
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
|
||||
if (!room) {
|
||||
console.error("Failed to find the room to invite users to");
|
||||
this.setState({
|
||||
busy: false,
|
||||
errorText: _t("Something went wrong trying to invite the users."),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
inviteMultipleToRoom(this.props.roomId, targetIds).then(result => {
|
||||
if (!this._shouldAbortAfterInviteError(result)) { // handles setting error message too
|
||||
this.props.onFinished();
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
this.setState({
|
||||
busy: false,
|
||||
errorText: _t("We couldn't invite those users. Please check the users you want to invite and try again."),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
_cancel = () => {
|
||||
// We do not want the user to close the dialog while an action is in progress
|
||||
if (this.state.busy) return;
|
||||
|
@ -658,7 +708,11 @@ export default class InviteDialog extends React.PureComponent {
|
|||
let showNum = kind === 'recents' ? this.state.numRecentsShown : this.state.numSuggestionsShown;
|
||||
const showMoreFn = kind === 'recents' ? this._showMoreRecents.bind(this) : this._showMoreSuggestions.bind(this);
|
||||
const lastActive = (m) => kind === 'recents' ? m.lastActive : null;
|
||||
const sectionName = kind === 'recents' ? _t("Recent Conversations") : _t("Suggestions");
|
||||
let sectionName = kind === 'recents' ? _t("Recent Conversations") : _t("Suggestions");
|
||||
|
||||
if (this.props.kind === KIND_INVITE) {
|
||||
sectionName = kind === 'recents' ? _t("Recently Direct Messaged") : _t("Suggestions");
|
||||
}
|
||||
|
||||
// Mix in the server results if we have any, but only if we're searching. We track the additional
|
||||
// members separately because we want to filter sourceMembers but trust the mixin arrays to have
|
||||
|
@ -805,33 +859,54 @@ export default class InviteDialog extends React.PureComponent {
|
|||
spinner = <Spinner w={20} h={20} />;
|
||||
}
|
||||
|
||||
const userId = MatrixClientPeg.get().getUserId();
|
||||
|
||||
let title;
|
||||
let helpText;
|
||||
let buttonText;
|
||||
let goButtonFn;
|
||||
|
||||
if (this.props.kind === KIND_DM) {
|
||||
const userId = MatrixClientPeg.get().getUserId();
|
||||
|
||||
title = _t("Direct Messages");
|
||||
helpText = _t(
|
||||
"If you can't find someone, ask them for their username, or share your " +
|
||||
"username (%(userId)s) or <a>profile link</a>.",
|
||||
{userId},
|
||||
{a: (sub) => <a href={makeUserPermalink(userId)} rel="noopener" target="_blank">{sub}</a>},
|
||||
);
|
||||
buttonText = _t("Go");
|
||||
goButtonFn = this._startDm;
|
||||
} else { // KIND_INVITE
|
||||
title = _t("Invite to this room");
|
||||
helpText = _t(
|
||||
"If you can't find someone, ask them for their username (e.g. @user:server.com) or " +
|
||||
"<a>share this room</a>.", {},
|
||||
{a: (sub) => <a href={makeRoomPermalink(this.props.roomId)} rel="noopener" target="_blank">{sub}</a>},
|
||||
);
|
||||
buttonText = _t("Invite");
|
||||
goButtonFn = this._inviteUsers;
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
className='mx_InviteDialog'
|
||||
hasCancel={true}
|
||||
onFinished={this._cancel}
|
||||
title={_t("Direct Messages")}
|
||||
title={title}
|
||||
>
|
||||
<div className='mx_InviteDialog_content'>
|
||||
<p>
|
||||
{_t(
|
||||
"If you can't find someone, ask them for their username, or share your " +
|
||||
"username (%(userId)s) or <a>profile link</a>.",
|
||||
{userId},
|
||||
{a: (sub) => <a href={makeUserPermalink(userId)} rel="noopener" target="_blank">{sub}</a>},
|
||||
)}
|
||||
</p>
|
||||
<p>{helpText}</p>
|
||||
<div className='mx_InviteDialog_addressBar'>
|
||||
{this._renderEditor()}
|
||||
<div className='mx_InviteDialog_buttonAndSpinner'>
|
||||
<AccessibleButton
|
||||
kind="primary"
|
||||
onClick={this._startDm}
|
||||
onClick={goButtonFn}
|
||||
className='mx_InviteDialog_goButton'
|
||||
disabled={this.state.busy}
|
||||
>
|
||||
{_t("Go")}
|
||||
{buttonText}
|
||||
</AccessibleButton>
|
||||
{spinner}
|
||||
</div>
|
||||
|
|
|
@ -372,7 +372,7 @@
|
|||
"Render simple counters in room header": "Render simple counters in room header",
|
||||
"Multiple integration managers": "Multiple integration managers",
|
||||
"Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)",
|
||||
"New DM invite dialog (under development)": "New DM invite dialog (under development)",
|
||||
"New invite dialog": "New invite dialog",
|
||||
"Show a presence dot next to DMs in the room list": "Show a presence dot next to DMs in the room list",
|
||||
"Enable cross-signing to verify per-user instead of per-device (in development)": "Enable cross-signing to verify per-user instead of per-device (in development)",
|
||||
"Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)",
|
||||
|
@ -1438,16 +1438,6 @@
|
|||
"View Servers in Room": "View Servers in Room",
|
||||
"Toolbox": "Toolbox",
|
||||
"Developer Tools": "Developer Tools",
|
||||
"Failed to invite the following users to chat: %(csvUsers)s": "Failed to invite the following users to chat: %(csvUsers)s",
|
||||
"We couldn't create your DM. Please check the users you want to invite and try again.": "We couldn't create your DM. Please check the users you want to invite and try again.",
|
||||
"Failed to find the following users": "Failed to find the following users",
|
||||
"The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "The following users might not exist or are invalid, and cannot be invited: %(csvNames)s",
|
||||
"Recent Conversations": "Recent Conversations",
|
||||
"Suggestions": "Suggestions",
|
||||
"Show more": "Show more",
|
||||
"Direct Messages": "Direct Messages",
|
||||
"If you can't find someone, ask them for their username, or share your username (%(userId)s) or <a>profile link</a>.": "If you can't find someone, ask them for their username, or share your username (%(userId)s) or <a>profile link</a>.",
|
||||
"Go": "Go",
|
||||
"An error has occurred.": "An error has occurred.",
|
||||
"Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.",
|
||||
"Verifying this user will mark their device as trusted, and also mark your device as trusted to them.": "Verifying this user will mark their device as trusted, and also mark your device as trusted to them.",
|
||||
|
@ -1457,6 +1447,20 @@
|
|||
"Enable 'Manage Integrations' in Settings to do this.": "Enable 'Manage Integrations' in Settings to do this.",
|
||||
"Integrations not allowed": "Integrations not allowed",
|
||||
"Your Riot doesn't allow you to use an Integration Manager to do this. Please contact an admin.": "Your Riot doesn't allow you to use an Integration Manager to do this. Please contact an admin.",
|
||||
"Failed to invite the following users to chat: %(csvUsers)s": "Failed to invite the following users to chat: %(csvUsers)s",
|
||||
"We couldn't create your DM. Please check the users you want to invite and try again.": "We couldn't create your DM. Please check the users you want to invite and try again.",
|
||||
"Something went wrong trying to invite the users.": "Something went wrong trying to invite the users.",
|
||||
"We couldn't invite those users. Please check the users you want to invite and try again.": "We couldn't invite those users. Please check the users you want to invite and try again.",
|
||||
"Failed to find the following users": "Failed to find the following users",
|
||||
"The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "The following users might not exist or are invalid, and cannot be invited: %(csvNames)s",
|
||||
"Recent Conversations": "Recent Conversations",
|
||||
"Suggestions": "Suggestions",
|
||||
"Recently Direct Messaged": "Recently Direct Messaged",
|
||||
"Show more": "Show more",
|
||||
"Direct Messages": "Direct Messages",
|
||||
"If you can't find someone, ask them for their username, or share your username (%(userId)s) or <a>profile link</a>.": "If you can't find someone, ask them for their username, or share your username (%(userId)s) or <a>profile link</a>.",
|
||||
"Go": "Go",
|
||||
"If you can't find someone, ask them for their username (e.g. @user:server.com) or <a>share this room</a>.": "If you can't find someone, ask them for their username (e.g. @user:server.com) or <a>share this room</a>.",
|
||||
"You added a new device '%(displayName)s', which is requesting encryption keys.": "You added a new device '%(displayName)s', which is requesting encryption keys.",
|
||||
"Your unverified device '%(displayName)s' is requesting encryption keys.": "Your unverified device '%(displayName)s' is requesting encryption keys.",
|
||||
"Start verification": "Start verification",
|
||||
|
|
Loading…
Reference in a new issue