mirror of https://github.com/vector-im/riot-web
add PL edit mode, don't show selector by default
still saves when changing the selector thoughpull/21833/head
parent
91e02aa623
commit
6db162a3a7
|
@ -125,8 +125,34 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_UserInfo_memberDetails {
|
.mx_UserInfo_memberDetails .mx_UserInfo_profileField {
|
||||||
text-align: center;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
margin: 6px 0;
|
||||||
|
|
||||||
|
.mx_IconButton {
|
||||||
|
margin-left: 6px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
mask-size: 80%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserInfo_roleDescription {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
// try to make it the same height as the dropdown
|
||||||
|
margin: 11px 0 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Field {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_UserInfo_field {
|
.mx_UserInfo_field {
|
||||||
|
|
|
@ -27,7 +27,6 @@ import sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import createRoom from '../../../createRoom';
|
import createRoom from '../../../createRoom';
|
||||||
import DMRoomMap from '../../../utils/DMRoomMap';
|
import DMRoomMap from '../../../utils/DMRoomMap';
|
||||||
import Unread from '../../../Unread';
|
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
@ -40,6 +39,7 @@ import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||||
import E2EIcon from "../rooms/E2EIcon";
|
import E2EIcon from "../rooms/E2EIcon";
|
||||||
import withLegacyMatrixClient from "../../../utils/withLegacyMatrixClient";
|
import withLegacyMatrixClient from "../../../utils/withLegacyMatrixClient";
|
||||||
import {useEventEmitter} from "../../../hooks/useEventEmitter";
|
import {useEventEmitter} from "../../../hooks/useEventEmitter";
|
||||||
|
import {textualPowerLevel} from '../../../Roles';
|
||||||
|
|
||||||
const _disambiguateDevices = (devices) => {
|
const _disambiguateDevices = (devices) => {
|
||||||
const names = Object.create(null);
|
const names = Object.create(null);
|
||||||
|
@ -780,43 +780,11 @@ const useIsSynapseAdmin = (cli) => {
|
||||||
return isAdmin;
|
return isAdmin;
|
||||||
};
|
};
|
||||||
|
|
||||||
// cli is injected by withLegacyMatrixClient
|
function useRoomPermissions(cli, room, user) {
|
||||||
const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, roomId, onClose}) => {
|
|
||||||
// Load room if we are given a room id and memoize it
|
|
||||||
const room = useMemo(() => roomId ? cli.getRoom(roomId) : null, [cli, roomId]);
|
|
||||||
|
|
||||||
// only display the devices list if our client supports E2E
|
|
||||||
const _enableDevices = cli.isCryptoEnabled();
|
|
||||||
|
|
||||||
// Load whether or not we are a Synapse Admin
|
|
||||||
const isSynapseAdmin = useIsSynapseAdmin(cli);
|
|
||||||
|
|
||||||
// Check whether the user is ignored
|
|
||||||
const [isIgnored, setIsIgnored] = useState(cli.isUserIgnored(user.userId));
|
|
||||||
// Recheck if the user or client changes
|
|
||||||
useEffect(() => {
|
|
||||||
setIsIgnored(cli.isUserIgnored(user.userId));
|
|
||||||
}, [cli, user.userId]);
|
|
||||||
// Recheck also if we receive new accountData m.ignored_user_list
|
|
||||||
const accountDataHandler = useCallback((ev) => {
|
|
||||||
if (ev.getType() === "m.ignored_user_list") {
|
|
||||||
setIsIgnored(cli.isUserIgnored(user.userId));
|
|
||||||
}
|
|
||||||
}, [cli, user.userId]);
|
|
||||||
useEventEmitter(cli, "accountData", accountDataHandler);
|
|
||||||
|
|
||||||
// Count of how many operations are currently in progress, if > 0 then show a Spinner
|
|
||||||
const [pendingUpdateCount, setPendingUpdateCount] = useState(0);
|
|
||||||
const startUpdating = useCallback(() => {
|
|
||||||
setPendingUpdateCount(pendingUpdateCount + 1);
|
|
||||||
}, [pendingUpdateCount]);
|
|
||||||
const stopUpdating = useCallback(() => {
|
|
||||||
setPendingUpdateCount(pendingUpdateCount - 1);
|
|
||||||
}, [pendingUpdateCount]);
|
|
||||||
|
|
||||||
const [roomPermissions, setRoomPermissions] = useState({
|
const [roomPermissions, setRoomPermissions] = useState({
|
||||||
// modifyLevelMax is the max PL we can set this user to, typically min(their PL, our PL) && canSetPL
|
// modifyLevelMax is the max PL we can set this user to, typically min(their PL, our PL) && canSetPL
|
||||||
modifyLevelMax: -1,
|
modifyLevelMax: -1,
|
||||||
|
canAffectUser: false,
|
||||||
canInvite: false,
|
canInvite: false,
|
||||||
});
|
});
|
||||||
const updateRoomPermissions = useCallback(async () => {
|
const updateRoomPermissions = useCallback(async () => {
|
||||||
|
@ -847,6 +815,7 @@ const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, room
|
||||||
|
|
||||||
setRoomPermissions({
|
setRoomPermissions({
|
||||||
canInvite: me.powerLevel >= powerLevels.invite,
|
canInvite: me.powerLevel >= powerLevels.invite,
|
||||||
|
canAffectUser,
|
||||||
modifyLevelMax,
|
modifyLevelMax,
|
||||||
});
|
});
|
||||||
}, [cli, user, room]);
|
}, [cli, user, room]);
|
||||||
|
@ -856,38 +825,16 @@ const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, room
|
||||||
return () => {
|
return () => {
|
||||||
setRoomPermissions({
|
setRoomPermissions({
|
||||||
maximalPowerLevel: -1,
|
maximalPowerLevel: -1,
|
||||||
|
canAffectUser: false,
|
||||||
canInvite: false,
|
canInvite: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}, [updateRoomPermissions]);
|
}, [updateRoomPermissions]);
|
||||||
|
|
||||||
const onSynapseDeactivate = useCallback(async () => {
|
return roomPermissions;
|
||||||
const QuestionDialog = sdk.getComponent('views.dialogs.QuestionDialog');
|
}
|
||||||
const {finished} = Modal.createTrackedDialog('Synapse User Deactivation', '', QuestionDialog, {
|
|
||||||
title: _t("Deactivate user?"),
|
|
||||||
description:
|
|
||||||
<div>{ _t(
|
|
||||||
"Deactivating this user will log them out and prevent them from logging back in. Additionally, " +
|
|
||||||
"they will leave all the rooms they are in. This action cannot be reversed. Are you sure you " +
|
|
||||||
"want to deactivate this user?",
|
|
||||||
) }</div>,
|
|
||||||
button: _t("Deactivate user"),
|
|
||||||
danger: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [accepted] = await finished;
|
|
||||||
if (!accepted) return;
|
|
||||||
try {
|
|
||||||
cli.deactivateSynapseUser(user.userId);
|
|
||||||
} catch (err) {
|
|
||||||
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
|
|
||||||
Modal.createTrackedDialog('Failed to deactivate user', '', ErrorDialog, {
|
|
||||||
title: _t('Failed to deactivate user'),
|
|
||||||
description: ((err && err.message) ? err.message : _t("Operation failed")),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [cli, user.userId]);
|
|
||||||
|
|
||||||
|
const PowerLevelEditor = withLegacyMatrixClient(({matrixClient: cli, user, room, startUpdating, stopUpdating, roomPermissions}) => {
|
||||||
const onPowerChange = useCallback(async (powerLevel) => {
|
const onPowerChange = useCallback(async (powerLevel) => {
|
||||||
const _applyPowerChange = (roomId, target, powerLevel, powerLevelEvent) => {
|
const _applyPowerChange = (roomId, target, powerLevel, powerLevelEvent) => {
|
||||||
startUpdating();
|
startUpdating();
|
||||||
|
@ -953,6 +900,104 @@ const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, room
|
||||||
_applyPowerChange(roomId, target, powerLevel, powerLevelEvent);
|
_applyPowerChange(roomId, target, powerLevel, powerLevelEvent);
|
||||||
}, [user.roomId, user.userId, room && room.currentState, cli]); // eslint-disable-line
|
}, [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 (
|
||||||
|
<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
|
||||||
|
const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, roomId, onClose}) => {
|
||||||
|
// Load room if we are given a room id and memoize it
|
||||||
|
const room = useMemo(() => roomId ? cli.getRoom(roomId) : null, [cli, roomId]);
|
||||||
|
|
||||||
|
// only display the devices list if our client supports E2E
|
||||||
|
const _enableDevices = cli.isCryptoEnabled();
|
||||||
|
|
||||||
|
// Load whether or not we are a Synapse Admin
|
||||||
|
const isSynapseAdmin = useIsSynapseAdmin(cli);
|
||||||
|
|
||||||
|
// Check whether the user is ignored
|
||||||
|
const [isIgnored, setIsIgnored] = useState(cli.isUserIgnored(user.userId));
|
||||||
|
// Recheck if the user or client changes
|
||||||
|
useEffect(() => {
|
||||||
|
setIsIgnored(cli.isUserIgnored(user.userId));
|
||||||
|
}, [cli, user.userId]);
|
||||||
|
// Recheck also if we receive new accountData m.ignored_user_list
|
||||||
|
const accountDataHandler = useCallback((ev) => {
|
||||||
|
if (ev.getType() === "m.ignored_user_list") {
|
||||||
|
setIsIgnored(cli.isUserIgnored(user.userId));
|
||||||
|
}
|
||||||
|
}, [cli, user.userId]);
|
||||||
|
useEventEmitter(cli, "accountData", accountDataHandler);
|
||||||
|
|
||||||
|
// Count of how many operations are currently in progress, if > 0 then show a Spinner
|
||||||
|
const [pendingUpdateCount, setPendingUpdateCount] = useState(0);
|
||||||
|
const startUpdating = useCallback(() => {
|
||||||
|
setPendingUpdateCount(pendingUpdateCount + 1);
|
||||||
|
}, [pendingUpdateCount]);
|
||||||
|
const stopUpdating = useCallback(() => {
|
||||||
|
setPendingUpdateCount(pendingUpdateCount - 1);
|
||||||
|
}, [pendingUpdateCount]);
|
||||||
|
|
||||||
|
const roomPermissions = useRoomPermissions(cli, room, user);
|
||||||
|
|
||||||
|
const onSynapseDeactivate = useCallback(async () => {
|
||||||
|
const QuestionDialog = sdk.getComponent('views.dialogs.QuestionDialog');
|
||||||
|
const {finished} = Modal.createTrackedDialog('Synapse User Deactivation', '', QuestionDialog, {
|
||||||
|
title: _t("Deactivate user?"),
|
||||||
|
description:
|
||||||
|
<div>{ _t(
|
||||||
|
"Deactivating this user will log them out and prevent them from logging back in. Additionally, " +
|
||||||
|
"they will leave all the rooms they are in. This action cannot be reversed. Are you sure you " +
|
||||||
|
"want to deactivate this user?",
|
||||||
|
) }</div>,
|
||||||
|
button: _t("Deactivate user"),
|
||||||
|
danger: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [accepted] = await finished;
|
||||||
|
if (!accepted) return;
|
||||||
|
try {
|
||||||
|
cli.deactivateSynapseUser(user.userId);
|
||||||
|
} catch (err) {
|
||||||
|
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
|
||||||
|
Modal.createTrackedDialog('Failed to deactivate user', '', ErrorDialog, {
|
||||||
|
title: _t('Failed to deactivate user'),
|
||||||
|
description: ((err && err.message) ? err.message : _t("Operation failed")),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [cli, user.userId]);
|
||||||
|
|
||||||
|
|
||||||
const onMemberAvatarKey = e => {
|
const onMemberAvatarKey = e => {
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
onMemberAvatarClick();
|
onMemberAvatarClick();
|
||||||
|
@ -1058,26 +1103,6 @@ const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, room
|
||||||
statusLabel = <span className="mx_UserInfo_statusMessage">{ statusMessage }</span>;
|
statusLabel = <span className="mx_UserInfo_statusMessage">{ statusMessage }</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let memberDetails = null;
|
|
||||||
|
|
||||||
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 PowerSelector = sdk.getComponent('elements.PowerSelector');
|
|
||||||
memberDetails = <div>
|
|
||||||
<div className="mx_UserInfo_profileField">
|
|
||||||
<PowerSelector
|
|
||||||
value={parseInt(user.powerLevel)}
|
|
||||||
maxValue={roomPermissions.modifyLevelMax}
|
|
||||||
disabled={roomPermissions.modifyLevelMax < 0}
|
|
||||||
usersDefault={powerLevelUsersDefault}
|
|
||||||
onChange={onPowerChange} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const avatarUrl = user.getMxcAvatarUrl ? user.getMxcAvatarUrl() : user.avatarUrl;
|
const avatarUrl = user.getMxcAvatarUrl ? user.getMxcAvatarUrl() : user.avatarUrl;
|
||||||
let avatarElement;
|
let avatarElement;
|
||||||
if (avatarUrl) {
|
if (avatarUrl) {
|
||||||
|
@ -1102,6 +1127,11 @@ const UserInfo = withLegacyMatrixClient(({matrixClient: cli, user, groupId, room
|
||||||
title={_t('Close')} />;
|
title={_t('Close')} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const memberDetails = <PowerLevelEditor
|
||||||
|
user={user} room={room} roomPermissions={roomPermissions}
|
||||||
|
startUpdating={startUpdating} stopUpdating={stopUpdating}
|
||||||
|
/>;
|
||||||
|
|
||||||
const isRoomEncrypted = useIsEncrypted(cli, room);
|
const isRoomEncrypted = useIsEncrypted(cli, room);
|
||||||
// undefined means yet to be loaded, null means failed to load, otherwise list of devices
|
// undefined means yet to be loaded, null means failed to load, otherwise list of devices
|
||||||
const [devices, setDevices] = useState(undefined);
|
const [devices, setDevices] = useState(undefined);
|
||||||
|
|
|
@ -118,6 +118,7 @@
|
||||||
"Restricted": "Restricted",
|
"Restricted": "Restricted",
|
||||||
"Moderator": "Moderator",
|
"Moderator": "Moderator",
|
||||||
"Admin": "Admin",
|
"Admin": "Admin",
|
||||||
|
"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,6 +1081,7 @@
|
||||||
"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",
|
"Failed to deactivate user": "Failed to deactivate user",
|
||||||
|
"<strong>%(role)s</strong> in %(roomName)s": "<strong>%(role)s</strong> in %(roomName)s",
|
||||||
"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.",
|
||||||
|
|
Loading…
Reference in New Issue