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..02adeb1307 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js @@ -26,6 +26,9 @@ import Promise from "bluebird"; import Modal from "../../../../../Modal"; import sdk from "../../../../.."; +// Delay between failing invite acceptance/rejections to avoid rate-limiting +const INVITE_MANAGEMENT_DELAY = 10000; + export class IgnoredUser extends React.Component { static propTypes = { userId: PropTypes.string.isRequired, @@ -52,9 +55,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 +100,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(INVITE_MANAGEMENT_DELAY); + + // 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 +218,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 ( -