Merge pull request #5201 from matrix-org/travis/3pid-invites

Tactical improvements to 3PID invites
This commit is contained in:
Travis Ralston 2020-09-15 10:42:55 -06:00 committed by GitHub
commit 3d9c520af8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 192 additions and 48 deletions

View file

@ -56,6 +56,7 @@ import { ViewRoomDeltaPayload } from "../../dispatcher/payloads/ViewRoomDeltaPay
import RoomListStore from "../../stores/room-list/RoomListStore";
import NonUrgentToastContainer from "./NonUrgentToastContainer";
import { ToggleRightPanelPayload } from "../../dispatcher/payloads/ToggleRightPanelPayload";
import { IThreepidInvite } from "../../stores/ThreepidInviteStore";
// We need to fetch each pinned message individually (if we don't already have it)
// so each pinned message may trigger a request. Limit the number per room for sanity.
@ -81,7 +82,7 @@ interface IProps {
// eslint-disable-next-line camelcase
page_type: string;
autoJoin: boolean;
thirdPartyInvite?: object;
threepidInvite?: IThreepidInvite;
roomOobData?: object;
currentRoomId: string;
ConferenceHandler?: object;
@ -631,7 +632,7 @@ class LoggedInView extends React.Component<IProps, IState> {
ref={this._roomView}
autoJoin={this.props.autoJoin}
onRegistered={this.props.onRegistered}
thirdPartyInvite={this.props.thirdPartyInvite}
threepidInvite={this.props.threepidInvite}
oobData={this.props.roomOobData}
viaServers={this.props.viaServers}
key={this.props.currentRoomId || 'roomview'}

View file

@ -78,6 +78,7 @@ import { RoomNotificationStateStore } from "../../stores/notifications/RoomNotif
import { SettingLevel } from "../../settings/SettingLevel";
import { leaveRoomBehaviour } from "../../utils/membership";
import CreateCommunityPrototypeDialog from "../views/dialogs/CreateCommunityPrototypeDialog";
import ThreepidInviteStore, { IThreepidInvite, IThreepidInviteWireFormat } from "../../stores/ThreepidInviteStore";
/** constants for MatrixChat.state.view */
export enum Views {
@ -137,9 +138,9 @@ interface IRoomInfo {
auto_join?: boolean;
highlighted?: boolean;
third_party_invite?: object;
oob_data?: object;
via_servers?: string[];
threepid_invite?: IThreepidInvite;
}
/* eslint-enable camelcase */
@ -196,7 +197,7 @@ interface IState {
resizeNotifier: ResizeNotifier;
serverConfig?: ValidatedServerConfig;
ready: boolean;
thirdPartyInvite?: object;
threepidInvite?: IThreepidInvite,
roomOobData?: object;
viaServers?: string[];
pendingInitialSync?: boolean;
@ -260,6 +261,14 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// outside this.state because updating it should never trigger a
// rerender.
this.screenAfterLogin = this.props.initialScreenAfterLogin;
if (this.screenAfterLogin) {
const params = this.screenAfterLogin.params || {};
if (this.screenAfterLogin.screen.startsWith("room/") && params['signurl'] && params['email']) {
// probably a threepid invite - try to store it
const roomId = this.screenAfterLogin.screen.substring("room/".length);
ThreepidInviteStore.instance.storeInvite(roomId, params as IThreepidInviteWireFormat);
}
}
this.windowWidth = 10000;
this.handleResize();
@ -404,8 +413,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
});
}).then((loadedSession) => {
if (!loadedSession) {
// fall back to showing the welcome screen
dis.dispatch({action: "view_welcome_page"});
// fall back to showing the welcome screen... unless we have a 3pid invite pending
if (ThreepidInviteStore.instance.pickBestInvite()) {
dis.dispatch({action: 'start_registration'});
} else {
dis.dispatch({action: "view_welcome_page"});
}
}
});
// Note we don't catch errors from this: we catch everything within
@ -835,10 +848,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// context of that particular event.
// @param {boolean=} roomInfo.highlighted If true, add event_id to the hash of the URL
// and alter the EventTile to appear highlighted.
// @param {Object=} roomInfo.third_party_invite Object containing data about the third party
// we received to join the room, if any.
// @param {string=} roomInfo.third_party_invite.inviteSignUrl 3pid invite sign URL
// @param {string=} roomInfo.third_party_invite.invitedEmail The email address the invite was sent to
// @param {Object=} roomInfo.threepid_invite Object containing data about the third party
// we received to join the room, if any.
// @param {Object=} roomInfo.oob_data Object of additional data about the room
// that has been passed out-of-band (eg.
// room name and avatar from an invite email)
@ -896,7 +907,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
view: Views.LOGGED_IN,
currentRoomId: roomInfo.room_id || null,
page_type: PageTypes.RoomView,
thirdPartyInvite: roomInfo.third_party_invite,
threepidInvite: roomInfo.threepid_invite,
roomOobData: roomInfo.oob_data,
viaServers: roomInfo.via_servers,
ready: true,
@ -1203,6 +1214,14 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// the homepage.
dis.dispatch({action: 'view_home_page'});
}
} else if (ThreepidInviteStore.instance.pickBestInvite()) {
// The user has a 3pid invite pending - show them that
const threepidInvite = ThreepidInviteStore.instance.pickBestInvite();
// HACK: This is a pretty brutal way of threading the invite back through
// our systems, but it's the safest we have for now.
const params = ThreepidInviteStore.instance.translateToWireFormat(threepidInvite);
this.showScreen(`room/${threepidInvite.roomId}`, params)
} else {
// The user has just logged in after registering,
// so show the homepage.
@ -1639,16 +1658,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// TODO: Handle encoded room/event IDs: https://github.com/vector-im/element-web/issues/9149
// FIXME: sort_out caseConsistency
const thirdPartyInvite = {
inviteSignUrl: params.signurl,
invitedEmail: params.email,
};
const oobData = {
name: params.room_name,
avatarUrl: params.room_avatar_url,
inviterName: params.inviter_name,
};
let threepidInvite: IThreepidInvite;
if (params.signurl && params.email) {
threepidInvite = ThreepidInviteStore.instance
.storeInvite(roomString, params as IThreepidInviteWireFormat);
}
// on our URLs there might be a ?via=matrix.org or similar to help
// joins to the room succeed. We'll pass these through as an array
@ -1669,8 +1683,15 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// it as highlighted, which will propagate to RoomView and highlight the
// associated EventTile.
highlighted: Boolean(eventId),
third_party_invite: thirdPartyInvite,
oob_data: oobData,
threepid_invite: threepidInvite,
// TODO: Replace oob_data with the threepidInvite (which has the same info).
// This isn't done yet because it's threaded through so many more places.
// See https://github.com/vector-im/element-web/issues/15157
oob_data: {
name: threepidInvite?.roomName,
avatarUrl: threepidInvite?.roomAvatarUrl,
inviterName: threepidInvite?.inviterName,
},
room_alias: undefined,
room_id: undefined,
};
@ -2002,12 +2023,13 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
view = <Welcome />;
} else if (this.state.view === Views.REGISTER) {
const Registration = sdk.getComponent('structures.auth.Registration');
const email = ThreepidInviteStore.instance.pickBestInvite()?.toEmail;
view = (
<Registration
clientSecret={this.state.register_client_secret}
sessionId={this.state.register_session_id}
idSid={this.state.register_id_sid}
email={this.props.startingFragmentQueryParams.email}
email={email}
brand={this.props.config.brand}
makeRegistrationUrl={this.makeRegistrationUrl}
onLoggedIn={this.onRegisterFlowComplete}

View file

@ -72,6 +72,7 @@ import RoomHeader from "../views/rooms/RoomHeader";
import TintableSvg from "../views/elements/TintableSvg";
import type * as ConferenceHandler from '../../VectorConferenceHandler';
import {XOR} from "../../@types/common";
import { IThreepidInvite } from "../../stores/ThreepidInviteStore";
const DEBUG = false;
let debuglog = function(msg: string) {};
@ -86,15 +87,7 @@ if (DEBUG) {
interface IProps {
ConferenceHandler?: ConferenceHandler;
// An object representing a third party invite to join this room
// Fields:
// * inviteSignUrl (string) The URL used to join this room from an email invite
// (given as part of the link in the invite email)
// * invitedEmail (string) The email address that was invited to this room
thirdPartyInvite?: {
inviteSignUrl: string;
invitedEmail: string;
};
threepidInvite: IThreepidInvite,
// Any data about the room that would normally come from the homeserver
// but has been passed out-of-band, eg. the room name and avatar URL
@ -1178,8 +1171,7 @@ export default class RoomView extends React.Component<IProps, IState> {
// return;
} else {
Promise.resolve().then(() => {
const signUrl = this.props.thirdPartyInvite ?
this.props.thirdPartyInvite.inviteSignUrl : undefined;
const signUrl = this.props.threepidInvite?.signUrl;
dis.dispatch({
action: 'join_room',
opts: { inviteSignUrl: signUrl, viaServers: this.props.viaServers },
@ -1752,10 +1744,7 @@ export default class RoomView extends React.Component<IProps, IState> {
if (this.props.oobData) {
inviterName = this.props.oobData.inviterName;
}
let invitedEmail = undefined;
if (this.props.thirdPartyInvite) {
invitedEmail = this.props.thirdPartyInvite.invitedEmail;
}
const invitedEmail = this.props.threepidInvite?.toEmail;
// We have no room object for this room, only the ID.
// We've got to this room by following a link, possibly a third party invite.
@ -1773,7 +1762,7 @@ export default class RoomView extends React.Component<IProps, IState> {
inviterName={inviterName}
invitedEmail={invitedEmail}
oobData={this.props.oobData}
signUrl={this.props.thirdPartyInvite ? this.props.thirdPartyInvite.inviteSignUrl : null}
signUrl={this.props.threepidInvite?.signUrl}
room={this.state.room}
/>
</ErrorBoundary>
@ -1907,10 +1896,7 @@ export default class RoomView extends React.Component<IProps, IState> {
if (this.props.oobData) {
inviterName = this.props.oobData.inviterName;
}
let invitedEmail = undefined;
if (this.props.thirdPartyInvite) {
invitedEmail = this.props.thirdPartyInvite.invitedEmail;
}
const invitedEmail = this.props.threepidInvite?.toEmail;
hideCancel = true;
previewBar = (
<RoomPreviewBar

View file

@ -25,6 +25,7 @@ import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
import AccessibleButton from "../elements/AccessibleButton";
import Spinner from "../elements/Spinner";
/* This file contains a collection of components which are used by the
* InteractiveAuth to prompt the user to enter the information needed
@ -404,8 +405,12 @@ export class EmailIdentityAuthEntry extends React.Component {
// the validation link, we won't know the email address, so if we don't have it,
// assume that the link has been clicked and the server will realise when we poll.
if (this.props.inputs.emailAddress === undefined) {
const Loader = sdk.getComponent("elements.Spinner");
return <Loader />;
return <Spinner />;
} else if (this.props.stageState?.emailSid) {
// we only have a session ID if the user has clicked the link in their email,
// so show a loading state instead of "an email has been sent to..." because
// that's confusing when you've already read that email.
return <Spinner />;
} else {
return (
<div>