Add room profile comp
authorAjay Bura <ajbura@gmail.com>
Wed, 22 Dec 2021 14:47:01 +0000 (20:17 +0530)
committerAjay Bura <ajbura@gmail.com>
Wed, 22 Dec 2021 14:47:01 +0000 (20:17 +0530)
Signed-off-by: Ajay Bura <ajbura@gmail.com>
src/app/molecules/room-profile/RoomProfile.jsx [new file with mode: 0644]
src/app/molecules/room-profile/RoomProfile.scss [new file with mode: 0644]

diff --git a/src/app/molecules/room-profile/RoomProfile.jsx b/src/app/molecules/room-profile/RoomProfile.jsx
new file mode 100644 (file)
index 0000000..9012bf4
--- /dev/null
@@ -0,0 +1,179 @@
+import React, { useState, useEffect } from 'react';
+import PropTypes from 'prop-types';
+import './RoomProfile.scss';
+
+import { twemojify } from '../../../util/twemojify';
+
+import initMatrix from '../../../client/initMatrix';
+import cons from '../../../client/state/cons';
+import colorMXID from '../../../util/colorMXID';
+
+import Text from '../../atoms/text/Text';
+import Avatar from '../../atoms/avatar/Avatar';
+import Button from '../../atoms/button/Button';
+import Input from '../../atoms/input/Input';
+import IconButton from '../../atoms/button/IconButton';
+import ImageUpload from '../image-upload/ImageUpload';
+
+import PencilIC from '../../../../public/res/ic/outlined/pencil.svg';
+
+import { useStore } from '../../hooks/useStore';
+
+function RoomProfile({ roomId }) {
+  const isMountStore = useStore();
+  const [isEditing, setIsEditing] = useState(false);
+  const [status, setStatus] = useState({
+    msg: null,
+    type: cons.status.PRE_FLIGHT,
+  });
+
+  const mx = initMatrix.matrixClient;
+  const isDM = initMatrix.roomList.directs.has(roomId);
+  let avatarSrc = mx.getRoom(roomId).getAvatarUrl(mx.baseUrl, 36, 36, 'crop');
+  avatarSrc = isDM ? mx.getRoom(roomId).getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 36, 36, 'crop') : avatarSrc;
+  const room = mx.getRoom(roomId);
+  const { currentState } = room;
+  const roomName = room.name;
+  const roomTopic = currentState.getStateEvents('m.room.topic')[0]?.getContent().topic;
+
+  const userId = mx.getUserId();
+
+  const canChangeAvatar = currentState.maySendStateEvent('m.room.avatar', userId);
+  const canChangeName = currentState.maySendStateEvent('m.room.name', userId);
+  const canChangeTopic = currentState.maySendStateEvent('m.room.topic', userId);
+
+  useEffect(() => {
+    isMountStore.setItem(true);
+    return () => {
+      isMountStore.setItem(false);
+      setStatus({
+        msg: null,
+        type: cons.status.PRE_FLIGHT,
+      });
+      setIsEditing(false);
+    };
+  }, [roomId]);
+
+  const handleOnSubmit = async (e) => {
+    e.preventDefault();
+    const { target } = e;
+    const roomNameInput = target.elements['room-name'];
+    const roomTopicInput = target.elements['room-topic'];
+
+    try {
+      if (canChangeName) {
+        const newName = roomNameInput.value;
+        if (newName !== roomName && roomName.trim() !== '') {
+          setStatus({
+            msg: 'Saving room name...',
+            type: cons.status.IN_FLIGHT,
+          });
+          await mx.setRoomName(roomId, newName);
+        }
+      }
+      if (canChangeTopic) {
+        const newTopic = roomTopicInput.value;
+        if (newTopic !== roomTopic) {
+          if (isMountStore.getItem()) {
+            setStatus({
+              msg: 'Saving room topic...',
+              type: cons.status.IN_FLIGHT,
+            });
+          }
+          await mx.setRoomTopic(roomId, newTopic);
+        }
+      }
+      if (!isMountStore.getItem()) return;
+      setStatus({
+        msg: 'Saved successfully',
+        type: cons.status.SUCCESS,
+      });
+    } catch (err) {
+      if (!isMountStore.getItem()) return;
+      setStatus({
+        msg: err.message || 'Unable to save.',
+        type: cons.status.ERROR,
+      });
+    }
+  };
+
+  const handleCancelEditing = () => {
+    setStatus({
+      msg: null,
+      type: cons.status.PRE_FLIGHT,
+    });
+    setIsEditing(false);
+  };
+
+  const handleAvatarUpload = async (url) => {
+    if (url === null) {
+      if (confirm('Are you sure you want to remove avatar?')) {
+        await mx.sendStateEvent(roomId, 'm.room.avatar', { url }, '');
+      }
+    } else await mx.sendStateEvent(roomId, 'm.room.avatar', { url }, '');
+    if (!isMountStore.getItem()) return;
+    setStatus({
+      msg: null,
+      type: cons.status.PRE_FLIGHT,
+    });
+  };
+
+  const renderEditNameAndTopic = () => (
+    <form className="room-profile__edit-form" onSubmit={handleOnSubmit}>
+      {canChangeName && <Input value={roomName} name="room-name" disabled={status.type === cons.status.IN_FLIGHT} label="Room name" required />}
+      {canChangeTopic && <Input value={roomTopic} name="room-topic" disabled={status.type === cons.status.IN_FLIGHT} minHeight={100} resizable label="Topic" />}
+      {(!canChangeName || !canChangeTopic) && <Text variant="b3">{`You have permission to change room ${canChangeName ? 'name' : 'topic'} only.`}</Text>}
+      { status.type === cons.status.IN_FLIGHT && <Text variant="b2">{status.msg}</Text>}
+      { status.type === cons.status.SUCCESS && <Text style={{ color: 'var(--tc-positive-high)' }} variant="b2">{status.msg}</Text>}
+      { status.type === cons.status.ERROR && <Text style={{ color: 'var(--tc-danger-high)' }} variant="b2">{status.msg}</Text>}
+      { status.type !== cons.status.IN_FLIGHT && (
+        <div>
+          <Button type="submit" variant="primary">Save</Button>
+          <Button onClick={handleCancelEditing}>Cancel</Button>
+        </div>
+      )}
+    </form>
+  );
+
+  const renderNameAndTopic = () => (
+    <div className="room-profile__display" style={{ marginBottom: avatarSrc && canChangeAvatar ? '24px' : '0' }}>
+      <div>
+        <Text variant="h2" weight="medium" primary>{twemojify(roomName)}</Text>
+        { (canChangeName || canChangeTopic) && (
+          <IconButton
+            src={PencilIC}
+            size="extra-small"
+            tooltip="Edit room name and topic"
+            onClick={() => setIsEditing(true)}
+          />
+        )}
+      </div>
+      {roomTopic && <Text variant="b2">{twemojify(roomTopic, undefined, true)}</Text>}
+    </div>
+  );
+
+  return (
+    <div className="room-profile">
+      <div className="room-profile__content">
+        { !canChangeAvatar && <Avatar imageSrc={avatarSrc} text={roomName} bgColor={colorMXID(roomId)} size="large" />}
+        { canChangeAvatar && (
+          <ImageUpload
+            text={roomName}
+            bgColor={colorMXID(roomId)}
+            imageSrc={avatarSrc}
+            onUpload={handleAvatarUpload}
+            onRequestRemove={() => handleAvatarUpload(null)}
+          />
+        )}
+        {!isEditing && renderNameAndTopic()}
+        {isEditing && renderEditNameAndTopic()}
+      </div>
+    </div>
+  );
+}
+
+RoomProfile.propTypes = {
+  roomId: PropTypes.string.isRequired,
+};
+
+export default RoomProfile;
diff --git a/src/app/molecules/room-profile/RoomProfile.scss b/src/app/molecules/room-profile/RoomProfile.scss
new file mode 100644 (file)
index 0000000..e6b12db
--- /dev/null
@@ -0,0 +1,53 @@
+@use '../../partials/flex';
+@use '../../partials/dir';
+
+.room-profile {
+  
+  &__content {
+    @extend .cp-fx__row;
+    & .avatar-container {
+      min-width: var(--av-large);
+    }
+  }
+
+  &__display {
+    align-self: flex-end;
+    @include dir.side(margin, var(--sp-loose), 0);
+
+    & > div:first-child {
+      @extend .cp-fx__row--s-c;
+      & > .text {
+        @include dir.side(margin, 0, var(--sp-extra-tight));
+      }
+    }
+
+    & > *:last-child {
+      margin-top: var(--sp-ultra-tight);
+      white-space: pre-wrap;
+      word-break: break-word;
+    }
+  }
+  
+  &__edit-form {
+    @extend .cp-fx__item-one;
+    @include dir.side(margin, var(--sp-loose), 0);
+
+    & .input-container {
+      margin-bottom: var(--sp-extra-tight);
+    }
+
+    & > .text {
+      margin-bottom: var(--sp-tight);
+    }
+    
+    & > *:last-child {
+      @extend .cp-fx__item-one;
+      @extend .cp-fx__row;
+      margin-top: var(--sp-tight);
+      
+      .btn-primary {
+        @include dir.side(margin, 0, var(--sp-tight));
+      }
+    }
+  }
+}
\ No newline at end of file