diff --git a/res/css/views/settings/tabs/user/_SecurityUserSettingsTab.scss b/res/css/views/settings/tabs/user/_SecurityUserSettingsTab.scss index 4835640904..1c0a4b5864 100644 --- a/res/css/views/settings/tabs/user/_SecurityUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_SecurityUserSettingsTab.scss @@ -40,6 +40,10 @@ limitations under the License. margin-right: 10px; } +.mx_SecurityUserSettingsTab_bulkOptions .mx_AccessibleButton { + margin-right: 10px; +} + .mx_SecurityUserSettingsTab_importExportButtons { margin-bottom: 15px; } diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js index 7f68a1d6d7..c18f5bda53 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js @@ -52,9 +52,13 @@ export default class SecurityUserSettingsTab extends React.Component { constructor() { super(); + // Get number of rooms we're invited to + const invitedRooms = this._getInvitedRooms(); + this.state = { ignoredUserIds: MatrixClientPeg.get().getIgnoredUsers(), - rejectingInvites: false, + managingInvites: false, + invitedRoomAmt: invitedRooms.length, }; } @@ -93,24 +97,62 @@ export default class SecurityUserSettingsTab extends React.Component { this.setState({ignoredUsers}); }; - _onRejectAllInvitesClicked = (rooms, ev) => { + _getInvitedRooms = () => { + return MatrixClientPeg.get().getRooms().filter((r) => { + return r.hasMembershipState(MatrixClientPeg.get().getUserId(), "invite"); + }); + }; + + _manageInvites = async (accept) => { this.setState({ - rejectingInvites: true, + managingInvites: true, }); - // reject the invites - const promises = rooms.map((room) => { - return MatrixClientPeg.get().leave(room.roomId).catch((e) => { - // purposefully drop errors to the floor: we'll just have a non-zero number on the UI - // after trying to reject all the invites. - }); - }); - Promise.all(promises).then(() => { - this.setState({ - rejectingInvites: false, + + // Compile array of invitation room ids + const invitedRoomIds = this._getInvitedRooms().map((room) => { + return room.roomId; + }); + + // Execute all acceptances/rejections sequentially + const self = this; + const cli = MatrixClientPeg.get(); + const action = accept ? cli.joinRoom.bind(cli) : cli.leave.bind(cli); + for (let i = 0; i < invitedRoomIds.length; i++) { + const roomId = invitedRoomIds[i]; + + // Accept/reject invite + await action(roomId).then(() => { + // No error, update invited rooms button + this.setState({invitedRoomAmt: self.state.invitedRoomAmt - 1}); + }, async (e) => { + // Action failure + if (e.errcode === "M_LIMIT_EXCEEDED") { + // Add a delay between each invite change in order to avoid rate + // limiting by the server. + await Promise.delay(e.retry_after_ms || 2500); + + // Redo last action + i--; + } else { + // Print out error with joining/leaving room + console.warn(e); + } }); + } + + this.setState({ + managingInvites: false, }); }; + _onAcceptAllInvitesClicked = (ev) => { + this._manageInvites(true); + }; + + _onRejectAllInvitesClicked = (ev) => { + this._manageInvites(false); + }; + _renderCurrentDeviceInfo() { const SettingsFlag = sdk.getComponent('views.elements.SettingsFlag'); @@ -173,21 +215,25 @@ export default class SecurityUserSettingsTab extends React.Component { ); } - _renderRejectInvites() { - const invitedRooms = MatrixClientPeg.get().getRooms().filter((r) => { - return r.hasMembershipState(MatrixClientPeg.get().getUserId(), "invite"); - }); - if (invitedRooms.length === 0) { + _renderManageInvites() { + if (this.state.invitedRoomAmt === 0) { return null; } - const onClick = this._onRejectAllInvitesClicked.bind(this, invitedRooms); + const invitedRooms = this._getInvitedRooms(); + const InlineSpinner = sdk.getComponent('elements.InlineSpinner'); + const onClickAccept = this._onAcceptAllInvitesClicked.bind(this, invitedRooms); + const onClickReject = this._onRejectAllInvitesClicked.bind(this, invitedRooms); return ( -
+
{_t('Bulk options')} - - {_t("Reject all %(invitedRooms)s invites", {invitedRooms: invitedRooms.length})} + + {_t("Accept all %(invitedRooms)s invites", {invitedRooms: this.state.invitedRoomAmt})} + + {_t("Reject all %(invitedRooms)s invites", {invitedRooms: this.state.invitedRoomAmt})} + + {this.state.managingInvites ? :
}
); } @@ -232,7 +278,7 @@ export default class SecurityUserSettingsTab extends React.Component { onChange={this._updateAnalytics} />
{this._renderIgnoredUsers()} - {this._renderRejectInvites()} + {this._renderManageInvites()}
); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d096ab4f1f..8cc85b6036 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -560,6 +560,7 @@ "Device key:": "Device key:", "Ignored users": "Ignored users", "Bulk options": "Bulk options", + "Accept all %(invitedRooms)s invites": "Accept all %(invitedRooms)s invites", "Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites", "Key backup": "Key backup", "Security & Privacy": "Security & Privacy",