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:
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user