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;
}