mirror of https://github.com/vector-im/riot-web
				
				
				
			Share e2ee keys when using /invite SlashCommand (#7655)
							parent
							
								
									15276ea3b4
								
							
						
					
					
						commit
						cbc671b19f
					
				|  | @ -43,16 +43,19 @@ export interface IInviteResult { | |||
|  * | ||||
|  * @param {string} roomId The ID of the room to invite to | ||||
|  * @param {string[]} addresses Array of strings of addresses to invite. May be matrix IDs or 3pids. | ||||
|  * @param {boolean} sendSharedHistoryKeys whether to share e2ee keys with the invitees if applicable. | ||||
|  * @param {function} progressCallback optional callback, fired after each invite. | ||||
|  * @returns {Promise} Promise | ||||
|  */ | ||||
| export function inviteMultipleToRoom( | ||||
|     roomId: string, | ||||
|     addresses: string[], | ||||
|     sendSharedHistoryKeys = false, | ||||
|     progressCallback?: () => void, | ||||
| ): Promise<IInviteResult> { | ||||
|     const inviter = new MultiInviter(roomId, progressCallback); | ||||
|     return inviter.invite(addresses).then(states => Promise.resolve({ states, inviter })); | ||||
|     return inviter.invite(addresses, undefined, sendSharedHistoryKeys) | ||||
|         .then(states => Promise.resolve({ states, inviter })); | ||||
| } | ||||
| 
 | ||||
| export function showStartChatInviteDialog(initialText = ""): void { | ||||
|  | @ -110,8 +113,13 @@ export function isValid3pidInvite(event: MatrixEvent): boolean { | |||
|     return true; | ||||
| } | ||||
| 
 | ||||
| export function inviteUsersToRoom(roomId: string, userIds: string[], progressCallback?: () => void): Promise<void> { | ||||
|     return inviteMultipleToRoom(roomId, userIds, progressCallback).then((result) => { | ||||
| export function inviteUsersToRoom( | ||||
|     roomId: string, | ||||
|     userIds: string[], | ||||
|     sendSharedHistoryKeys = false, | ||||
|     progressCallback?: () => void, | ||||
| ): Promise<void> { | ||||
|     return inviteMultipleToRoom(roomId, userIds, sendSharedHistoryKeys, progressCallback).then((result) => { | ||||
|         const room = MatrixClientPeg.get().getRoom(roomId); | ||||
|         showAnyInviteErrors(result.states, room, result.inviter); | ||||
|     }).catch((err) => { | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ import { linkifyAndSanitizeHtml } from './HtmlUtils'; | |||
| import QuestionDialog from "./components/views/dialogs/QuestionDialog"; | ||||
| import WidgetUtils from "./utils/WidgetUtils"; | ||||
| import { textToHtmlRainbow } from "./utils/colour"; | ||||
| import { getAddressType } from './UserAddress'; | ||||
| import { AddressType, getAddressType } from './UserAddress'; | ||||
| import { abbreviateUrl } from './utils/UrlUtils'; | ||||
| import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils'; | ||||
| import { isPermalinkHost, parsePermalink } from "./utils/permalinks/Permalinks"; | ||||
|  | @ -500,7 +500,7 @@ export const Commands = [ | |||
|                     // meaningful.
 | ||||
|                     let prom = Promise.resolve(); | ||||
|                     if ( | ||||
|                         getAddressType(address) === 'email' && | ||||
|                         getAddressType(address) === AddressType.Email && | ||||
|                         !MatrixClientPeg.get().getIdentityServerUrl() | ||||
|                     ) { | ||||
|                         const defaultIdentityServerUrl = getDefaultIdentityServerUrl(); | ||||
|  | @ -541,7 +541,7 @@ export const Commands = [ | |||
|                     } | ||||
|                     const inviter = new MultiInviter(roomId); | ||||
|                     return success(prom.then(() => { | ||||
|                         return inviter.invite([address], reason); | ||||
|                         return inviter.invite([address], reason, true); | ||||
|                     }).then(() => { | ||||
|                         if (inviter.getCompletionState(address) !== "invited") { | ||||
|                             throw new Error(inviter.getErrorText(address)); | ||||
|  |  | |||
|  | @ -208,7 +208,7 @@ const CreateSpaceFromCommunityDialog: React.FC<IProps> = ({ matrixClient: cli, g | |||
|             setProgress(Progress.InvitingUsers); | ||||
| 
 | ||||
|             const userIds = [...members, ...invitedMembers].map(m => m.userId).filter(m => m !== cli.getUserId()); | ||||
|             await inviteUsersToRoom(roomId, userIds, () => setProgress(p => p + 1)); | ||||
|             await inviteUsersToRoom(roomId, userIds, false, () => setProgress(p => p + 1)); | ||||
| 
 | ||||
|             // eagerly remove it from the community panel
 | ||||
|             dis.dispatch(TagOrderActions.removeTag(cli, groupId)); | ||||
|  |  | |||
|  | @ -761,31 +761,11 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps | |||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             const result = await inviteMultipleToRoom(this.props.roomId, targetIds); | ||||
|             const result = await inviteMultipleToRoom(this.props.roomId, targetIds, true); | ||||
|             CountlyAnalytics.instance.trackSendInvite(startTime, this.props.roomId, targetIds.length); | ||||
|             if (!this.shouldAbortAfterInviteError(result, room)) { // handles setting error message too
 | ||||
|                 this.props.onFinished(); | ||||
|             } | ||||
| 
 | ||||
|             if (cli.isRoomEncrypted(this.props.roomId)) { | ||||
|                 const visibilityEvent = room.currentState.getStateEvents( | ||||
|                     "m.room.history_visibility", "", | ||||
|                 ); | ||||
|                 const visibility = visibilityEvent && visibilityEvent.getContent() && | ||||
|                     visibilityEvent.getContent().history_visibility; | ||||
|                 if (visibility == "world_readable" || visibility == "shared") { | ||||
|                     const invitedUsers = []; | ||||
|                     for (const [addr, state] of Object.entries(result.states)) { | ||||
|                         if (state === "invited" && getAddressType(addr) === "mx-user-id") { | ||||
|                             invitedUsers.push(addr); | ||||
|                         } | ||||
|                     } | ||||
|                     logger.log("Sharing history with", invitedUsers); | ||||
|                     cli.sendSharedHistoryKeys( | ||||
|                         this.props.roomId, invitedUsers, | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         } catch (err) { | ||||
|             logger.error(err); | ||||
|             this.setState({ | ||||
|  |  | |||
|  | @ -17,6 +17,9 @@ limitations under the License. | |||
| import { MatrixError } from "matrix-js-sdk/src/http-api"; | ||||
| import { defer, IDeferred } from "matrix-js-sdk/src/utils"; | ||||
| import { logger } from "matrix-js-sdk/src/logger"; | ||||
| import { MatrixClient } from "matrix-js-sdk/src/client"; | ||||
| import { EventType } from "matrix-js-sdk/src/@types/event"; | ||||
| import { HistoryVisibility } from "matrix-js-sdk/src/@types/partials"; | ||||
| 
 | ||||
| import { MatrixClientPeg } from '../MatrixClientPeg'; | ||||
| import { AddressType, getAddressType } from '../UserAddress'; | ||||
|  | @ -49,6 +52,7 @@ const USER_ALREADY_INVITED = "IO.ELEMENT.ALREADY_INVITED"; | |||
| export default class MultiInviter { | ||||
|     private readonly roomId?: string; | ||||
|     private readonly groupId?: string; | ||||
|     private readonly matrixClient: MatrixClient; | ||||
| 
 | ||||
|     private canceled = false; | ||||
|     private addresses: string[] = []; | ||||
|  | @ -71,6 +75,8 @@ export default class MultiInviter { | |||
|             this.roomId = targetId; | ||||
|             this.groupId = null; | ||||
|         } | ||||
| 
 | ||||
|         this.matrixClient = MatrixClientPeg.get(); | ||||
|     } | ||||
| 
 | ||||
|     public get fatal() { | ||||
|  | @ -83,9 +89,10 @@ export default class MultiInviter { | |||
|      * | ||||
|      * @param {array} addresses Array of addresses to invite | ||||
|      * @param {string} reason Reason for inviting (optional) | ||||
|      * @param {boolean} sendSharedHistoryKeys whether to share e2ee keys with the invitees if applicable. | ||||
|      * @returns {Promise} Resolved when all invitations in the queue are complete | ||||
|      */ | ||||
|     public invite(addresses, reason?: string): Promise<CompletionStates> { | ||||
|     public invite(addresses, reason?: string, sendSharedHistoryKeys = false): Promise<CompletionStates> { | ||||
|         if (this.addresses.length > 0) { | ||||
|             throw new Error("Already inviting/invited"); | ||||
|         } | ||||
|  | @ -104,7 +111,30 @@ export default class MultiInviter { | |||
|         this.deferred = defer<CompletionStates>(); | ||||
|         this.inviteMore(0); | ||||
| 
 | ||||
|         return this.deferred.promise; | ||||
|         if (!sendSharedHistoryKeys || !this.roomId || !this.matrixClient.isRoomEncrypted(this.roomId)) { | ||||
|             return this.deferred.promise; | ||||
|         } | ||||
| 
 | ||||
|         const room = this.matrixClient.getRoom(this.roomId); | ||||
|         const visibilityEvent = room?.currentState.getStateEvents(EventType.RoomHistoryVisibility, ""); | ||||
|         const visibility = visibilityEvent?.getContent().history_visibility; | ||||
| 
 | ||||
|         if (visibility !== HistoryVisibility.WorldReadable && visibility !== HistoryVisibility.Shared) { | ||||
|             return this.deferred.promise; | ||||
|         } | ||||
| 
 | ||||
|         return this.deferred.promise.then(async states => { | ||||
|             const invitedUsers = []; | ||||
|             for (const [addr, state] of Object.entries(states)) { | ||||
|                 if (state === InviteState.Invited && getAddressType(addr) === AddressType.MatrixUserId) { | ||||
|                     invitedUsers.push(addr); | ||||
|                 } | ||||
|             } | ||||
|             logger.log("Sharing history with", invitedUsers); | ||||
|             await this.matrixClient.sendSharedHistoryKeys(this.roomId, invitedUsers); | ||||
| 
 | ||||
|             return states; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | @ -129,9 +159,9 @@ export default class MultiInviter { | |||
|         const addrType = getAddressType(addr); | ||||
| 
 | ||||
|         if (addrType === AddressType.Email) { | ||||
|             return MatrixClientPeg.get().inviteByEmail(roomId, addr); | ||||
|             return this.matrixClient.inviteByEmail(roomId, addr); | ||||
|         } else if (addrType === AddressType.MatrixUserId) { | ||||
|             const room = MatrixClientPeg.get().getRoom(roomId); | ||||
|             const room = this.matrixClient.getRoom(roomId); | ||||
|             if (!room) throw new Error("Room not found"); | ||||
| 
 | ||||
|             const member = room.getMember(addr); | ||||
|  | @ -148,14 +178,14 @@ export default class MultiInviter { | |||
|             } | ||||
| 
 | ||||
|             if (!ignoreProfile && SettingsStore.getValue("promptBeforeInviteUnknownUsers", this.roomId)) { | ||||
|                 const profile = await MatrixClientPeg.get().getProfileInfo(addr); | ||||
|                 const profile = await this.matrixClient.getProfileInfo(addr); | ||||
|                 if (!profile) { | ||||
|                     // noinspection ExceptionCaughtLocallyJS
 | ||||
|                     throw new Error("User has no profile"); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return MatrixClientPeg.get().invite(roomId, addr, undefined, this.reason); | ||||
|             return this.matrixClient.invite(roomId, addr, undefined, this.reason); | ||||
|         } else { | ||||
|             throw new Error('Unsupported address'); | ||||
|         } | ||||
|  |  | |||
|  | @ -117,7 +117,7 @@ export async function upgradeRoom( | |||
| 
 | ||||
|     if (toInvite.length > 0) { | ||||
|         // Errors are handled internally to this function
 | ||||
|         await inviteUsersToRoom(newRoomId, toInvite, () => { | ||||
|         await inviteUsersToRoom(newRoomId, toInvite, false, () => { | ||||
|             progress.inviteUsersProgress++; | ||||
|             progressCallback?.(progress); | ||||
|         }); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Michael Telatynski
						Michael Telatynski