read all api calls from context (#691)

main
Michael Fix 6 years ago committed by GitHub
parent a40f016fb4
commit 223bccd6b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -23,7 +23,8 @@ module.exports = {
'react/jsx-uses-react': 'error',
'react/jsx-uses-vars': 'error',
'jsx-a11y/click-events-have-key-events': 'off',
'react-hooks/rules-of-hooks': 'error'
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error'
},
settings: {
react: {

@ -0,0 +1,4 @@
import React from 'react'
import api from '../lib/api'
export default React.createContext(api)

@ -6,6 +6,7 @@ import dynamic from 'next/dynamic'
import Dropzone from 'dropperx'
// Ours
import ApiContext from './ApiContext'
import Dropdown from './Dropdown'
import Settings from './Settings'
import Toolbar from './Toolbar'
@ -38,6 +39,7 @@ const BackgroundSelect = dynamic(() => import('./BackgroundSelect'), {
})
class Editor extends React.Component {
static contextType = ApiContext
constructor(props) {
super(props)
this.state = {
@ -65,8 +67,8 @@ class Editor extends React.Component {
const initialState = Object.keys(queryParams).length ? queryParams : {}
try {
// TODO fix this hack
if (this.props.api.getGist && path.length >= 19 && path.indexOf('.') === -1) {
const { content, language } = await this.props.api.getGist(path)
if (this.context.gist && path.length >= 19 && path.indexOf('.') === -1) {
const { content, language } = await this.context.gist.get(path)
if (language) {
initialState.language = language.toLowerCase()
}
@ -115,9 +117,9 @@ class Editor extends React.Component {
) {
// if safari, get image from api
const isPNG = format !== 'svg'
if (this.props.api.image && this.isSafari && isPNG) {
if (this.context.image && this.isSafari && isPNG) {
const encodedState = serializeState(this.state)
return this.props.api.image(encodedState)
return this.context.image(encodedState)
}
const node = this.carbonNode.current
@ -226,7 +228,7 @@ class Editor extends React.Component {
upload() {
this.getCarbonImage({ format: 'png' }).then(
this.props.api.tweet.bind(null, this.state.code || DEFAULT_CODE)
this.context.tweet.bind(null, this.state.code || DEFAULT_CODE)
)
}
@ -322,7 +324,7 @@ class Editor extends React.Component {
getCarbonImage={this.getCarbonImage}
/>
<div className="buttons">
{this.props.api.tweet && <TweetButton onClick={this.upload} />}
<TweetButton onClick={this.upload} />
<ExportMenu
onChange={this.updateSetting}
export={this.export}
@ -385,7 +387,6 @@ function isImage(file) {
}
Editor.defaultProps = {
api: {},
onUpdate: () => {},
onReset: () => {}
}

@ -1,11 +1,12 @@
import React from 'react'
import ReactCrop, { makeAspectCrop } from 'react-image-crop'
import RandomImage, { downloadThumbnailImage } from './RandomImage'
import RandomImage from './RandomImage'
import PhotoCredit from './PhotoCredit'
import Input from './Input'
import { Link } from './Meta'
import { fileToDataURL } from '../lib/util'
import ApiContext from './ApiContext'
const getCroppedImg = (imageDataURL, pixelCrop) => {
const canvas = document.createElement('canvas')
@ -43,6 +44,7 @@ const INITIAL_STATE = {
}
export default class ImagePicker extends React.Component {
static contextType = ApiContext
constructor(props) {
super(props)
this.state = INITIAL_STATE
@ -103,7 +105,8 @@ export default class ImagePicker extends React.Component {
handleURLInput(e) {
e.preventDefault()
const url = e.target[0].value
return downloadThumbnailImage({ url })
return this.context
.downloadThumbnailImage({ url })
.then(({ dataURL }) =>
this.props.onChange({
backgroundImage: dataURL,

@ -2,47 +2,29 @@ import React from 'react'
import Spinner from 'react-spinner'
import { useAsyncCallback } from '@dawnlabs/tacklebox'
import api from '../lib/api'
import ApiContext from './ApiContext'
import PhotoCredit from './PhotoCredit'
import { fileToDataURL } from '../lib/util'
export const downloadThumbnailImage = img => {
return api.client
.get(img.url.replace('http://', 'https://'), { responseType: 'blob' })
.then(res => res.data)
.then(fileToDataURL)
.then(dataURL => Object.assign(img, { dataURL }))
}
const getImageDownloadUrl = img =>
api.client.get(`/unsplash/download/${img.id}`).then(res => res.data.url)
async function getImages() {
const imageUrls = await api.client.get('/unsplash/random')
return Promise.all(imageUrls.data.map(downloadThumbnailImage))
}
function RandomImage(props) {
const { current: cache } = React.useRef([])
const [cacheIndex, updateIndex] = React.useState(0)
const api = React.useContext(ApiContext)
const [selectImage, { loading: selecting }] = useAsyncCallback(() => {
const image = cache[cacheIndex]
return getImageDownloadUrl(image)
.then(url => api.client.get(url, { responseType: 'blob' }))
.then(res => res.data)
.then(blob => props.onChange(blob, image))
return api.unsplash.download(image.id).then(blob => props.onChange(blob, image))
})
const [updateCache, { loading: updating, data: imgs }] = useAsyncCallback(getImages)
const [updateCache, { loading: updating, error, data: imgs }] = useAsyncCallback(
api.unsplash.random
)
React.useEffect(() => {
if (cacheIndex === 0 || cacheIndex > cache.length - 2) {
if (!error && !updating && (!imgs || cacheIndex > cache.length - 2)) {
updateCache()
}
}, [cacheIndex, cache.length, updateCache])
}, [error, updating, imgs, cacheIndex, cache.length, updateCache])
React.useEffect(() => {
if (imgs) {

@ -1,14 +1,15 @@
import React from 'react'
import { useAsyncCallback } from '@dawnlabs/tacklebox'
import ApiContext from './ApiContext'
import Button from './Button'
function useWindowListener(key, fn) {
const callback = React.useRef(fn)
const { current: callback } = React.useRef(fn)
React.useEffect(() => {
window.addEventListener(key, callback.current)
return () => window.removeEventListener(key, callback.current)
window.addEventListener(key, callback)
return () => window.removeEventListener(key, callback)
}, [key, callback])
}
@ -30,9 +31,14 @@ function useOnlineListener() {
}
function TweetButton(props) {
const api = React.useContext(ApiContext)
const online = useOnlineListener()
const [onClick, { loading }] = useAsyncCallback(props.onClick)
if (!api || !api.tweet) {
return null
}
if (!online) {
return null
}

@ -2,6 +2,8 @@ import axios from 'axios'
import debounce from 'lodash.debounce'
import ms from 'ms'
import { fileToDataURL } from './util'
const client = axios.create({
baseURL: `${
process.env.API_URL || process.env.NODE_ENV === 'production' ? '' : 'http://localhost:4000'
@ -68,9 +70,34 @@ function checkIfRateLimited(err) {
throw err
}
const downloadThumbnailImage = img => {
return client
.get(img.url.replace('http://', 'https://'), { responseType: 'blob' })
.then(res => res.data)
.then(fileToDataURL)
.then(dataURL => Object.assign(img, { dataURL }))
}
const unsplash = {
download(id) {
return client
.get(`/unsplash/download/${id}`)
.then(res => res.data.url)
.then(url => client.get(url, { responseType: 'blob' }))
.then(res => res.data)
},
async random() {
const imageUrls = await client.get('/unsplash/random')
return Promise.all(imageUrls.data.map(downloadThumbnailImage))
}
}
export default {
client,
getGist,
gist: {
get: getGist
},
tweet: debounce(tweet, ms('5s'), { leading: true, trailing: false }),
image: debounce(image, ms('5s'), { leading: true, trailing: false })
image: debounce(image, ms('5s'), { leading: true, trailing: false }),
unsplash,
downloadThumbnailImage
}

@ -60,7 +60,7 @@
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-react": "^7.12.3",
"eslint-plugin-react-hooks": "^1.1.0-rc.0",
"eslint-plugin-react-hooks": "^1.4.0",
"husky": "^1.3.1",
"lint-staged": "^8.1.3",
"now": "^14.0.0",

@ -7,7 +7,6 @@ import debounce from 'lodash.debounce'
import Editor from '../components/Editor'
import Page from '../components/Page'
import { MetaLinks } from '../components/Meta'
import api from '../lib/api'
import { updateQueryString } from '../lib/routing'
import { saveSettings, clearSettings, omit } from '../lib/util'
@ -30,12 +29,7 @@ class Index extends React.Component {
return (
<Page enableHeroText={true}>
<MetaLinks />
<Editor
router={this.props.router}
onUpdate={this.onEditorUpdate}
api={api}
onReset={onReset}
/>
<Editor router={this.props.router} onUpdate={this.onEditorUpdate} onReset={onReset} />
</Page>
)
}

@ -2722,10 +2722,10 @@ eslint-plugin-jsx-a11y@^6.2.1:
has "^1.0.3"
jsx-ast-utils "^2.0.1"
eslint-plugin-react-hooks@^1.1.0-rc.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.2.0.tgz#a1c78e792b8d7d3e9c2a2aad28df80b9b5cd1101"
integrity sha512-pb/pwyHg0K3Ss/8loSwCGRSXIsvPBHWfzcP/6jeei0SgWBOyXRbcKFpGxolg0xSmph0jQKLyM27B74clbZM/YQ==
eslint-plugin-react-hooks@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.4.0.tgz#ad86001e05519368e55a888b1b31004b8e2ae8f6"
integrity sha512-fMGlzztW/5hSQT0UBnlPwulao0uF8Kyp0Uv6PA81lzmcDz2LBtthkWQaE8Wz2F2kEe7mSRDgK8ABEFK1ipeDxw==
eslint-plugin-react@^7.12.3:
version "7.12.4"

Loading…
Cancel
Save