API & Client: Profile picture

This commit is contained in:
SamR1
2018-01-01 21:54:03 +01:00
parent 8e21fc4112
commit ddb765e02d
14 changed files with 293 additions and 8 deletions

View File

@ -11,6 +11,10 @@ function AuthErrors(messages) {
return { type: 'AUTH_ERRORS', messages }
}
function PictureError(message) {
return { type: 'PICTURE_ERROR', message }
}
function ProfileSuccess(message) {
return { type: 'PROFILE_SUCCESS', message }
}
@ -151,7 +155,7 @@ export function handleProfileFormSubmit(event) {
event.preventDefault()
return (dispatch, getState) => {
const state = getState()
if (state.formProfile.formProfile.password !==
if (!state.formProfile.formProfile.password ===
state.formProfile.formProfile.passwordConf) {
dispatch(PwdError('Password and password confirmation don\'t match.'))
} else {
@ -172,3 +176,41 @@ export function handleProfileFormSubmit(event) {
}
}
export function uploadPicture (event) {
event.preventDefault()
const form = new FormData()
form.append('file', event.target.picture.files[0])
event.target.reset()
return function(dispatch) {
return mpwoApi
.updatePicture(form)
.then(ret => {
if (ret.status === 'success') {
getProfile(dispatch)
} else {
dispatch(PictureError(ret.message))
}
})
.catch(error => {
throw error
})
}
}
export function deletePicture() {
return function(dispatch) {
return mpwoApi
.deletePicture()
.then(ret => {
if (ret.status === 'success') {
getProfile(dispatch)
} else {
dispatch(PictureError(ret.message))
}
})
.catch(error => {
throw error
})
}
}

View File

@ -22,6 +22,18 @@
font-size: large;
}
.App-nav-profile-img {
max-width: 35px;
max-height: 35px;
border-radius: 50%;
}
.App-profile-img-small {
max-width: 150px;
max-height: 150px;
border-radius: 50%;
}
@keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }

View File

@ -2,6 +2,9 @@ import React from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import mpwoApi from '../../mpwoApi'
function NavBar (props) {
return (
<header>
@ -56,6 +59,14 @@ function NavBar (props) {
</Link>
</li>
)}
{props.user.picture === true && (
<img
alt="Profile"
src={`${mpwoApi.getApiUrl()}users/${props.user.id}/picture` +
`?${Date.now()}`}
className="img-fluid App-nav-profile-img"
/>
)}
{props.user.isAuthenticated && (
<li className="nav-item">
<Link

View File

@ -3,12 +3,18 @@ import { Helmet } from 'react-helmet'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
function Profile ({ user }) {
import mpwoApi from '../../mpwoApi'
import { deletePicture, uploadPicture } from '../../actions'
function Profile ({ message, onDeletePicture, onUploadPicture, user }) {
return (
<div>
<Helmet>
<title>mpwo - {user.username} - Profile</title>
</Helmet>
{ message !== '' && (
<code>{message}</code>
)}
<div className="container">
<h1 className="page-title">Profile</h1>
<div className="row">
@ -35,6 +41,38 @@ function Profile ({ user }) {
<p>Location : {user.location}</p>
<p>Bio : {user.bio}</p>
</div>
<div className="col-md-4">
{ user.picture === true && (
<div>
<img
alt="Profile"
src={`${mpwoApi.getApiUrl()}users/${user.id}/picture` +
`?${Date.now()}`}
className="img-fluid App-profile-img-small"
/>
<br />
<button
type="submit"
onClick={() => onDeletePicture()}
>
Delete picture
</button>
<br /><br />
</div>
)}
<form
encType="multipart/form-data"
onSubmit={event => onUploadPicture(event)}
>
<input
type="file"
name="picture"
accept=".png,.jpg,.gif"
/>
<br />
<button type="submit">Send</button>
</form>
</div>
</div>
</div>
</div>
@ -55,6 +93,15 @@ function Profile ({ user }) {
export default connect(
state => ({
message: state.message,
user: state.user,
}),
dispatch => ({
onDeletePicture: () => {
dispatch(deletePicture())
},
onUploadPicture: event => {
dispatch(uploadPicture(event))
},
})
)(Profile)

View File

@ -67,4 +67,30 @@ export default class MpwoApi {
.then(response => response.json())
.catch(error => error)
}
static updatePicture(form) {
const request = new Request(`${apiUrl}auth/picture`, {
method: 'POST',
headers: new Headers({
Authorization: `Bearer ${window.localStorage.getItem('authToken')}`,
}),
body: form,
})
return fetch(request)
.then(response => response.json())
.catch(error => error)
}
static deletePicture() {
const request = new Request(`${apiUrl}auth/picture`, {
method: 'DELETE',
headers: new Headers({
Authorization: `Bearer ${window.localStorage.getItem('authToken')}`,
}),
})
return fetch(request)
.then(response => response.json())
.catch(error => error)
}
static getApiUrl() {
return apiUrl
}
}

View File

@ -50,6 +50,7 @@ const message = (state = initial.message, action) => {
case 'AUTH_ERROR':
case 'PROFILE_ERROR':
case 'PWD_ERROR':
case 'PICTURE_ERROR':
return action.message
case 'LOGOUT':
case 'PROFILE_SUCCESS':
@ -103,6 +104,9 @@ const user = (state = initial.user, action) => {
birthDate: action.message.data.birth_date
? action.message.data.birth_date
: '',
picture: action.message.data.picture === true
? action.message.data.picture
: false,
}
default:
return state

View File

@ -12,7 +12,8 @@ export default {
lastName: '',
bio: '',
location: '',
birthDate: ''
birthDate: '',
picture: false
},
formData: {
formData: {
@ -30,7 +31,7 @@ export default {
location: '',
birthDate: '',
password: '',
passwordConf: ''
passwordConf: '',
}
},
}