Add optoins to change room visibility
authorAjay Bura <ajbura@gmail.com>
Sun, 26 Dec 2021 10:04:20 +0000 (15:34 +0530)
committerAjay Bura <ajbura@gmail.com>
Sun, 26 Dec 2021 10:04:20 +0000 (15:34 +0530)
Signed-off-by: Ajay Bura <ajbura@gmail.com>
src/app/atoms/avatar/Avatar.jsx
src/app/molecules/room-notification/RoomNotification.jsx
src/app/molecules/room-visibility/RoomVisibility.jsx [new file with mode: 0644]
src/app/molecules/room-visibility/RoomVisibility.scss [new file with mode: 0644]
src/app/organisms/room/RoomSettings.jsx
src/client/state/navigation.js

index 3a2206c2a46333aaea3e44c8c08555a09beec3e1..de1b3fabf2e8408d89912cea067d32baf19fc57d 100644 (file)
@@ -16,7 +16,10 @@ function Avatar({
   if (size === 'small') textSize = 'b1';
   if (size === 'extra-small') textSize = 'b3';
 
-  useEffect(() => updateImage(imageSrc), [imageSrc]);
+  useEffect(() => {
+    updateImage(imageSrc);
+    return () => updateImage(null);
+  }, [imageSrc]);
 
   return (
     <div className={`avatar-container avatar-container__${size} noselect`}>
index 64e4eeec3212bfbc4c0f9105857e665406d1644f..5d1186329b3bccea1f287e4959539b9505dc48da 100644 (file)
@@ -129,9 +129,6 @@ function useNotifications(roomId) {
 function RoomNotification({ roomId }) {
   const [activeType, setNotification] = useNotifications(roomId);
 
-  console.log(roomId)
-  console.log(activeType)
-  
   return (
     <div className="room-notification">
       {
diff --git a/src/app/molecules/room-visibility/RoomVisibility.jsx b/src/app/molecules/room-visibility/RoomVisibility.jsx
new file mode 100644 (file)
index 0000000..746a07a
--- /dev/null
@@ -0,0 +1,121 @@
+import React, { useState, useEffect, useCallback } from 'react';
+import PropTypes from 'prop-types';
+import './RoomVisibility.scss';
+
+import initMatrix from '../../../client/initMatrix';
+
+import Text from '../../atoms/text/Text';
+import RadioButton from '../../atoms/button/RadioButton';
+import { MenuItem } from '../../atoms/context-menu/ContextMenu';
+
+import HashIC from '../../../../public/res/ic/outlined/hash.svg';
+import HashLockIC from '../../../../public/res/ic/outlined/hash-lock.svg';
+import HashGlobeIC from '../../../../public/res/ic/outlined/hash-globe.svg';
+import SpaceIC from '../../../../public/res/ic/outlined/space.svg';
+import SpaceLockIC from '../../../../public/res/ic/outlined/space-lock.svg';
+import SpaceGlobeIC from '../../../../public/res/ic/outlined/space-globe.svg';
+
+const visibility = {
+  INVITE: 'invite',
+  RESTRICTED: 'restricted',
+  PUBLIC: 'public',
+};
+
+function setJoinRule(roomId, type) {
+  const mx = initMatrix.matrixClient;
+  let allow;
+  if (type === visibility.RESTRICTED) {
+    const { currentState } = mx.getRoom(roomId);
+    const mEvent = currentState.getStateEvents('m.space.parent')[0];
+    if (!mEvent) return Promise.resolve(undefined);
+
+    allow = [{
+      room_id: mEvent.getStateKey(),
+      type: 'm.room_membership',
+    }];
+  }
+
+  return mx.sendStateEvent(
+    roomId,
+    'm.room.join_rules',
+    {
+      join_rule: type,
+      allow,
+    },
+  );
+}
+
+function useVisibility(roomId) {
+  const mx = initMatrix.matrixClient;
+  const room = mx.getRoom(roomId);
+
+  const [activeType, setActiveType] = useState(room.getJoinRule());
+  useEffect(() => setActiveType(room.getJoinRule()), [roomId]);
+
+  const setNotification = useCallback((item) => {
+    if (item.type === activeType.type) return;
+    setActiveType(item.type);
+    setJoinRule(roomId, item.type);
+  }, [activeType, roomId]);
+
+  return [activeType, setNotification];
+}
+
+function RoomVisibility({ roomId }) {
+  const [activeType, setVisibility] = useVisibility(roomId);
+  const mx = initMatrix.matrixClient;
+  const room = mx.getRoom(roomId);
+  const isSpace = room.isSpaceRoom();
+  const { currentState } = room;
+
+  const noSpaceParent = currentState.getStateEvents('m.space.parent').length === 0;
+  const mCreate = currentState.getStateEvents('m.room.create')[0]?.getContent();
+  const roomVersion = Number(mCreate.room_version);
+
+  const myPowerlevel = room.getMember(mx.getUserId())?.powerLevel || 0;
+  const canChange = room.currentState.hasSufficientPowerLevelFor('state_default', myPowerlevel);
+
+  const items = [{
+    iconSrc: isSpace ? SpaceLockIC : HashLockIC,
+    text: 'Private (invite only)',
+    type: visibility.INVITE,
+    unsupported: false,
+  }, {
+    iconSrc: isSpace ? SpaceIC : HashIC,
+    text: roomVersion < 8 ? 'Restricted (unsupported: required room upgrade)' : 'Restricted (space member can join)',
+    type: visibility.RESTRICTED,
+    unsupported: roomVersion < 8 || noSpaceParent,
+  }, {
+    iconSrc: isSpace ? SpaceGlobeIC : HashGlobeIC,
+    text: 'Public (anyone can join)',
+    type: visibility.PUBLIC,
+    unsupported: false,
+  }];
+
+  return (
+    <div className="room-visibility">
+      {
+        items.map((item) => (
+          <MenuItem
+            variant={activeType === item.type ? 'positive' : 'surface'}
+            key={item.type}
+            iconSrc={item.iconSrc}
+            onClick={() => setVisibility(item)}
+            disabled={(!canChange || item.unsupported)}
+          >
+            <Text varient="b1">
+              <span>{item.text}</span>
+              <RadioButton isActive={activeType === item.type} />
+            </Text>
+          </MenuItem>
+        ))
+      }
+    </div>
+  );
+}
+
+RoomVisibility.propTypes = {
+  roomId: PropTypes.string.isRequired,
+};
+
+export default RoomVisibility;
diff --git a/src/app/molecules/room-visibility/RoomVisibility.scss b/src/app/molecules/room-visibility/RoomVisibility.scss
new file mode 100644 (file)
index 0000000..b3ad966
--- /dev/null
@@ -0,0 +1,19 @@
+@use '../../partials/flex';
+@use '../../partials/dir';
+@use '../../partials/text';
+
+.room-visibility {
+  & .context-menu__item .text {
+    @extend .cp-fx__item-one;
+    @extend .cp-fx__row--s-c;
+    
+    & span:first-child {
+      @extend .cp-fx__item-one;
+      @extend .cp-txt__ellipsis;
+    }
+    
+    & .radio-btn {
+      @include dir.side(margin, var(--sp-tight), 0);
+    }
+  }
+}
\ No newline at end of file
index ec3f7f5dd707ab4e80d8616614b5d6838026f1fa..0504f474a7463a4c0a8a43e68bd0d9d623c2a60a 100644 (file)
@@ -15,6 +15,7 @@ import Tabs from '../../atoms/tabs/Tabs';
 import { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu';
 import RoomProfile from '../../molecules/room-profile/RoomProfile';
 import RoomNotification from '../../molecules/room-notification/RoomNotification';
+import RoomVisibility from '../../molecules/room-visibility/RoomVisibility';
 
 import SettingsIC from '../../../../public/res/ic/outlined/settings.svg';
 import SearchIC from '../../../../public/res/ic/outlined/search.svg';
@@ -56,7 +57,7 @@ function GeneralSettings({ roomId }) {
   return (
     <>
       <div className="room-settings__card">
-        <MenuHeader>Notification</MenuHeader>
+        <MenuHeader>Notification (Changing this will only affect you)</MenuHeader>
         <RoomNotification roomId={roomId} />
       </div>
       <div className="room-settings__card">
@@ -69,6 +70,10 @@ function GeneralSettings({ roomId }) {
         </MenuItem>
         <MenuItem variant="danger" onClick={() => roomActions.leave(roomId)} iconSrc={LeaveArrowIC}>Leave</MenuItem>
       </div>
+      <div className="room-settings__card">
+        <MenuHeader>Visibility (who can join)</MenuHeader>
+        <RoomVisibility roomId={roomId} />
+      </div>
     </>
   );
 }
@@ -111,7 +116,11 @@ function RoomSettings({ roomId }) {
             </TitleWrapper>
           </Header>
           <RoomProfile roomId={roomId} />
-          <Tabs items={tabItems} onSelect={handleTabChange} />
+          <Tabs
+            items={tabItems}
+            defaultSelected={tabItems.findIndex((tab) => tab.text === selectedTab.text)}
+            onSelect={handleTabChange}
+          />
           <div className="room-settings__cards-wrapper">
             {selectedTab.text === tabItems[0].text && <GeneralSettings roomId={roomId} />}
           </div>
index 8ebf4040bd812b44dac56cf3449a14e324605713..fb8873ef5f1d6b4a6430e1f2c2202ec47195c153 100644 (file)
@@ -78,7 +78,7 @@ class Navigation extends EventEmitter {
         this.removeRecentRoom(prevSelectedRoomId);
         this.addRecentRoom(prevSelectedRoomId);
         this.removeRecentRoom(this.selectedRoomId);
-        if (this.isRoomSettings) {
+        if (this.isRoomSettings && typeof this.selectedRoomId === 'string') {
           this.isRoomSettings = !this.isRoomSettings;
           this.emit(cons.events.navigation.ROOM_SETTINGS_TOGGLED, this.isRoomSettings);
         }