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.
This commit is contained in:
Bruno Windels 2019-11-15 15:16:23 +01:00
parent e86ceb986f
commit 48b1207c6e
4 changed files with 122 additions and 82 deletions

View file

@ -132,8 +132,8 @@ limitations under the License.
margin: 6px 0; margin: 6px 0;
.mx_IconButton { .mx_IconButton, .mx_Spinner {
margin-left: 6px; margin-left: 20px;
width: 16px; width: 16px;
height: 16px; height: 16px;
@ -148,6 +148,10 @@ limitations under the License.
align-items: center; align-items: center;
// try to make it the same height as the dropdown // try to make it the same height as the dropdown
margin: 11px 0 12px 0; margin: 11px 0 12px 0;
.mx_IconButton {
margin-left: 6px;
}
} }
.mx_Field { .mx_Field {

View file

@ -30,6 +30,6 @@ export function textualPowerLevel(level, usersDefault) {
if (LEVEL_ROLE_MAP[level]) { if (LEVEL_ROLE_MAP[level]) {
return LEVEL_ROLE_MAP[level]; return LEVEL_ROLE_MAP[level];
} else { } else {
return _t("Custom %(level)s", {level}); return _t("Custom (%(level)s)", {level});
} }
} }

View file

@ -837,9 +837,46 @@ function useRoomPermissions(cli, room, user) {
} }
const PowerLevelSection = withLegacyMatrixClient(({matrixClient: cli, user, room, roomPermissions, powerLevels}) => { const PowerLevelSection = withLegacyMatrixClient(({matrixClient: cli, user, room, roomPermissions, powerLevels}) => {
const [isEditing, setEditing] = useState(false);
if (room && user.roomId) { // is in room
if (isEditing) {
return (<PowerLevelEditor
user={user} room={room} roomPermissions={roomPermissions}
onFinished={() => 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 ?
(<IconButton icon="edit" onClick={() => setEditing(true)} />) : null;
const role = textualPowerLevel(powerLevel, powerLevelUsersDefault);
const label = _t("<strong>%(role)s</strong> in %(roomName)s",
{role, roomName: room.name},
{strong: label => <strong>{label}</strong>},
);
return (
<div className="mx_UserInfo_profileField">
<div className="mx_UserInfo_roleDescription">{label}{modifyButton}</div>
</div>
);
}
} 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) => { const _applyPowerChange = (roomId, target, powerLevel, powerLevelEvent) => {
startUpdating(); return cli.setPowerLevel(roomId, target, parseInt(powerLevel), powerLevelEvent).then(
cli.setPowerLevel(roomId, target, parseInt(powerLevel), powerLevelEvent).then(
function() { function() {
// NO-OP; rely on the m.room.member event coming down else we could // NO-OP; rely on the m.room.member event coming down else we could
// get out of sync if we force setState here! // 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"), description: _t("Failed to change power level"),
}); });
}, },
).finally(() => { );
stopUpdating();
}).done();
}; };
const roomId = user.roomId; try {
const target = user.userId; if (!isDirty) {
return;
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);
} }
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:
<div>
{ _t("You will not be able to undo this change as you are promoting the user " +
"to have the same power level as yourself.") }<br />
{ _t("Are you sure?") }
</div>,
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]; const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
if (parseInt(myPower) === parseInt(powerLevel)) { const powerLevelUsersDefault = powerLevelEvent ? powerLevelEvent.getContent().users_default : 0;
const {finished} = Modal.createTrackedDialog('Promote to PL100 Warning', '', QuestionDialog, { const IconButton = sdk.getComponent('elements.IconButton');
title: _t("Warning!"), const Spinner = sdk.getComponent("elements.Spinner");
description: const buttonOrSpinner = isUpdating ? <Spinner w={16} h={16} /> :
<div> <IconButton icon="check" onClick={changePowerLevel} />;
{ _t("You will not be able to undo this change as you are promoting the user " +
"to have the same power level as yourself.") }<br />
{ _t("Are you sure?") }
</div>,
button: _t("Continue"),
});
const [confirmed] = await finished; const PowerSelector = sdk.getComponent('elements.PowerSelector');
if (confirmed) return; return (
} <div className="mx_UserInfo_profileField">
_applyPowerChange(roomId, target, powerLevel, powerLevelEvent); <PowerSelector
}, [user.roomId, user.userId, room && room.currentState, cli]); // eslint-disable-line label={null}
value={selectedPowerLevel}
maxValue={roomPermissions.modifyLevelMax}
const [isEditingPL, setEditingPL] = useState(false); usersDefault={powerLevelUsersDefault}
if (room && user.roomId) { // is in room onChange={onPowerChange}
const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", ""); disabled={isUpdating}
const powerLevelUsersDefault = powerLevelEvent ? powerLevelEvent.getContent().users_default : 0; />
const powerLevel = parseInt(user.powerLevel); {buttonOrSpinner}
const IconButton = sdk.getComponent('elements.IconButton'); </div>
if (isEditingPL) { );
const PowerSelector = sdk.getComponent('elements.PowerSelector');
return (
<div className="mx_UserInfo_profileField">
<PowerSelector
label={null}
value={powerLevel}
maxValue={roomPermissions.modifyLevelMax}
usersDefault={powerLevelUsersDefault}
onChange={onPowerChange}
/>
<IconButton icon="check" onClick={() => setEditingPL(false)} />
</div>
);
} else {
const modifyButton = roomPermissions.canAffectUser ?
(<IconButton icon="edit" onClick={() => setEditingPL(true)} />) : null;
const role = textualPowerLevel(powerLevel, powerLevelUsersDefault);
const label = _t("<strong>%(role)s</strong> in %(roomName)s",
{role, roomName: room.name},
{strong: label => <strong>{label}</strong>},
);
return (<div className="mx_UserInfo_profileField"><div className="mx_UserInfo_roleDescription">{label}{modifyButton}</div></div>);
}
}
}); });
// cli is injected by withLegacyMatrixClient // cli is injected by withLegacyMatrixClient

View file

@ -118,7 +118,7 @@
"Restricted": "Restricted", "Restricted": "Restricted",
"Moderator": "Moderator", "Moderator": "Moderator",
"Admin": "Admin", "Admin": "Admin",
"Custom %(level)s": "Custom %(level)s", "Custom (%(level)s)": "Custom (%(level)s)",
"Start a chat": "Start a chat", "Start a chat": "Start a chat",
"Who would you like to communicate with?": "Who would you like to communicate with?", "Who would you like to communicate with?": "Who would you like to communicate with?",
"Email, name or Matrix ID": "Email, name or Matrix ID", "Email, name or Matrix ID": "Email, name or Matrix ID",
@ -1080,8 +1080,8 @@
"Remove this user from community?": "Remove this user from community?", "Remove this user from community?": "Remove this user from community?",
"Failed to withdraw invitation": "Failed to withdraw invitation", "Failed to withdraw invitation": "Failed to withdraw invitation",
"Failed to remove user from community": "Failed to remove user from community", "Failed to remove user from community": "Failed to remove user from community",
"Failed to deactivate user": "Failed to deactivate user",
"<strong>%(role)s</strong> in %(roomName)s": "<strong>%(role)s</strong> in %(roomName)s", "<strong>%(role)s</strong> in %(roomName)s": "<strong>%(role)s</strong> 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.", "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 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.", "Messages in this room are end-to-end encrypted.": "Messages in this room are end-to-end encrypted.",