diff --git a/src/app/molecules/room-profile/RoomProfile.jsx b/src/app/molecules/room-profile/RoomProfile.jsx new file mode 100644 index 0000000..9012bf4 --- /dev/null +++ b/src/app/molecules/room-profile/RoomProfile.jsx @@ -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 = () => ( +
+ {canChangeName && } + {canChangeTopic && } + {(!canChangeName || !canChangeTopic) && {`You have permission to change room ${canChangeName ? 'name' : 'topic'} only.`}} + { status.type === cons.status.IN_FLIGHT && {status.msg}} + { status.type === cons.status.SUCCESS && {status.msg}} + { status.type === cons.status.ERROR && {status.msg}} + { status.type !== cons.status.IN_FLIGHT && ( +
+ + +
+ )} +
+ ); + + const renderNameAndTopic = () => ( +
+
+ {twemojify(roomName)} + { (canChangeName || canChangeTopic) && ( + setIsEditing(true)} + /> + )} +
+ {roomTopic && {twemojify(roomTopic, undefined, true)}} +
+ ); + + return ( +
+
+ { !canChangeAvatar && } + { canChangeAvatar && ( + handleAvatarUpload(null)} + /> + )} + {!isEditing && renderNameAndTopic()} + {isEditing && renderEditNameAndTopic()} +
+
+ ); +} + +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 index 0000000..e6b12db --- /dev/null +++ b/src/app/molecules/room-profile/RoomProfile.scss @@ -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