Improving interface for media uploads
This commit is contained in:
parent
d14518ac91
commit
9fcc14c8e6
|
@ -13,17 +13,18 @@ sys.path.insert(0,'..')
|
||||||
# Hack to work around ascii encode error
|
# Hack to work around ascii encode error
|
||||||
# TODO: Figure out which dependency tries to encode input to ascii
|
# TODO: Figure out which dependency tries to encode input to ascii
|
||||||
reload(sys)
|
reload(sys)
|
||||||
sys.setdefaultencoding("utf-8")
|
sys.setdefaultencoding('utf-8')
|
||||||
|
|
||||||
s3 = boto3.client('s3')
|
s3 = boto3.client('s3')
|
||||||
|
|
||||||
def create_presigned_post():
|
def create_presigned_post():
|
||||||
bucket_name = config.JOYCE_S3_BUCKET
|
bucket_name = config.JOYCE_S3_BUCKET
|
||||||
key_name = str(uuid.uuid4())
|
key_name = 'images/' + str(uuid.uuid4())
|
||||||
response = s3.generate_presigned_post(
|
response = s3.generate_presigned_post(
|
||||||
bucket_name,
|
bucket_name,
|
||||||
key_name,
|
key_name,
|
||||||
ExpiresIn = 3600
|
ExpiresIn = 3600,
|
||||||
|
Conditions = [[ 'eq', '$acl', 'public-read' ]],
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -158,19 +159,19 @@ def es_search_text(body):
|
||||||
'from': 0,
|
'from': 0,
|
||||||
'size': 10,
|
'size': 10,
|
||||||
'query': {
|
'query': {
|
||||||
"nested": {
|
'nested': {
|
||||||
"path": "search_text",
|
'path': 'search_text',
|
||||||
"query": {
|
'query': {
|
||||||
"bool": {
|
'bool': {
|
||||||
"must": [
|
'must': [
|
||||||
{ "match": { "search_text.text": body}}
|
{ 'match': { 'search_text.text': body}}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"inner_hits": {
|
'inner_hits': {
|
||||||
"highlight": {
|
'highlight': {
|
||||||
"fields": {
|
'fields': {
|
||||||
"search_text.text": {}
|
'search_text.text': {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,11 @@ const userActions = {
|
||||||
({
|
({
|
||||||
type: 'UPLOAD_MEDIA_SUBMIT',
|
type: 'UPLOAD_MEDIA_SUBMIT',
|
||||||
data: input
|
data: input
|
||||||
})
|
}),
|
||||||
|
clearLoadedMedia: () =>
|
||||||
|
({
|
||||||
|
type: 'CLEAR_LOADED_MEDIA'
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
export default userActions
|
export default userActions
|
|
@ -29,7 +29,7 @@ export const EditorEditModeRichTextOptions = ({editorState, onToolButtonClick, d
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
export const EditorSubmitOptions = ({cancelEdit, onSubmitClick}) =>
|
export const EditorSubmitOptions = ({cancelEdit, onSubmitClick}) =>
|
||||||
<div className='row'>
|
<div className='row mt-2'>
|
||||||
<div className='submit_option_button col-5'>
|
<div className='submit_option_button col-5'>
|
||||||
<EditorCancelButton onClick={()=>cancelEdit()}/>
|
<EditorCancelButton onClick={()=>cancelEdit()}/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,4 +46,4 @@ export const EditorAnnotateOptions = ({onNewAnnotationClick, onRemoveAnnotationC
|
||||||
<div className='annotate_option_button col-5 offset-2'>
|
<div className='annotate_option_button col-5 offset-2'>
|
||||||
<AnnotatorRemoveButton onClick={onRemoveAnnotationClick} disabled={removeDisabled} />
|
<AnnotatorRemoveButton onClick={onRemoveAnnotationClick} disabled={removeDisabled} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import { MediaList } from './list'
|
||||||
|
|
||||||
|
const NoteMediaPicker = ({media}) =>
|
||||||
|
<div id='note_media_picker' className='row'>
|
||||||
|
<div className='col-12'>
|
||||||
|
<div className="btn-group">
|
||||||
|
<button className="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||||
|
Select Media
|
||||||
|
</button>
|
||||||
|
<div className="dropdown-menu">
|
||||||
|
{media.map(media =>
|
||||||
|
<div key={media.id}>
|
||||||
|
<input type="checkbox" />
|
||||||
|
<div>{media.title}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
export default NoteMediaPicker
|
|
@ -8,10 +8,12 @@ import actions from '../actions'
|
||||||
import DocumentTitle from '../components/documentTitle'
|
import DocumentTitle from '../components/documentTitle'
|
||||||
import DocumentTitleInput from '../components/documentTitleInput'
|
import DocumentTitleInput from '../components/documentTitleInput'
|
||||||
import TagColorPicker from '../components/tagColorPicker'
|
import TagColorPicker from '../components/tagColorPicker'
|
||||||
|
import NoteMediaPicker from '../components/noteMediaPicker'
|
||||||
import MediaUploadInput from '../components/mediaUploadInput'
|
import MediaUploadInput from '../components/mediaUploadInput'
|
||||||
import LoadingSpinner from '../components/loadingSpinner'
|
import LoadingSpinner from '../components/loadingSpinner'
|
||||||
|
|
||||||
const EditorEditMode = ({
|
const EditorEditMode = ({
|
||||||
|
media,
|
||||||
currentDocument,
|
currentDocument,
|
||||||
docType,
|
docType,
|
||||||
editorState,
|
editorState,
|
||||||
|
@ -22,6 +24,7 @@ const EditorEditMode = ({
|
||||||
onDocumentTitleChange,
|
onDocumentTitleChange,
|
||||||
onColorPickerInputChange,
|
onColorPickerInputChange,
|
||||||
onColorSwatchClick,
|
onColorSwatchClick,
|
||||||
|
onClearLoadedMedia,
|
||||||
onMediaInputChange,
|
onMediaInputChange,
|
||||||
onMediaUpload,
|
onMediaUpload,
|
||||||
cancelEdit,
|
cancelEdit,
|
||||||
|
@ -50,6 +53,9 @@ const EditorEditMode = ({
|
||||||
/>
|
/>
|
||||||
</EditorTextContentBlock>
|
</EditorTextContentBlock>
|
||||||
<EditorAttributeContentBlock>
|
<EditorAttributeContentBlock>
|
||||||
|
{docType === 'notes' &&
|
||||||
|
<NoteMediaPicker media={media} />
|
||||||
|
}
|
||||||
{docType === 'tags' &&
|
{docType === 'tags' &&
|
||||||
<TagColorPicker
|
<TagColorPicker
|
||||||
input={inputs.colorPicker}
|
input={inputs.colorPicker}
|
||||||
|
@ -58,7 +64,14 @@ const EditorEditMode = ({
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
{docType === 'media' && inputs.s3Path &&
|
{docType === 'media' && inputs.s3Path &&
|
||||||
<p>File uploaded!</p>
|
<div className='row'>
|
||||||
|
<div className='col-8'>File uploaded!</div>
|
||||||
|
<div className='col-2 offset-2'>
|
||||||
|
<button type='button' onClick={onClearLoadedMedia} className='btn btn-outline-info btn-sm'>
|
||||||
|
<i className={'fas fa-trash-alt'}></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
{docType === 'media' && !inputs.s3Path &&
|
{docType === 'media' && !inputs.s3Path &&
|
||||||
<MediaUploadInput input={inputs.uploadFile} onChange={onMediaInputChange} onUpload={onMediaUpload}/>
|
<MediaUploadInput input={inputs.uploadFile} onChange={onMediaInputChange} onUpload={onMediaUpload}/>
|
||||||
|
@ -79,6 +92,7 @@ const EditorEditMode = ({
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
return {
|
return {
|
||||||
|
media: state.media,
|
||||||
currentDocument: state.currentDocument,
|
currentDocument: state.currentDocument,
|
||||||
docType: state.docType,
|
docType: state.docType,
|
||||||
editorState: state.editorState,
|
editorState: state.editorState,
|
||||||
|
@ -107,6 +121,9 @@ const mapDispatchToProps = dispatch => {
|
||||||
onMediaUpload: input => {
|
onMediaUpload: input => {
|
||||||
dispatch(actions.uploadMediaInput(input))
|
dispatch(actions.uploadMediaInput(input))
|
||||||
},
|
},
|
||||||
|
onClearLoadedMedia: () => {
|
||||||
|
dispatch(actions.clearLoadedMedia())
|
||||||
|
},
|
||||||
cancelEdit: () => {
|
cancelEdit: () => {
|
||||||
dispatch(actions.cancelEdit())
|
dispatch(actions.cancelEdit())
|
||||||
},
|
},
|
||||||
|
|
|
@ -54,8 +54,9 @@ const joyceAPI = store => next => action => {
|
||||||
case 'UPLOAD_TO_S3_REQUEST':
|
case 'UPLOAD_TO_S3_REQUEST':
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
const url = action.signed_post.url
|
const url = action.signed_post.url
|
||||||
formData.append('AWSAccessKeyId', action.signed_post.fields.AWSAccessKeyId)
|
|
||||||
formData.append('key', action.signed_post.fields.key)
|
formData.append('key', action.signed_post.fields.key)
|
||||||
|
formData.append('AWSAccessKeyId', action.signed_post.fields.AWSAccessKeyId)
|
||||||
|
formData.append('acl', 'public-read')
|
||||||
formData.append('policy', action.signed_post.fields.policy)
|
formData.append('policy', action.signed_post.fields.policy)
|
||||||
formData.append('signature', action.signed_post.fields.signature)
|
formData.append('signature', action.signed_post.fields.signature)
|
||||||
formData.append('file', action.file)
|
formData.append('file', action.file)
|
||||||
|
|
|
@ -52,7 +52,7 @@ const api = {
|
||||||
return {status: 'error', data: error}
|
return {status: 'error', data: error}
|
||||||
}),
|
}),
|
||||||
HTTPPostMedia: (url, formData) =>
|
HTTPPostMedia: (url, formData) =>
|
||||||
axios.post(url, formData, {headers: {'Content-Type': 'image/*', 'ACL': 'public-read'}}).then(res=> {
|
axios.post(url, formData, {headers: {'Content-Type': formData.get('file').type}}).then(res=> {
|
||||||
return {status: 'success', url: url + formData.get('key')}
|
return {status: 'success', url: url + formData.get('key')}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
return {status: 'error', data: error}
|
return {status: 'error', data: error}
|
||||||
|
|
|
@ -9,6 +9,11 @@ export const validateSubmittedDocument = (docType, inputs) => {
|
||||||
errors.push('Please select a valid hex code color.')
|
errors.push('Please select a valid hex code color.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (docType === 'media') {
|
||||||
|
if (inputs.s3Path === undefined) {
|
||||||
|
errors.push('Please upload an image first.')
|
||||||
|
}
|
||||||
|
}
|
||||||
if (inputs.documentTitle.length < 1) {
|
if (inputs.documentTitle.length < 1) {
|
||||||
errors.push('Please enter a title.')
|
errors.push('Please enter a title.')
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ const initialState = {
|
||||||
documentTitle: '',
|
documentTitle: '',
|
||||||
search: '',
|
search: '',
|
||||||
colorPicker: '',
|
colorPicker: '',
|
||||||
|
noteMediaSelection: [],
|
||||||
uploadFile: undefined,
|
uploadFile: undefined,
|
||||||
s3Path: undefined
|
s3Path: undefined
|
||||||
}
|
}
|
||||||
|
@ -10,9 +11,6 @@ const inputs = (state=initialState, action) => {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
// Document Title
|
// Document Title
|
||||||
case 'GET_DOCUMENT_TEXT':
|
case 'GET_DOCUMENT_TEXT':
|
||||||
console.log('state', action.state)
|
|
||||||
console.log('status', action.status)
|
|
||||||
console.log('docType', action.docType)
|
|
||||||
if (action.status === 'success' && action.state === 'currentDocument' && ['tags', 'media'].indexOf(action.docType) <= 0 ) {
|
if (action.status === 'success' && action.state === 'currentDocument' && ['tags', 'media'].indexOf(action.docType) <= 0 ) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
@ -76,8 +74,6 @@ const inputs = (state=initialState, action) => {
|
||||||
...state,
|
...state,
|
||||||
uploadFile: action.data
|
uploadFile: action.data
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
return state
|
|
||||||
// S3 File
|
// S3 File
|
||||||
case 'UPLOAD_TO_S3_RESPONSE':
|
case 'UPLOAD_TO_S3_RESPONSE':
|
||||||
if (action.status === 'success') {
|
if (action.status === 'success') {
|
||||||
|
@ -86,6 +82,13 @@ const inputs = (state=initialState, action) => {
|
||||||
s3Path: action.url
|
s3Path: action.url
|
||||||
}
|
}
|
||||||
} else { return state }
|
} else { return state }
|
||||||
|
case 'CLEAR_LOADED_MEDIA':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
s3Path: undefined
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,15 @@
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#note_media_picker {
|
||||||
|
div {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#editor_top_bar_block {
|
#editor_top_bar_block {
|
||||||
button {
|
button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
Loading…
Reference in New Issue