From 48b1207c6ed2ea07d764f96db5429c2a6c72f24d Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 15 Nov 2019 15:16:23 +0100 Subject: [PATCH] split up PowerLevelEditor into two components one while editing (PowerLevelEditor) and one while not editing (PowerLevelSection). Also save when pressing the button, not when changing the power dropdown. Also show the spinner next to the dropdown when saving, not at the bottom of the component. --- res/css/views/right_panel/_UserInfo.scss | 8 +- src/Roles.js | 2 +- src/components/views/right_panel/UserInfo.js | 190 +++++++++++-------- src/i18n/strings/en_EN.json | 4 +- 4 files changed, 122 insertions(+), 82 deletions(-) diff --git a/res/css/views/right_panel/_UserInfo.scss b/res/css/views/right_panel/_UserInfo.scss index 9e4d4dc471..2b2add49ee 100644 --- a/res/css/views/right_panel/_UserInfo.scss +++ b/res/css/views/right_panel/_UserInfo.scss @@ -132,8 +132,8 @@ limitations under the License. margin: 6px 0; - .mx_IconButton { - margin-left: 6px; + .mx_IconButton, .mx_Spinner { + margin-left: 20px; width: 16px; height: 16px; @@ -148,6 +148,10 @@ limitations under the License. align-items: center; // try to make it the same height as the dropdown margin: 11px 0 12px 0; + + .mx_IconButton { + margin-left: 6px; + } } .mx_Field { diff --git a/src/Roles.js b/src/Roles.js index 4c0d2ab4e6..7cc3c880d7 100644 --- a/src/Roles.js +++ b/src/Roles.js @@ -30,6 +30,6 @@ export function textualPowerLevel(level, usersDefault) { if (LEVEL_ROLE_MAP[level]) { return LEVEL_ROLE_MAP[level]; } else { - return _t("Custom %(level)s", {level}); + return _t("Custom (%(level)s)", {level}); } } diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index a0a256e4c8..589eca9a08 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -837,9 +837,46 @@ function useRoomPermissions(cli, room, user) { } const PowerLevelSection = withLegacyMatrixClient(({matrixClient: cli, user, room, roomPermissions, powerLevels}) => { + const [isEditing, setEditing] = useState(false); + if (room && user.roomId) { // is in room + if (isEditing) { + return ( setEditing(false)} />); + } else { + const IconButton = sdk.getComponent('elements.IconButton'); + const powerLevelUsersDefault = powerLevels.users_default || 0; + const powerLevel = parseInt(user.powerLevel, 10); + const modifyButton = roomPermissions.canEdit ? + ( setEditing(true)} />) : null; + const role = textualPowerLevel(powerLevel, powerLevelUsersDefault); + const label = _t("%(role)s in %(roomName)s", + {role, roomName: room.name}, + {strong: label => {label}}, + ); + return ( +
+
{label}{modifyButton}
+
+ ); + } + } else { + return null; + } +}); + +const PowerLevelEditor = withLegacyMatrixClient(({matrixClient: cli, user, room, roomPermissions, onFinished}) => { + const [isUpdating, setIsUpdating] = useState(false); + const [selectedPowerLevel, setSelectedPowerLevel] = useState(parseInt(user.powerLevel, 10)); + const [isDirty, setIsDirty] = useState(false); + const onPowerChange = useCallback((powerLevel) => { + setIsDirty(true); + setSelectedPowerLevel(parseInt(powerLevel, 10)); + }, [setSelectedPowerLevel, setIsDirty]); + + const changePowerLevel = useCallback(async () => { const _applyPowerChange = (roomId, target, powerLevel, powerLevelEvent) => { - startUpdating(); - cli.setPowerLevel(roomId, target, parseInt(powerLevel), powerLevelEvent).then( + return cli.setPowerLevel(roomId, target, parseInt(powerLevel), powerLevelEvent).then( function() { // NO-OP; rely on the m.room.member event coming down else we could // get out of sync if we force setState here! @@ -852,87 +889,86 @@ const PowerLevelSection = withLegacyMatrixClient(({matrixClient: cli, user, room description: _t("Failed to change power level"), }); }, - ).finally(() => { - stopUpdating(); - }).done(); + ); }; - const roomId = user.roomId; - const target = user.userId; - - const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", ""); - if (!powerLevelEvent) return; - - if (!powerLevelEvent.getContent().users) { - _applyPowerChange(roomId, target, powerLevel, powerLevelEvent); - return; - } - - const myUserId = cli.getUserId(); - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - - // If we are changing our own PL it can only ever be decreasing, which we cannot reverse. - if (myUserId === target) { - try { - if (!(await _warnSelfDemote())) return; - _applyPowerChange(roomId, target, powerLevel, powerLevelEvent); - } catch (e) { - console.error("Failed to warn about self demotion: ", e); + try { + if (!isDirty) { + return; } - return; + + setIsUpdating(true); + + const powerLevel = selectedPowerLevel; + + const roomId = user.roomId; + const target = user.userId; + + const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", ""); + if (!powerLevelEvent) return; + + if (!powerLevelEvent.getContent().users) { + _applyPowerChange(roomId, target, powerLevel, powerLevelEvent); + return; + } + + const myUserId = cli.getUserId(); + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + + // If we are changing our own PL it can only ever be decreasing, which we cannot reverse. + if (myUserId === target) { + try { + if (!(await _warnSelfDemote())) return; + } catch (e) { + console.error("Failed to warn about self demotion: ", e); + } + await _applyPowerChange(roomId, target, powerLevel, powerLevelEvent); + return; + } + + const myPower = powerLevelEvent.getContent().users[myUserId]; + if (parseInt(myPower) === parseInt(powerLevel)) { + const {finished} = Modal.createTrackedDialog('Promote to PL100 Warning', '', QuestionDialog, { + title: _t("Warning!"), + description: +
+ { _t("You will not be able to undo this change as you are promoting the user " + + "to have the same power level as yourself.") }
+ { _t("Are you sure?") } +
, + button: _t("Continue"), + }); + + const [confirmed] = await finished; + if (confirmed) return; + } + await _applyPowerChange(roomId, target, powerLevel, powerLevelEvent); + } finally { + onFinished(); } + }, [user.roomId, user.userId, cli, selectedPowerLevel, isDirty, setIsUpdating, onFinished, room]); - const myPower = powerLevelEvent.getContent().users[myUserId]; - if (parseInt(myPower) === parseInt(powerLevel)) { - const {finished} = Modal.createTrackedDialog('Promote to PL100 Warning', '', QuestionDialog, { - title: _t("Warning!"), - description: -
- { _t("You will not be able to undo this change as you are promoting the user " + - "to have the same power level as yourself.") }
- { _t("Are you sure?") } -
, - button: _t("Continue"), - }); + const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", ""); + const powerLevelUsersDefault = powerLevelEvent ? powerLevelEvent.getContent().users_default : 0; + const IconButton = sdk.getComponent('elements.IconButton'); + const Spinner = sdk.getComponent("elements.Spinner"); + const buttonOrSpinner = isUpdating ? : + ; - const [confirmed] = await finished; - if (confirmed) return; - } - _applyPowerChange(roomId, target, powerLevel, powerLevelEvent); - }, [user.roomId, user.userId, room && room.currentState, cli]); // eslint-disable-line - - - const [isEditingPL, setEditingPL] = useState(false); - if (room && user.roomId) { // is in room - const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", ""); - const powerLevelUsersDefault = powerLevelEvent ? powerLevelEvent.getContent().users_default : 0; - const powerLevel = parseInt(user.powerLevel); - const IconButton = sdk.getComponent('elements.IconButton'); - if (isEditingPL) { - const PowerSelector = sdk.getComponent('elements.PowerSelector'); - return ( -
- - setEditingPL(false)} /> -
- ); - } else { - const modifyButton = roomPermissions.canAffectUser ? - ( setEditingPL(true)} />) : null; - const role = textualPowerLevel(powerLevel, powerLevelUsersDefault); - const label = _t("%(role)s in %(roomName)s", - {role, roomName: room.name}, - {strong: label => {label}}, - ); - return (
{label}{modifyButton}
); - } - } + const PowerSelector = sdk.getComponent('elements.PowerSelector'); + return ( +
+ + {buttonOrSpinner} +
+ ); }); // cli is injected by withLegacyMatrixClient diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 46ad7d5135..029000e9d2 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -118,7 +118,7 @@ "Restricted": "Restricted", "Moderator": "Moderator", "Admin": "Admin", - "Custom %(level)s": "Custom %(level)s", + "Custom (%(level)s)": "Custom (%(level)s)", "Start a chat": "Start a chat", "Who would you like to communicate with?": "Who would you like to communicate with?", "Email, name or Matrix ID": "Email, name or Matrix ID", @@ -1080,8 +1080,8 @@ "Remove this user from community?": "Remove this user from community?", "Failed to withdraw invitation": "Failed to withdraw invitation", "Failed to remove user from community": "Failed to remove user from community", - "Failed to deactivate user": "Failed to deactivate user", "%(role)s in %(roomName)s": "%(role)s in %(roomName)s", + "Failed to deactivate user": "Failed to deactivate user", "This client does not support end-to-end encryption.": "This client does not support end-to-end encryption.", "Messages in this room are not end-to-end encrypted.": "Messages in this room are not end-to-end encrypted.", "Messages in this room are end-to-end encrypted.": "Messages in this room are end-to-end encrypted.",