diff --git a/src/app/organisms/profile-viewer/ProfileViewer.jsx b/src/app/organisms/profile-viewer/ProfileViewer.jsx index 87f085d..e6b3a83 100644 --- a/src/app/organisms/profile-viewer/ProfileViewer.jsx +++ b/src/app/organisms/profile-viewer/ProfileViewer.jsx @@ -17,6 +17,7 @@ import colorMXID from '../../../util/colorMXID'; import Text from '../../atoms/text/Text'; import Chip from '../../atoms/chip/Chip'; import IconButton from '../../atoms/button/IconButton'; +import Input from '../../atoms/input/Input'; import Avatar from '../../atoms/avatar/Avatar'; import Button from '../../atoms/button/Button'; import PowerLevelSelector from '../../molecules/power-level-selector/PowerLevelSelector'; @@ -29,6 +30,45 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import { useForceUpdate } from '../../hooks/useForceUpdate'; +function ModerationTools({ + roomId, userId, +}) { + const mx = initMatrix.matrixClient; + const room = mx.getRoom(roomId); + const roomMember = room.getMember(userId); + + const myPowerLevel = room.getMember(mx.getUserId()).powerLevel; + const powerLevel = roomMember?.powerLevel || 0; + const canIKick = ( + roomMember?.membership === 'join' + && room.currentState.hasSufficientPowerLevelFor('kick', myPowerLevel) + && powerLevel < myPowerLevel + ); + + const handleKick = (e) => { + e.preventDefault(); + const kickReason = e.target.elements['kick-reason']?.value.trim(); + roomActions.kick(roomId, userId, kickReason !== '' ? kickReason : undefined); + }; + + return ( +
+ {canIKick && ( + <> +
+ + +
+ + )} +
+ ); +} +ModerationTools.propTypes = { + roomId: PropTypes.string.isRequired, + userId: PropTypes.string.isRequired, +}; + function SessionInfo({ userId }) { const [devices, setDevices] = useState(null); const mx = initMatrix.matrixClient; @@ -257,25 +297,30 @@ function useToggleDialog() { return [isOpen, roomId, userId, closeDialog, afterClose]; } -function useRerenderOnRoleChange(roomId, userId) { +function useRerenderOnProfileChange(roomId, userId) { const mx = initMatrix.matrixClient; const [, forceUpdate] = useForceUpdate(); useEffect(() => { - const handlePowerLevelChange = (mEvent, member) => { - if (mEvent.getRoomId() === roomId && member.userId === userId) { + const handleProfileChange = (mEvent, member) => { + if ( + mEvent.getRoomId() === roomId + && (member.userId === userId || member.userId === mx.getUserId()) + ) { forceUpdate(); } }; - mx.on('RoomMember.powerLevel', handlePowerLevelChange); + mx.on('RoomMember.powerLevel', handleProfileChange); + mx.on('RoomMember.membership', handleProfileChange); return () => { - mx.removeListener('RoomMember.powerLevel', handlePowerLevelChange); + mx.removeListener('RoomMember.powerLevel', handleProfileChange); + mx.removeListener('RoomMember.membership', handleProfileChange); }; }, [roomId, userId]); } function ProfileViewer() { const [isOpen, roomId, userId, closeDialog, handleAfterClose] = useToggleDialog(); - useRerenderOnRoleChange(roomId, userId); + useRerenderOnProfileChange(roomId, userId); const mx = initMatrix.matrixClient; const room = mx.getRoom(roomId); @@ -288,8 +333,8 @@ function ProfileViewer() { } const renderProfile = () => { - const avatarMxc = roomMember.getMxcAvatarUrl?.() || mx.getMember(userId).avatarUrl; - const avatarUrl = avatarMxc ? mx.mxcUrlToHttp(avatarMxc, 80, 80, 'crop') : null; + const avatarMxc = roomMember?.getMxcAvatarUrl?.() || mx.getUser(userId).avatarUrl; + const avatarUrl = (avatarMxc && avatarMxc !== 'null') ? mx.mxcUrlToHttp(avatarMxc, 80, 80, 'crop') : null; const powerLevel = roomMember.powerLevel || 0; const myPowerLevel = room.getMember(mx.getUserId())?.powerLevel || 0; @@ -344,13 +389,10 @@ function ProfileViewer() { + { userId !== mx.getUserId() && ( - + )} ); diff --git a/src/app/organisms/profile-viewer/ProfileViewer.scss b/src/app/organisms/profile-viewer/ProfileViewer.scss index 230c8db..b8c4642 100644 --- a/src/app/organisms/profile-viewer/ProfileViewer.scss +++ b/src/app/organisms/profile-viewer/ProfileViewer.scss @@ -1,3 +1,4 @@ +@use '../../partials/flex'; @use '../../partials/dir'; .profile-viewer__dialog { @@ -61,6 +62,27 @@ } } +.profile-viewer__admin-tool { + .setting-tile { + margin-top: var(--sp-loose); + } +} + +.moderation-tools { + & > form { + margin: var(--sp-normal) 0; + display: flex; + align-items: flex-end; + & .input-container { + @extend .cp-fx__item-one; + @include dir.side(margin, 0, var(--sp-tight)); + } + & button { + height: 46px; + } + } +} + .session-info { & .setting-tile__title .text { color: var(--tc-surface-high); diff --git a/src/client/action/room.js b/src/client/action/room.js index 73783ef..8d67afd 100644 --- a/src/client/action/room.js +++ b/src/client/action/room.js @@ -192,10 +192,10 @@ async function invite(roomId, userId) { return result; } -async function kick(roomId, userId) { +async function kick(roomId, userId, reason) { const mx = initMatrix.matrixClient; - const result = await mx.kick(roomId, userId); + const result = await mx.kick(roomId, userId, reason); return result; }