/* 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 { base32 } from "rfc4648"; import { RoomType } from "matrix-js-sdk/src/@types/event"; // 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; // eslint-disable-line camelcase room_avatar_url: string; // eslint-disable-line camelcase inviter_name: string; // eslint-disable-line camelcase // TODO: Figure out if these are ever populated guest_access_token?: string; // eslint-disable-line camelcase guest_user_id?: string; // eslint-disable-line camelcase } 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; } // 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 // from an email invite (a workaround for the fact that we can't // get this information from the HS using an email invite). export interface IOOBData { name?: string; // The room's name avatarUrl?: string; // The mxc:// avatar URL for the room inviterName?: string; // The display name of the person who invited us to the room // eslint-disable-next-line camelcase room_name?: string; // The name of the room, to be used until we are told better by the server roomType?: RoomType | string; // The type of the room, to be used until we are told better by the server } 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 { const invite = { roomId, ...wireInvite }; const id = this.generateIdOf(invite); localStorage.setItem(`${STORAGE_PREFIX}${id}`, JSON.stringify(invite)); return this.translateInvite(invite); } public getWireInvites(): IPersistedThreepidInvite[] { const results: IPersistedThreepidInvite[] = []; for (let i = 0; i < localStorage.length; i++) { const keyName = localStorage.key(i); if (!keyName?.startsWith(STORAGE_PREFIX)) continue; results.push(JSON.parse(localStorage.getItem(keyName)) as IPersistedThreepidInvite); } return results; } public getInvites(): IThreepidInvite[] { return this.getWireInvites().map((i) => this.translateInvite(i)); } // Currently Element can only handle one invite at a time, so handle that public pickBestInvite(): IThreepidInvite { return this.getInvites()[0]; } public resolveInvite(invite: IThreepidInvite): void { 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, }; } public translateToWireFormat(invite: IThreepidInvite): IThreepidInviteWireFormat { return { email: invite.toEmail, signurl: invite.signUrl, room_name: invite.roomName, room_avatar_url: invite.roomAvatarUrl, inviter_name: invite.inviterName, }; } }