Add saving of profile photo (#19)

* Add saving of profile photo

* Add removal of profile photo

Needs work on knowing when a default photo has been supplied.

* Add action creator tests

* Fix some reference issues after merge

* Fix broken test
This commit is contained in:
Adam Butterworth
2019-02-20 11:44:47 -05:00
committed by GitHub
parent f1b6af4975
commit e7ffc6fe0c
9 changed files with 412 additions and 46 deletions

View File

@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Input } from 'reactstrap';
import { Input, Spinner } from 'reactstrap';
class ProfileAvatar extends React.Component {
constructor(props) {
@@ -10,8 +10,8 @@ class ProfileAvatar extends React.Component {
this.form = React.createRef();
this.onClick = this.onClick.bind(this);
this.onClickDelete = this.onClickDelete.bind(this);
this.onInput = this.onInput.bind(this);
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
@@ -19,17 +19,17 @@ class ProfileAvatar extends React.Component {
this.fileInput.current.click();
}
onInput(e) { // eslint-disable-line no-unused-vars
// console.log('input', e)
this.form.current.submit();
onClickDelete() {
this.props.onDelete();
}
onChange(e) { // eslint-disable-line no-unused-vars
// console.log('change', e)
onInput() {
this.onSubmit();
}
onSubmit(e) { // eslint-disable-line no-unused-vars
// console.log('onsubmit', e);
onSubmit(e) {
if (e) e.preventDefault();
this.props.onSave(new FormData(this.form.current));
}
render() {
@@ -38,31 +38,52 @@ class ProfileAvatar extends React.Component {
} = this.props;
return (
<div className="profile-avatar rounded-circle overflow-hidden">
<button
className="text-white profile-avatar-edit-button"
onClick={this.onClick}
>
Update
</button>
<img className="w-100" src={src} alt="profile avatar" />
<form
ref={this.form}
onSubmit={this.onSubmit}
method="post"
encType="multipart/form-data"
>
<Input
className="d-none"
innerRef={this.fileInput}
type="file"
name="file"
id="exampleFile"
onInput={this.onInput}
onChange={this.onChange}
accept=".jpg, .jpeg, .png"
/>
</form>
<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"
/>
</form>
</div>
{src ? (
<button
className="position-absolute btn btn-link w-100 btn-sm"
onClick={this.onClickDelete}
>
Remove
</button>
) : null}
</div>
);
}
@@ -73,8 +94,12 @@ export default ProfileAvatar;
ProfileAvatar.propTypes = {
src: PropTypes.string,
onSave: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired,
savePhotoState: PropTypes.oneOf([null, 'pending', 'complete', 'error']),
};
ProfileAvatar.defaultProps = {
src: null,
savePhotoState: null,
};

View File

@@ -27,6 +27,8 @@ class UserProfile extends React.Component {
this.onCancel = this.onCancel.bind(this);
this.onEdit = this.onEdit.bind(this);
this.onSave = this.onSave.bind(this);
this.onSaveProfilePhoto = this.onSaveProfilePhoto.bind(this);
this.onDeleteProfilePhoto = this.onDeleteProfilePhoto.bind(this);
this.onChange = this.onChange.bind(this);
this.onVisibilityChange = this.onVisibilityChange.bind(this);
}
@@ -46,6 +48,14 @@ class UserProfile extends React.Component {
this.props.saveUserProfile(this.props.username, userAccountData, fieldName);
}
onSaveProfilePhoto(formData) {
this.props.saveUserProfilePhoto(this.props.username, formData);
}
onDeleteProfilePhoto() {
this.props.deleteUserProfilePhoto(this.props.username);
}
onChange(fieldName, value) {
this.setState({
[fieldName]: {
@@ -103,7 +113,9 @@ class UserProfile extends React.Component {
<ProfileAvatar
className="mb-md-3"
src={profileImage}
{...commonProps}
onSave={this.onSaveProfilePhoto}
onDelete={this.onDeleteProfilePhoto}
savePhotoState={this.props.savePhotoState}
/>
<div>
<h2 className="mb-0">{username}</h2>
@@ -167,6 +179,7 @@ export default UserProfile;
UserProfile.propTypes = {
currentlyEditingField: PropTypes.string,
saveState: PropTypes.oneOf([null, 'pending', 'complete', 'error']),
savePhotoState: PropTypes.oneOf([null, 'pending', 'complete', 'error']),
error: PropTypes.string,
profileImage: PropTypes.string,
fullName: PropTypes.string,
@@ -183,6 +196,8 @@ UserProfile.propTypes = {
title: PropTypes.string,
})),
saveUserProfile: PropTypes.func,
saveUserProfilePhoto: PropTypes.func.isRequired,
deleteUserProfilePhoto: PropTypes.func.isRequired,
openEditableField: PropTypes.func.isRequired,
closeEditableField: PropTypes.func.isRequired,
};
@@ -190,6 +205,7 @@ UserProfile.propTypes = {
UserProfile.defaultProps = {
currentlyEditingField: null,
saveState: null,
savePhotoState: null,
error: null,
profileImage: null,
fullName: null,