[ABANDONED] React/Redux front end for the Flexor social network.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

131 lines
4.6 KiB

import React, { FC, ChangeEvent, useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import classNames from 'classnames'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faUpload } from '@fortawesome/free-solid-svg-icons'
import { uploadBrowserDataToBlockBlob, Aborter, BlockBlobURL, AnonymousCredential } from '@azure/storage-blob'
import { setFieldValue } from 'src/actions/forms'
import { showNotification } from 'src/actions/notifications'
import { getConfig } from 'src/selectors'
import { getFieldValue } from 'src/selectors/forms'
import { apiFetch } from 'src/api/fetch'
import { AppState, ClassDictionary, SasResponse, NotificationType, Config } from 'src/types'
interface Props {
name: string
label: string
help?: string
previewWidth?: number
}
const FileField: FC<Props> = ({ name, label, help, previewWidth = 128 }) => {
const value = useSelector<AppState, boolean>(state => getFieldValue<boolean>(state, name, false))
const config = useSelector<AppState, Config>(getConfig)
const dispatch = useDispatch()
const [progress, setProgress] = useState(0)
const [uploading, setUploading] = useState(false)
const [uploaded, setUploaded] = useState(false)
const classes: ClassDictionary = {
file: true,
'is-primary': true,
'has-name': !!value,
}
const handleChange = async (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
const file = event.target.files[0]
if (file.size > 1024 * 1024 * 5) {
dispatch(showNotification(NotificationType.Error, 'Files must be less than 5 MBs'))
return
}
const ext = file.name.substring(file.name.lastIndexOf('.'))
const { sas, id } = await apiFetch<SasResponse>({ path: '/api/sas' })
const filename = `${id}${ext}`
const blobURL = new BlockBlobURL(`${config.blobUrl}${filename}?${sas}`, BlockBlobURL.newPipeline(new AnonymousCredential()))
setUploading(true)
await uploadBrowserDataToBlockBlob(Aborter.none, file, blobURL, {
blockSize: 4 * 1024 * 1024,
progress: p => {
setProgress((p.loadedBytes / file.size) * 100)
}
})
await apiFetch({
path: '/api/media',
method: 'post',
body: {
name: filename,
size: file.size,
type: file.type,
originalName: file.name,
}
})
dispatch(setFieldValue(name, filename))
setUploaded(true)
setUploading(false)
}
}
const handleDelete = async () => {
if (uploaded) {
await apiFetch({
path: '/api/media/delete',
method: 'post',
body: {
name: value,
}
})
}
dispatch(setFieldValue(name, ''))
}
if (uploading) {
return (
<div className="field">
<label className="label">{label}</label>
<progress className="progress is-success" value={progress} max="100">{progress}%</progress>
</div>
)
}
return (
<div>
<div className="field">
<label className="label">{label}</label>
{value &&
<div style={{ padding: '10px 0px' }}>
<img src={`${config.blobUrl}${value}`} style={{ width: previewWidth }} />
<br />
<a className="is-danger is-size-7" onClick={() => handleDelete()}>Delete</a>
</div>
}
<div className={classNames(classes)}>
<label className="file-label">
<input className="file-input" type="file" name={name} onChange={handleChange} />
<span className="file-cta">
<span className="file-icon">
<FontAwesomeIcon icon={faUpload} />
</span>
<span className="file-label">
Upload
</span>
</span>
{value && <span className="file-name">{value}</span>}
</label>
</div>
<p className="help">{help}</p>
</div>
</div>
)
}
export default FileField