Add ability to change power level in profile viewer
authorAjay Bura <ajbura@gmail.com>
Wed, 12 Jan 2022 08:27:13 +0000 (13:57 +0530)
committerAjay Bura <ajbura@gmail.com>
Wed, 12 Jan 2022 08:27:13 +0000 (13:57 +0530)
Signed-off-by: Ajay Bura <ajbura@gmail.com>
src/app/organisms/profile-viewer/ProfileViewer.jsx
src/app/templates/client/Client.jsx
src/client/action/room.js

index 73b722e57c9efea896ec6df452e327347ed64c28..b5940b4d4a49f24b0d8147158337cb1ce79b745a 100644 (file)
@@ -7,10 +7,11 @@ import { twemojify } from '../../../util/twemojify';
 import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import navigation from '../../../client/state/navigation';
-import { selectRoom } from '../../../client/action/navigation';
+import { selectRoom, openReusableContextMenu } from '../../../client/action/navigation';
 import * as roomActions from '../../../client/action/room';
 
 import { getUsername, getUsernameOfRoomMember, getPowerLabel } from '../../../util/matrixUtil';
+import { getEventCords } from '../../../util/common';
 import colorMXID from '../../../util/colorMXID';
 
 import Text from '../../atoms/text/Text';
@@ -18,6 +19,7 @@ import Chip from '../../atoms/chip/Chip';
 import IconButton from '../../atoms/button/IconButton';
 import Avatar from '../../atoms/avatar/Avatar';
 import Button from '../../atoms/button/Button';
+import PowerLevelSelector from '../../molecules/power-level-selector/PowerLevelSelector';
 import Dialog from '../../molecules/dialog/Dialog';
 import SettingTile from '../../molecules/setting-tile/SettingTile';
 
@@ -25,6 +27,8 @@ import ShieldEmptyIC from '../../../../public/res/ic/outlined/shield-empty.svg';
 import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
 import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
 
+import { useForceUpdate } from '../../hooks/useForceUpdate';
+
 function SessionInfo({ userId }) {
   const [devices, setDevices] = useState(null);
   const mx = initMatrix.matrixClient;
@@ -230,6 +234,7 @@ function ProfileViewer() {
   const [isOpen, setIsOpen] = useState(false);
   const [roomId, setRoomId] = useState(null);
   const [userId, setUserId] = useState(null);
+  const [, forceUpdate] = useForceUpdate();
 
   const mx = initMatrix.matrixClient;
   const room = roomId ? mx.getRoom(roomId) : null;
@@ -240,19 +245,30 @@ function ProfileViewer() {
     else username = getUsername(userId);
   }
 
-  function loadProfile(uId, rId) {
-    setIsOpen(true);
-    setUserId(uId);
-    setRoomId(rId);
-  }
-
   useEffect(() => {
+    const loadProfile = (uId, rId) => {
+      setIsOpen(true);
+      setUserId(uId);
+      setRoomId(rId);
+    };
     navigation.on(cons.events.navigation.PROFILE_VIEWER_OPENED, loadProfile);
     return () => {
       navigation.removeListener(cons.events.navigation.PROFILE_VIEWER_OPENED, loadProfile);
     };
   }, []);
 
+  useEffect(() => {
+    const handlePowerLevelChange = (mEvent, member) => {
+      if (mEvent.getRoomId() === roomId && member.userId === userId) {
+        forceUpdate();
+      }
+    };
+    mx.on('RoomMember.powerLevel', handlePowerLevelChange);
+    return () => {
+      mx.removeListener('RoomMember.powerLevel', handlePowerLevelChange);
+    };
+  }, [roomId, userId]);
+
   const handleAfterClose = () => {
     setUserId(null);
     setRoomId(null);
@@ -261,8 +277,40 @@ function ProfileViewer() {
   function renderProfile() {
     const member = room.getMember(userId) || mx.getUser(userId) || {};
     const avatarMxc = member.getMxcAvatarUrl?.() || member.avatarUrl;
+
     const powerLevel = member.powerLevel || 0;
-    const canChangeRole = room.currentState.maySendEvent('m.room.power_levels', mx.getUserId());
+    const myPowerLevel = room.getMember(mx.getUserId())?.powerLevel || 0;
+
+    const canChangeRole = (
+      room.currentState.maySendEvent('m.room.power_levels', mx.getUserId())
+      && (powerLevel < myPowerLevel || userId === mx.getUserId())
+    );
+
+    const handleChangePowerLevel = (newPowerLevel) => {
+      if (newPowerLevel === powerLevel) return;
+      if (newPowerLevel === myPowerLevel
+        ? confirm('You will not be able to undo this change as you are promoting the user to have the same power level as yourself. Are you sure?')
+        : true
+      ) {
+        roomActions.setPowerLevel(roomId, userId, newPowerLevel);
+      }
+    };
+    const handlePowerSelector = (e) => {
+      openReusableContextMenu(
+        'bottom',
+        getEventCords(e, '.btn-surface'),
+        (closeMenu) => (
+          <PowerLevelSelector
+            value={powerLevel}
+            max={myPowerLevel}
+            onSelect={(pl) => {
+              closeMenu();
+              handleChangePowerLevel(pl);
+            }}
+          />
+        ),
+      );
+    };
 
     return (
       <div className="profile-viewer">
@@ -279,7 +327,10 @@ function ProfileViewer() {
           </div>
           <div className="profile-viewer__user__role">
             <Text variant="b3">Role</Text>
-            <Button iconSrc={canChangeRole ? ChevronBottomIC : null}>
+            <Button
+              onClick={canChangeRole ? handlePowerSelector : null}
+              iconSrc={canChangeRole ? ChevronBottomIC : null}
+            >
               {`${getPowerLabel(powerLevel) || 'Member'} - ${powerLevel}`}
             </Button>
           </div>
index 837724fb4b8d8381c2f1fba13d8ca8e967553a71..3ec7f8dd783bf62875c548e1e2893686e4ff0ef5 100644 (file)
@@ -4,6 +4,7 @@ import './Client.scss';
 import Text from '../../atoms/text/Text';
 import Spinner from '../../atoms/spinner/Spinner';
 import Navigation from '../../organisms/navigation/Navigation';
+import ReusableContextMenu from '../../atoms/context-menu/ReusableContextMenu';
 import Room from '../../organisms/room/Room';
 import Windows from '../../organisms/pw/Windows';
 import Dialogs from '../../organisms/pw/Dialogs';
@@ -66,6 +67,7 @@ function Client() {
       <Dialogs />
       <EmojiBoardOpener />
       <RoomOptions />
+      <ReusableContextMenu />
     </div>
   );
 }
index 5fe5faacbcee79425a1bdec5a61801f865c2acc9..73783ef0c677be2639e1e6ece7ca874c39cda783 100644 (file)
@@ -199,6 +199,16 @@ async function kick(roomId, userId) {
   return result;
 }
 
+async function setPowerLevel(roomId, userId, powerLevel) {
+  const mx = initMatrix.matrixClient;
+  const room = mx.getRoom(roomId);
+
+  const powerlevelEvent = room.currentState.getStateEvents('m.room.power_levels')[0];
+
+  const result = await mx.setPowerLevel(roomId, userId, powerLevel, powerlevelEvent);
+  return result;
+}
+
 function createSpaceShortcut(roomId) {
   appDispatcher.dispatch({
     type: cons.actions.room.CREATE_SPACE_SHORTCUT,
@@ -216,5 +226,6 @@ function deleteSpaceShortcut(roomId) {
 export {
   join, leave,
   create, invite, kick,
+  setPowerLevel,
   createSpaceShortcut, deleteSpaceShortcut,
 };