Redesign user profile in settings
Signed-off-by: Ajay Bura <ajbura@gmail.com>
This commit is contained in:
parent
4265b97419
commit
ea98418e50
5 changed files with 90 additions and 46 deletions
|
@ -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}
|
||||||
|
|
|
@ -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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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}>
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue