Add room profile comp
Signed-off-by: Ajay Bura <ajbura@gmail.com>
This commit is contained in:
parent
23c430fadc
commit
8eda0aeab3
2 changed files with 232 additions and 0 deletions
179
src/app/molecules/room-profile/RoomProfile.jsx
Normal file
179
src/app/molecules/room-profile/RoomProfile.jsx
Normal file
|
@ -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;
|
53
src/app/molecules/room-profile/RoomProfile.scss
Normal file
53
src/app/molecules/room-profile/RoomProfile.scss
Normal file
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue