Update display of avatar upload menu (#50)
* Update display of avatar upload menu * Add i18n * Incorporate feedback * Add mobile style fix * Fix loading style * Use ===
This commit is contained in:
@@ -101,6 +101,7 @@ export class ProfilePage extends React.Component {
|
||||
onSave={this.handleSaveProfilePhoto}
|
||||
onDelete={this.handleDeleteProfilePhoto}
|
||||
savePhotoState={this.props.savePhotoState}
|
||||
isEditable={this.props.isCurrentUserProfile}
|
||||
/>
|
||||
<div>
|
||||
<h2 className="mb-0">{username}</h2>
|
||||
@@ -201,7 +202,8 @@ ProfilePage.defaultProps = {
|
||||
const mapStateToProps = (state) => {
|
||||
const profileImage =
|
||||
state.profilePage.account.profileImage != null
|
||||
? state.profilePage.account.profileImage.imageUrlLarge
|
||||
// TODO: This will change back to camelcase in the future
|
||||
? state.profilePage.account.profileImage.image_url_large
|
||||
: null;
|
||||
return {
|
||||
isCurrentUserProfile: state.userAccount.username === state.profilePage.account.username,
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Input, Spinner } from 'reactstrap';
|
||||
import { Input, Spinner, Dropdown, DropdownToggle, DropdownMenu, DropdownItem, Button } from 'reactstrap';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
|
||||
class ProfileAvatar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
dropdownOpen: false,
|
||||
};
|
||||
|
||||
this.fileInput = React.createRef();
|
||||
this.form = React.createRef();
|
||||
|
||||
this.onClick = this.onClick.bind(this);
|
||||
this.onClickUpload = this.onClickUpload.bind(this);
|
||||
this.onClickDelete = this.onClickDelete.bind(this);
|
||||
this.onInput = this.onInput.bind(this);
|
||||
this.onSubmit = this.onSubmit.bind(this);
|
||||
this.toggleDropdown = this.toggleDropdown.bind(this);
|
||||
}
|
||||
|
||||
onClick() {
|
||||
onClickUpload() {
|
||||
this.fileInput.current.click();
|
||||
}
|
||||
|
||||
@@ -32,58 +39,104 @@ class ProfileAvatar extends React.Component {
|
||||
this.props.onSave(new FormData(this.form.current));
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
src,
|
||||
} = this.props;
|
||||
toggleDropdown() {
|
||||
this.setState({
|
||||
dropdownOpen: !this.state.dropdownOpen,
|
||||
});
|
||||
}
|
||||
|
||||
renderPending() {
|
||||
return (
|
||||
<div
|
||||
className="position-absolute w-100 h-100 d-flex justify-content-center align-items-center rounded-circle"
|
||||
style={{ backgroundColor: 'rgba(0,0,0,.65)' }}
|
||||
>
|
||||
<Spinner color="primary" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderMenu() {
|
||||
if (!this.props.isEditable) return null;
|
||||
|
||||
// TODO: only checking for null now. We need a way to
|
||||
// check if this src is the default image
|
||||
const hasImage = this.props.src === null;
|
||||
|
||||
if (hasImage) {
|
||||
return (
|
||||
<Button className="text-white btn-block" color="link" size="sm">
|
||||
<FormattedMessage
|
||||
id="profile.profileavatar.upload-button"
|
||||
defaultMessage="Upload Photo"
|
||||
description="Upload photo button"
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="profile-avatar-wrap position-relative">
|
||||
<div className="profile-avatar rounded-circle overflow-hidden bg-dark">
|
||||
{this.props.savePhotoState === 'pending' ? (
|
||||
<div
|
||||
className="p-absolute w-100 h-100 d-flex justify-content-center align-items-center"
|
||||
style={{ backgroundColor: 'rgba(255,255,255,.5)' }}
|
||||
>
|
||||
<Spinner color="primary" />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<button
|
||||
className="text-white profile-avatar-edit-button"
|
||||
onClick={this.onClick}
|
||||
>
|
||||
Change
|
||||
</button>
|
||||
|
||||
<form
|
||||
ref={this.form}
|
||||
onSubmit={this.onSubmit}
|
||||
encType="multipart/form-data"
|
||||
>
|
||||
<img className="w-100" src={src} alt="profile avatar" />
|
||||
|
||||
{/* The name of this input must be 'file' */}
|
||||
<Input
|
||||
className="d-none"
|
||||
innerRef={this.fileInput}
|
||||
type="file"
|
||||
name="file"
|
||||
id="exampleFile"
|
||||
onInput={this.onInput}
|
||||
accept=".jpg, .jpeg, .png"
|
||||
<Dropdown
|
||||
isOpen={this.state.dropdownOpen}
|
||||
toggle={this.toggleDropdown}
|
||||
>
|
||||
<DropdownToggle className="text-white btn-block" color="link" size="sm">
|
||||
<FormattedMessage
|
||||
id="profile.profileavatar.change-button"
|
||||
defaultMessage="Change"
|
||||
description="Change photo button"
|
||||
/>
|
||||
</DropdownToggle>
|
||||
<DropdownMenu>
|
||||
<DropdownItem onClick={this.onClickUpload}>
|
||||
<FormattedMessage
|
||||
id="profile.profileavatar.upload-button"
|
||||
defaultMessage="Upload Photo"
|
||||
description="Upload photo button"
|
||||
/>
|
||||
</form>
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this.onClickDelete}>
|
||||
<FormattedMessage
|
||||
id="profile.profileavatar.remove.button"
|
||||
defaultMessage="Remove"
|
||||
description="Remove photo button"
|
||||
/>
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="profile-avatar-wrap position-relative">
|
||||
<div className="profile-avatar rounded-circle bg-dark">
|
||||
<div className="profile-avatar-menu-container">
|
||||
{this.props.savePhotoState === 'pending' ? this.renderPending() : this.renderMenu() }
|
||||
</div>
|
||||
<img
|
||||
className="w-100 h-100 d-block rounded-circle overflow-hidden"
|
||||
style={{ objectFit: 'cover' }}
|
||||
alt="profile avatar"
|
||||
src={this.props.src}
|
||||
/>
|
||||
</div>
|
||||
{src ? (
|
||||
<button
|
||||
className="position-absolute btn btn-link w-100 btn-sm"
|
||||
onClick={this.onClickDelete}
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
) : null}
|
||||
<form
|
||||
ref={this.form}
|
||||
onSubmit={this.onSubmit}
|
||||
encType="multipart/form-data"
|
||||
>
|
||||
{/* The name of this input must be 'file' */}
|
||||
<Input
|
||||
className="d-none"
|
||||
innerRef={this.fileInput}
|
||||
type="file"
|
||||
name="file"
|
||||
id="photo-file"
|
||||
onInput={this.onInput}
|
||||
accept=".jpg, .jpeg, .png"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -97,9 +150,11 @@ ProfileAvatar.propTypes = {
|
||||
onSave: PropTypes.func.isRequired,
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
savePhotoState: PropTypes.oneOf([null, 'pending', 'complete', 'error']),
|
||||
isEditable: PropTypes.bool,
|
||||
};
|
||||
|
||||
ProfileAvatar.defaultProps = {
|
||||
src: null,
|
||||
savePhotoState: null,
|
||||
isEditable: false,
|
||||
};
|
||||
|
||||
@@ -57,6 +57,27 @@ $fa-font-path: "~font-awesome/fonts";
|
||||
}
|
||||
}
|
||||
|
||||
.profile-avatar-menu-container {
|
||||
background: rgba(0,0,0,.65);
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
@include media-breakpoint-up(md) {
|
||||
background: linear-gradient(to top, rgba(0,0,0,.65) 4rem, rgba(0,0,0,0) 4rem);
|
||||
align-items: flex-end;
|
||||
}
|
||||
.btn {
|
||||
text-decoration: none;
|
||||
@include media-breakpoint-up(md) {
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.profile-avatar {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
|
||||
Reference in New Issue
Block a user