|
|
|
@ -1,5 +1,6 @@
|
|
|
|
|
import React from 'react'
|
|
|
|
|
import Spinner from 'react-spinner'
|
|
|
|
|
import { useAsyncCallback } from '@dawnlabs/tacklebox'
|
|
|
|
|
|
|
|
|
|
import api from '../lib/api'
|
|
|
|
|
|
|
|
|
@ -17,80 +18,53 @@ export const downloadThumbnailImage = img => {
|
|
|
|
|
const getImageDownloadUrl = img =>
|
|
|
|
|
api.client.get(`/unsplash/download/${img.id}`).then(res => res.data.url)
|
|
|
|
|
|
|
|
|
|
class RandomImage extends React.Component {
|
|
|
|
|
constructor(props) {
|
|
|
|
|
super(props)
|
|
|
|
|
this.state = { cacheIndex: 0, loading: false }
|
|
|
|
|
this.selectImage = this.selectImage.bind(this)
|
|
|
|
|
this.updateCache = this.updateCache.bind(this)
|
|
|
|
|
this.getImages = this.getImages.bind(this)
|
|
|
|
|
this.nextImage = this.nextImage.bind(this)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cache = []
|
|
|
|
|
|
|
|
|
|
// fetch images in browser (we require window.FileReader)
|
|
|
|
|
componentDidMount() {
|
|
|
|
|
// clear cache when remounted
|
|
|
|
|
this.cache = []
|
|
|
|
|
this.updateCache()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getImages() {
|
|
|
|
|
async function getImages() {
|
|
|
|
|
const imageUrls = await api.client.get('/unsplash/random')
|
|
|
|
|
return Promise.all(imageUrls.data.map(downloadThumbnailImage))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
selectImage() {
|
|
|
|
|
if (this.state.loading) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
function RandomImage(props) {
|
|
|
|
|
const { current: cache } = React.useRef([])
|
|
|
|
|
const [cacheIndex, updateIndex] = React.useState(0)
|
|
|
|
|
|
|
|
|
|
const image = this.cache[this.state.cacheIndex]
|
|
|
|
|
const [selectImage, { loading: selecting }] = useAsyncCallback(() => {
|
|
|
|
|
const image = cache[cacheIndex]
|
|
|
|
|
|
|
|
|
|
this.setState({ loading: true })
|
|
|
|
|
getImageDownloadUrl(image)
|
|
|
|
|
return getImageDownloadUrl(image)
|
|
|
|
|
.then(url => api.client.get(url, { responseType: 'blob' }))
|
|
|
|
|
.then(res => res.data)
|
|
|
|
|
.then(blob => this.props.onChange(blob, image))
|
|
|
|
|
.then(() => this.setState({ loading: false }))
|
|
|
|
|
}
|
|
|
|
|
.then(blob => props.onChange(blob, image))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
updateCache() {
|
|
|
|
|
this.setState({ loading: true })
|
|
|
|
|
this.getImages()
|
|
|
|
|
.then(imgs => (this.cache = this.cache.concat(imgs)))
|
|
|
|
|
.then(() => this.setState({ loading: false }))
|
|
|
|
|
}
|
|
|
|
|
const [updateCache, { loading: updating, data: imgs }] = useAsyncCallback(getImages)
|
|
|
|
|
|
|
|
|
|
nextImage() {
|
|
|
|
|
if (this.state.loading) {
|
|
|
|
|
return
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
if (cacheIndex === 0 || cacheIndex > cache.length - 2) {
|
|
|
|
|
updateCache()
|
|
|
|
|
}
|
|
|
|
|
}, [cacheIndex])
|
|
|
|
|
|
|
|
|
|
this.setState(state => ({ cacheIndex: state.cacheIndex + 1 }))
|
|
|
|
|
|
|
|
|
|
if (this.state.cacheIndex > this.cache.length - 2) {
|
|
|
|
|
this.updateCache()
|
|
|
|
|
}
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
if (imgs) {
|
|
|
|
|
cache.push(...imgs)
|
|
|
|
|
}
|
|
|
|
|
}, [imgs])
|
|
|
|
|
|
|
|
|
|
render() {
|
|
|
|
|
const photographer =
|
|
|
|
|
this.cache[this.state.cacheIndex] && this.cache[this.state.cacheIndex].photographer
|
|
|
|
|
const bgImage = this.cache[this.state.cacheIndex] && this.cache[this.state.cacheIndex].dataURL
|
|
|
|
|
const loading = updating || selecting
|
|
|
|
|
|
|
|
|
|
const photographer = cache[cacheIndex] && cache[cacheIndex].photographer
|
|
|
|
|
const bgImage = cache[cacheIndex] && cache[cacheIndex].dataURL
|
|
|
|
|
return (
|
|
|
|
|
<div className="random-image-container">
|
|
|
|
|
<div className="controls">
|
|
|
|
|
<span role="button" tabIndex={0} onClick={this.selectImage}>
|
|
|
|
|
<span role="button" tabIndex={0} disabled={loading} onClick={selectImage}>
|
|
|
|
|
Use Image
|
|
|
|
|
</span>
|
|
|
|
|
<span role="button" tabIndex={0} onClick={this.nextImage}>
|
|
|
|
|
<span role="button" tabIndex={0} disabled={loading} onClick={() => updateIndex(i => i + 1)}>
|
|
|
|
|
Try Another
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="image">{this.state.loading && <Spinner />}</div>
|
|
|
|
|
<div className="image">{loading && <Spinner />}</div>
|
|
|
|
|
{photographer && <PhotoCredit photographer={photographer} />}
|
|
|
|
|
<style jsx>
|
|
|
|
|
{`
|
|
|
|
@ -110,15 +84,14 @@ class RandomImage extends React.Component {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
span {
|
|
|
|
|
opacity: ${this.state.loading ? 0.5 : 1};
|
|
|
|
|
cursor: ${this.state.loading ? 'not-allowed' : 'pointer'};
|
|
|
|
|
opacity: ${loading ? 0.5 : 1};
|
|
|
|
|
cursor: ${loading ? 'not-allowed' : 'pointer'};
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
`}
|
|
|
|
|
</style>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default RandomImage
|
|
|
|
|