diff --git a/public/res/svg/avatar-clip.svg b/public/res/svg/avatar-clip.svg new file mode 100644 index 00000000..ffaa1a2f --- /dev/null +++ b/public/res/svg/avatar-clip.svg @@ -0,0 +1,3 @@ + diff --git a/src/app/atoms/image-upload/ImageUpload.jsx b/src/app/atoms/image-upload/ImageUpload.jsx new file mode 100644 index 00000000..13930527 --- /dev/null +++ b/src/app/atoms/image-upload/ImageUpload.jsx @@ -0,0 +1,62 @@ +import React, { useRef } from 'react'; +import PropTypes from 'prop-types'; + +import initMatrix from '../../../client/initMatrix'; + +import GenIC from '../../../../public/res/ic/outlined/settings.svg'; +import Avatar from '../avatar/Avatar'; + +import RawIcon from '../system-icons/RawIcon'; +import './ImageUpload.scss'; + +function ImageUpload({ + text, bgColor, imageSrc, onUpload, +}) { + const uploadImageRef = useRef(null); + + function uploadImage(e) { + const file = e.target.files.item(0); + if (file !== null) { // TODO Add upload progress spinner + initMatrix.matrixClient.uploadContent(file, { onlyContentUri: false }).then((res) => { + if (res.content_uri !== null) { + onUpload({ content_uri: res.content_uri }); + } + }, (err) => { + console.log(err); // TODO Replace with alert banner. + }); + } + } + + return ( + + ); +} + +ImageUpload.defaultProps = { + text: null, + bgColor: 'transparent', + imageSrc: null, + onUpload: null, +}; + +ImageUpload.propTypes = { + text: PropTypes.string, + bgColor: PropTypes.string, + imageSrc: PropTypes.string, + onUpload: PropTypes.func, +}; + +export default ImageUpload; diff --git a/src/app/atoms/image-upload/ImageUpload.scss b/src/app/atoms/image-upload/ImageUpload.scss new file mode 100644 index 00000000..c7118ba8 --- /dev/null +++ b/src/app/atoms/image-upload/ImageUpload.scss @@ -0,0 +1,20 @@ +.img-upload-container { + display: flex; + flex-direction: row-reverse; + width: 80px; + height: 80px; +} + +.img-upload-container:hover { + cursor: pointer; +} + +.img-upload-mask { + mask: url('../../../../public/res/svg/avatar-clip.svg'); + //width: 80px; +} + +.img-upload-icon { + z-index: 1; + position: absolute; +} \ No newline at end of file diff --git a/src/app/molecules/profile-editor/ProfileEditor.jsx b/src/app/molecules/profile-editor/ProfileEditor.jsx new file mode 100644 index 00000000..818ed935 --- /dev/null +++ b/src/app/molecules/profile-editor/ProfileEditor.jsx @@ -0,0 +1,66 @@ +import React, { useState, useRef } from 'react'; +import PropTypes from 'prop-types'; + +import initMatrix from '../../../client/initMatrix'; +import colorMXID from '../../../util/colorMXID'; + +import Button from '../../atoms/button/Button'; +import ImageUpload from '../../atoms/image-upload/ImageUpload'; +import Input from '../../atoms/input/Input'; +import Text from '../../atoms/text/Text'; + +import './ProfileEditor.scss'; + +// TODO Fix bug that prevents 'Save' button from enabling up until second changed. +function ProfileEditor({ + userId, +}) { + const mx = initMatrix.matrixClient; + const displayNameRef = useRef(null); + const bgColor = colorMXID(userId); + const [imageSrc, updateImgSrc] = useState(mx.mxcUrlToHttp(mx.getUser(mx.getUserId()).avatarUrl)); + const [disabled, setDisabled] = useState(true); + + let username = mx.getUser(mx.getUserId()).displayName; + + function handleUpload(e) { + mx.setAvatarUrl(e.content_uri); + updateImgSrc(mx.mxcUrlToHttp(e.content_uri)); + } + + function saveDisplayName() { + if (displayNameRef.current.value !== null && displayNameRef.current.value !== '') { + mx.setDisplayName(displayNameRef.current.value); + username = displayNameRef.current.value; + setDisabled(true); + } + } + + function onDisplayNameInputChange() { + setDisabled((username === displayNameRef.current.value) || displayNameRef.current.value === '' || displayNameRef.current.value == null); + } + + return ( +
+ ); +} + +ProfileEditor.defaultProps = { + userId: null, +}; + +ProfileEditor.propTypes = { + userId: PropTypes.string, +}; + +export default ProfileEditor; diff --git a/src/app/molecules/profile-editor/ProfileEditor.scss b/src/app/molecules/profile-editor/ProfileEditor.scss new file mode 100644 index 00000000..98a453aa --- /dev/null +++ b/src/app/molecules/profile-editor/ProfileEditor.scss @@ -0,0 +1,24 @@ +.profile-editor { + display: flex; + align-items: end; +} + +.img-upload-container { + margin-right: var(--sp-normal) +} + +.display-name-input-container { + display: flex; + flex-direction: column; + margin-right: var(--sp-normal); + width: 100%; + max-width: 400px; +} + +.display-name-input-container > .text-b3 { + margin-bottom: var(--sp-ultra-tight) +} + +.profile-editor > .btn-primary { + height: 46px; +} \ No newline at end of file diff --git a/src/app/organisms/settings/Settings.jsx b/src/app/organisms/settings/Settings.jsx index 8914640d..91be164f 100644 --- a/src/app/organisms/settings/Settings.jsx +++ b/src/app/organisms/settings/Settings.jsx @@ -14,8 +14,10 @@ import SegmentedControls from '../../atoms/segmented-controls/SegmentedControls' import PopupWindow, { PWContentSelector } from '../../molecules/popup-window/PopupWindow'; import SettingTile from '../../molecules/setting-tile/SettingTile'; +import ProfileEditor from '../../molecules/profile-editor/ProfileEditor'; import ImportE2ERoomKeys from '../../molecules/import-e2e-room-keys/ImportE2ERoomKeys'; +import GenIC from '../../../../public/res/ic/outlined/settings.svg'; import SunIC from '../../../../public/res/ic/outlined/sun.svg'; import LockIC from '../../../../public/res/ic/outlined/lock.svg'; import InfoIC from '../../../../public/res/ic/outlined/info.svg'; @@ -23,6 +25,19 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import CinnySVG from '../../../../public/res/svg/cinny.svg'; +function GeneralSection() { + return ( +