-import React, { useRef } from 'react';
+import React, { useState, useRef } from 'react';
import PropTypes from 'prop-types';
+import './ImageUpload.scss';
import initMatrix from '../../../client/initMatrix';
-import SettingsIC from '../../../../public/res/ic/outlined/settings.svg';
+import Text from '../../atoms/text/Text';
import Avatar from '../../atoms/avatar/Avatar';
-
-import RawIcon from '../../atoms/system-icons/RawIcon';
-import './ImageUpload.scss';
+import Spinner from '../../atoms/spinner/Spinner';
function ImageUpload({
- text, bgColor, imageSrc, onUpload,
+ text, bgColor, imageSrc, onUpload, onRequestRemove,
}) {
+ const [uploadPromise, setUploadPromise] = useState(null);
const uploadImageRef = useRef(null);
- // Uploads image and passes resulting URI to onUpload function provided in component props.
- function uploadImage(e) {
+ async 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.
- });
+ if (file === null) return;
+ try {
+ const uPromise = initMatrix.matrixClient.uploadContent(file, { onlyContentUri: false });
+ setUploadPromise(uPromise);
+
+ const res = await uPromise;
+ if (typeof res?.content_uri === 'string') onUpload(res.content_uri);
+ setUploadPromise(null);
+ } catch {
+ setUploadPromise(null);
}
+ uploadImageRef.current.value = null;
+ }
+
+ function cancelUpload() {
+ initMatrix.matrixClient.cancelUpload(uploadPromise);
+ setUploadPromise(null);
+ uploadImageRef.current.value = null;
}
return (
- <button type="button" className="img-upload" onClick={() => { uploadImageRef.current.click(); }}>
- <div className="img-upload__mask">
+ <div className="img-upload__wrapper">
+ <button
+ type="button"
+ className="img-upload"
+ onClick={() => {
+ if (uploadPromise !== null) return;
+ uploadImageRef.current.click();
+ }}
+ >
<Avatar
imageSrc={imageSrc}
text={text.slice(0, 1)}
bgColor={bgColor}
size="large"
/>
- </div>
- <div className="img-upload__icon">
- <RawIcon size="small" src={SettingsIC} />
- </div>
+ <div className={`img-upload__process ${uploadPromise === null ? ' img-upload__process--stopped' : ''}`}>
+ {uploadPromise === null && <Text variant="b3">Upload</Text>}
+ {uploadPromise !== null && <Spinner size="small" />}
+ </div>
+ </button>
+ { (typeof imageSrc === 'string' || uploadPromise !== null) && (
+ <button
+ className="img-upload__btn-cancel"
+ type="button"
+ onClick={uploadPromise === null ? onRequestRemove : cancelUpload}
+ >
+ <Text variant="b3">{uploadPromise ? 'Cancel' : 'Remove'}</Text>
+ </button>
+ )}
<input onChange={uploadImage} style={{ display: 'none' }} ref={uploadImageRef} type="file" />
- </button>
+ </div>
);
}
text: null,
bgColor: 'transparent',
imageSrc: null,
- onUpload: null,
};
ImageUpload.propTypes = {
text: PropTypes.string,
bgColor: PropTypes.string,
imageSrc: PropTypes.string,
- onUpload: PropTypes.func,
+ onUpload: PropTypes.func.isRequired,
+ onRequestRemove: PropTypes.func.isRequired,
};
export default ImageUpload;
-.img-upload {
+.img-upload__wrapper {
display: flex;
- flex-direction: row-reverse;
- width: 80px;
- height: 80px;
+ flex-direction: column;
+ align-items: center;
}
-.img-upload:hover {
+.img-upload {
+ display: flex;
cursor: pointer;
-}
+ position: relative;
+
+ &__process {
+ width: 100%;
+ height: 100%;
+ border-radius: var(--bo-radius);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: rgba(0, 0, 0, .6);
+
+ position: absolute;
+ left: 0;
+ right: 0;
+ z-index: 1;
+ & .text {
+ text-transform: uppercase;
+ font-weight: 600;
+ color: white;
+ }
+ &--stopped {
+ display: none;
+ }
+ & .donut-spinner {
+ border-color: rgb(255, 255, 255, .3);
+ border-left-color: white;
+ }
+ }
+ &:hover .img-upload__process--stopped {
+ display: flex;
+ }
-.img-upload__mask {
- mask: url('../../../../public/res/svg/avatar-clip.svg');
- -webkit-mask: url('../../../../public/res/svg/avatar-clip.svg');
-}
-.img-upload__icon {
- z-index: 1;
- position: absolute;
-}
\ No newline at end of file
+ &__btn-cancel {
+ margin-top: var(--sp-extra-tight);
+ cursor: pointer;
+ & .text {
+ color: var(--tc-danger-normal)
+ }
+ }
+}
const mx = initMatrix.matrixClient;
const displayNameRef = useRef(null);
const bgColor = colorMXID(userId);
- const [imageSrc, updateImgSrc] = useState(mx.mxcUrlToHttp(mx.getUser(mx.getUserId()).avatarUrl));
+ const [avatarSrc, setAvatarSrc] = useState(mx.mxcUrlToHttp(mx.getUser(mx.getUserId()).avatarUrl, 80, 80, 'crop') || null);
const [disabled, setDisabled] = useState(true);
let username = mx.getUser(mx.getUserId()).displayName;
// Sets avatar URL and updates the avatar component in profile editor to reflect new upload
- function handleUpload(e) {
- mx.setAvatarUrl(e.content_uri);
- updateImgSrc(mx.mxcUrlToHttp(e.content_uri));
+ function handleAvatarUpload(url) {
+ if (url === null) {
+ if (confirm('Are you sure you want to remove avatar?')) {
+ mx.setAvatarUrl('');
+ setAvatarSrc(null);
+ }
+ return;
+ }
+ mx.setAvatarUrl(url);
+ setAvatarSrc(mx.mxcUrlToHttp(url, 80, 80, 'crop'));
}
function saveDisplayName() {
return (
<form className="profile-editor">
- <ImageUpload text={username} bgColor={bgColor} imageSrc={imageSrc} onUpload={handleUpload} />
+ <ImageUpload
+ text={username}
+ bgColor={bgColor}
+ imageSrc={avatarSrc}
+ onUpload={handleAvatarUpload}
+ onRequestRemove={() => handleAvatarUpload(null)}
+ />
<div className="profile-editor__input-container">
<Text variant="b3">
Display name of