Redesign user profile in settings

Signed-off-by: Ajay Bura <ajbura@gmail.com>
This commit is contained in:
Ajay Bura 2022-03-20 21:59:54 +05:30
parent 4265b97419
commit ea98418e50
5 changed files with 90 additions and 46 deletions

View file

@ -72,7 +72,7 @@ function ProfileAvatarMenu() {
return ( return (
<SidebarAvatar <SidebarAvatar
onClick={openSettings} onClick={openSettings}
tooltip={profile.displayName} tooltip="User settings"
avatar={( avatar={(
<Avatar <Avatar
text={profile.displayName} text={profile.displayName}

View file

@ -1,35 +1,43 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import colorMXID from '../../../util/colorMXID'; import colorMXID from '../../../util/colorMXID';
import Text from '../../atoms/text/Text';
import IconButton from '../../atoms/button/IconButton';
import Button from '../../atoms/button/Button'; import Button from '../../atoms/button/Button';
import ImageUpload from '../../molecules/image-upload/ImageUpload'; import ImageUpload from '../../molecules/image-upload/ImageUpload';
import Input from '../../atoms/input/Input'; import Input from '../../atoms/input/Input';
import PencilIC from '../../../../public/res/ic/outlined/pencil.svg';
import './ProfileEditor.scss'; import './ProfileEditor.scss';
// TODO Fix bug that prevents 'Save' button from enabling up until second changed. // TODO Fix bug that prevents 'Save' button from enabling up until second changed.
function ProfileEditor({ function ProfileEditor({ userId }) {
userId, const [isEditing, setIsEditing] = useState(false);
}) {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
const user = mx.getUser(mx.getUserId());
const displayNameRef = useRef(null); const displayNameRef = useRef(null);
const bgColor = colorMXID(userId); const [avatarSrc, setAvatarSrc] = useState(user.avatarUrl ? mx.mxcUrlToHttp(user.avatarUrl, 80, 80, 'crop') : null);
const [avatarSrc, setAvatarSrc] = useState(null); const [username, setUsername] = useState(user.displayName);
const [disabled, setDisabled] = useState(true); const [disabled, setDisabled] = useState(true);
let username = mx.getUser(mx.getUserId()).displayName;
useEffect(() => { useEffect(() => {
let isMounted = true;
mx.getProfileInfo(mx.getUserId()).then((info) => { mx.getProfileInfo(mx.getUserId()).then((info) => {
if (!isMounted) return;
setAvatarSrc(info.avatar_url ? mx.mxcUrlToHttp(info.avatar_url, 80, 80, 'crop') : null); setAvatarSrc(info.avatar_url ? mx.mxcUrlToHttp(info.avatar_url, 80, 80, 'crop') : null);
}); });
return () => {
isMounted = false;
};
}, [userId]); }, [userId]);
// Sets avatar URL and updates the avatar component in profile editor to reflect new upload const handleAvatarUpload = (url) => {
function handleAvatarUpload(url) {
if (url === null) { if (url === null) {
if (confirm('Are you sure you want to remove avatar?')) { if (confirm('Are you sure you want to remove avatar?')) {
mx.setAvatarUrl(''); mx.setAvatarUrl('');
@ -39,48 +47,72 @@ function ProfileEditor({
} }
mx.setAvatarUrl(url); mx.setAvatarUrl(url);
setAvatarSrc(mx.mxcUrlToHttp(url, 80, 80, 'crop')); setAvatarSrc(mx.mxcUrlToHttp(url, 80, 80, 'crop'));
} };
function saveDisplayName() { const saveDisplayName = () => {
const newDisplayName = displayNameRef.current.value; const newDisplayName = displayNameRef.current.value;
if (newDisplayName !== null && newDisplayName !== username) { if (newDisplayName !== null && newDisplayName !== username) {
mx.setDisplayName(newDisplayName); mx.setDisplayName(newDisplayName);
username = newDisplayName; setUsername(newDisplayName);
setDisabled(true); setDisabled(true);
setIsEditing(false);
} }
} };
function onDisplayNameInputChange() { const onDisplayNameInputChange = () => {
setDisabled(username === displayNameRef.current.value || displayNameRef.current.value == null); setDisabled(username === displayNameRef.current.value || displayNameRef.current.value == null);
} };
function cancelDisplayNameChanges() { const cancelDisplayNameChanges = () => {
displayNameRef.current.value = username; displayNameRef.current.value = username;
onDisplayNameInputChange(); onDisplayNameInputChange();
} setIsEditing(false);
};
return ( const renderForm = () => (
<form <form
className="profile-editor" className="profile-editor__form"
style={{ marginBottom: avatarSrc ? '24px' : '0' }}
onSubmit={(e) => { e.preventDefault(); saveDisplayName(); }} onSubmit={(e) => { e.preventDefault(); saveDisplayName(); }}
> >
<Input
label={`Display name of ${mx.getUserId()}`}
onChange={onDisplayNameInputChange}
value={mx.getUser(mx.getUserId()).displayName}
forwardRef={displayNameRef}
/>
<Button variant="primary" type="submit" disabled={disabled}>Save</Button>
<Button onClick={cancelDisplayNameChanges}>Cancel</Button>
</form>
);
const renderInfo = () => (
<div className="profile-editor__info" style={{ marginBottom: avatarSrc ? '24px' : '0' }}>
<div>
<Text variant="h2" primary weight="medium">{twemojify(username)}</Text>
<IconButton
src={PencilIC}
size="extra-small"
tooltip="Edit"
onClick={() => setIsEditing(true)}
/>
</div>
<Text variant="b2">{mx.getUserId()}</Text>
</div>
);
return (
<div className="profile-editor">
<ImageUpload <ImageUpload
text={username} text={username}
bgColor={bgColor} bgColor={colorMXID(userId)}
imageSrc={avatarSrc} imageSrc={avatarSrc}
onUpload={handleAvatarUpload} onUpload={handleAvatarUpload}
onRequestRemove={() => handleAvatarUpload(null)} onRequestRemove={() => handleAvatarUpload(null)}
/> />
<div className="profile-editor__input-wrapper"> {
<Input isEditing ? renderForm() : renderInfo()
label={`Display name of ${mx.getUserId()}`} }
onChange={onDisplayNameInputChange} </div>
value={mx.getUser(mx.getUserId()).displayName}
forwardRef={displayNameRef}
/>
<Button variant="primary" type="submit" disabled={disabled}>Save</Button>
<Button onClick={cancelDisplayNameChanges}>Cancel</Button>
</div>
</form>
); );
} }

View file

@ -1,28 +1,41 @@
@use '../../partials/dir'; @use '../../partials/dir';
@use '../../partials/flex';
.profile-editor { .profile-editor {
display: flex; display: flex;
align-items: flex-start; align-items: flex-end;
} }
.profile-editor__input-wrapper { .profile-editor__info,
flex: 1; .profile-editor__form {
min-width: 0; @extend .cp-fx__item-one;
margin-top: 10px; @include dir.side(margin, var(--sp-loose), 0);
display: flex; display: flex;
align-items: flex-end; }
.profile-editor__info {
flex-direction: column;
& > div:first-child {
display: flex;
align-items: center;
}
.ic-btn {
margin: 0 var(--sp-extra-tight);
}
}
.profile-editor__form {
margin-top: 10px;
flex-wrap: wrap; flex-wrap: wrap;
align-items: flex-end;
& > .input-container { & > .input-container {
flex: 1; @extend .cp-fx__item-one;
} }
& > button { & > button {
height: 46px; height: 46px;
margin-top: var(--sp-normal); margin-top: var(--sp-normal);
}
& > * {
@include dir.side(margin, var(--sp-normal), 0); @include dir.side(margin, var(--sp-normal), 0);
} }
} }

View file

@ -306,7 +306,7 @@ function Settings() {
<PopupWindow <PopupWindow
isOpen={isOpen} isOpen={isOpen}
className="settings-window" className="settings-window"
title={<Text variant="s1" weight="medium" primary>Settings</Text>} title={<Text variant="s1" weight="medium" primary>User settings</Text>}
contentOptions={( contentOptions={(
<> <>
<Button variant="danger" iconSrc={PowerIC} onClick={handleLogout}> <Button variant="danger" iconSrc={PowerIC} onClick={handleLogout}>

View file

@ -8,8 +8,7 @@
.header .btn-danger { .header .btn-danger {
margin: 0 var(--sp-tight); margin: 0 var(--sp-tight);
padding-top: var(--sp-ultra-tight); box-shadow: none;
padding-bottom: var(--sp-ultra-tight);
} }
& .profile-editor { & .profile-editor {