mirror of
https://github.com/element-hq/element-web
synced 2024-11-25 02:35:48 +03:00
Store and thread 3pid invite through the app
This doesn't do anything with the stored value (yet), but enables us to do something with it in a future commit.
This commit is contained in:
parent
77f8c48dc4
commit
dc44b9ef59
4 changed files with 141 additions and 41 deletions
|
@ -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'}
|
||||
|
|
|
@ -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();
|
||||
|
@ -835,10 +844,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 +903,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,
|
||||
|
@ -1639,16 +1646,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 +1671,12 @@ 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,
|
||||
oob_data: {
|
||||
name: threepidInvite?.roomName,
|
||||
avatarUrl: threepidInvite?.roomAvatarUrl,
|
||||
inviterName: threepidInvite?.inviterName,
|
||||
},
|
||||
room_alias: undefined,
|
||||
room_id: undefined,
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
107
src/stores/ThreepidInviteStore.ts
Normal file
107
src/stores/ThreepidInviteStore.ts
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
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.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import EventEmitter from "events";
|
||||
import { ComponentClass } from "../@types/common";
|
||||
import { UPDATE_EVENT } from "./AsyncStore";
|
||||
import { base32, base64 } from "rfc4648";
|
||||
|
||||
// Dev note: the interface is split in two so we don't have to disable the
|
||||
// linter across the whole project.
|
||||
export interface IThreepidInviteWireFormat {
|
||||
email: string;
|
||||
signurl: string;
|
||||
room_name: string;
|
||||
room_avatar_url: string;
|
||||
inviter_name: string;
|
||||
|
||||
// TODO: Figure out if these are ever populated
|
||||
guest_access_token?: string;
|
||||
guest_user_id?: string;
|
||||
}
|
||||
|
||||
interface IPersistedThreepidInvite extends IThreepidInviteWireFormat {
|
||||
roomId: string;
|
||||
}
|
||||
|
||||
export interface IThreepidInvite {
|
||||
id: string; // generated by us
|
||||
roomId: string;
|
||||
toEmail: string;
|
||||
signUrl: string;
|
||||
roomName: string;
|
||||
roomAvatarUrl: string;
|
||||
inviterName: string;
|
||||
}
|
||||
|
||||
const STORAGE_PREFIX = "mx_threepid_invite_";
|
||||
|
||||
export default class ThreepidInviteStore extends EventEmitter {
|
||||
private static _instance: ThreepidInviteStore;
|
||||
|
||||
public static get instance(): ThreepidInviteStore {
|
||||
if (!ThreepidInviteStore._instance) {
|
||||
ThreepidInviteStore._instance = new ThreepidInviteStore();
|
||||
}
|
||||
return ThreepidInviteStore._instance;
|
||||
}
|
||||
|
||||
public storeInvite(roomId: string, wireInvite: IThreepidInviteWireFormat): IThreepidInvite {
|
||||
console.log("Storing invite: ", {roomId, ...wireInvite});
|
||||
const invite = <IPersistedThreepidInvite>{roomId, ...wireInvite};
|
||||
const id = this.generateIdOf(invite);
|
||||
localStorage.setItem(`${STORAGE_PREFIX}${id}`, JSON.stringify(invite));
|
||||
return this.translateInvite(invite);
|
||||
}
|
||||
|
||||
public getInvites(): IThreepidInvite[] {
|
||||
const result: IThreepidInvite[] = [];
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const keyName = localStorage.key(i);
|
||||
if (!keyName.startsWith(STORAGE_PREFIX)) continue;
|
||||
|
||||
const persisted = JSON.parse(localStorage.getItem(keyName)) as IPersistedThreepidInvite;
|
||||
result.push(this.translateInvite(persisted));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Currently Element can only handle one invite at a time, so handle that
|
||||
public pickBestInvite(): IThreepidInvite {
|
||||
return this.getInvites()[0];
|
||||
}
|
||||
|
||||
public resolveInvite(invite: IThreepidInvite) {
|
||||
localStorage.removeItem(`${STORAGE_PREFIX}${invite.id}`);
|
||||
}
|
||||
|
||||
private generateIdOf(persisted: IPersistedThreepidInvite): string {
|
||||
// Use a consistent "hash" to form an ID.
|
||||
return base32.stringify(Buffer.from(JSON.stringify(persisted)));
|
||||
}
|
||||
|
||||
private translateInvite(persisted: IPersistedThreepidInvite): IThreepidInvite {
|
||||
return {
|
||||
id: this.generateIdOf(persisted),
|
||||
roomId: persisted.roomId,
|
||||
toEmail: persisted.email,
|
||||
signUrl: persisted.signurl,
|
||||
roomName: persisted.room_name,
|
||||
roomAvatarUrl: persisted.room_avatar_url,
|
||||
inviterName: persisted.inviter_name,
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue