Skip to content

Commit

Permalink
[infrastructure] Add infrastructure for generic uploads and support b…
Browse files Browse the repository at this point in the history
…atch uploading to arrays (#229)
  • Loading branch information
bjoerge committed Oct 12, 2017
1 parent 4de8a0c commit 655e652
Show file tree
Hide file tree
Showing 34 changed files with 919 additions and 105 deletions.
26 changes: 24 additions & 2 deletions packages/@sanity/base/src/preview/SanityDefaultPreview.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import PropTypes from 'prop-types'
import React from 'react'
import UploadProgressBar from './UploadProgressBar'

import PreviewComponentCard from 'part:@sanity/components/previews/card'
import PreviewComponentDefault from 'part:@sanity/components/previews/default'
Expand All @@ -17,6 +18,14 @@ const previewComponentMap = {
block: PreviewComponentBlock
}

function extractUploadState(value) {
if (!value || typeof value !== 'object') {
return {_upload: null, value}
}
const {_upload, ...rest} = value
return {_upload, value: rest}
}

export default class SanityDefaultPreview extends React.PureComponent {

static propTypes = {
Expand All @@ -25,12 +34,25 @@ export default class SanityDefaultPreview extends React.PureComponent {
}

render() {
const {layout, value, ...rest} = this.props
const {layout, ...rest} = this.props

const PreviewComponent = previewComponentMap.hasOwnProperty(layout)
? previewComponentMap[layout]
: previewComponentMap.default

return <PreviewComponent item={value} {...rest} />
const {_upload, value} = extractUploadState(this.props.value)

const item = _upload ? {
...value,
// todo: remove this from here and handle invalid preview urls (e.g. blob urls from another computer)
imageUrl: _upload.previewImage
} : value

return (
<div>
{_upload && <UploadProgressBar percent={_upload.percent} />}
<PreviewComponent item={item} {...rest} />
</div>
)
}
}
40 changes: 40 additions & 0 deletions packages/@sanity/base/src/preview/UploadProgressBar.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@import "part:@sanity/base/theme/variables-style";

:root {
--progress-bar-height: 2px;
}

.root {
position: relative;
width: 100%;
clear: both;
z-index: 1000;
}

.inner {
width: 100%;
position: absolute;
}

.barContainer {
position: relative;
width: 100%;
height: var(--progress-bar-height);
padding: 0;
box-sizing: border-box;
}

.bar {
background-color: black;
margin: 2px 2px 0 2px;
height: var(--progress-bar-height);
transition-property: width;
transition-duration: 1s;
transition-timing-function: linear;
max-width: 100%;
box-shadow: 0 0 2px 2px #eeffff;

@nest .completed & {
background-color: var(--state-success-color);
}
}
31 changes: 31 additions & 0 deletions packages/@sanity/base/src/preview/UploadProgressBar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react'
import PropTypes from 'prop-types'
import styles from './UploadProgressBar.css'

export default function UploadProgressBar(props) {
const {percent} = props
const classes = [
styles.root,
percent === 100 && styles.completed
]
.filter(Boolean)
.join(' ')

return (
<div className={classes}>
<div className={styles.inner}>
<div className={styles.barContainer}>
<div className={styles.bar} style={{width: `${percent}%`}} />
</div>
</div>
</div>
)
}

UploadProgressBar.propTypes = {
percent: PropTypes.number
}

UploadProgressBar.defaultProps = {
percent: 0
}
3 changes: 2 additions & 1 deletion packages/@sanity/base/src/preview/observeWithPaths.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ function listen(id) {
observer.next({type: 'welcome', documentId: id})
return getGlobalListener()
.filter(event => event.documentId === id)
.debounceTime(2000)
.debounceTime(1000)
.subscribe(observer)
})
.debounceTime(1000)
}

function fetchAllDocumentSnapshots(selections) {
Expand Down
2 changes: 1 addition & 1 deletion packages/@sanity/base/src/preview/prepareForPreview.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {get, pick, debounce, flatten, uniqBy} from 'lodash'

const pass = v => v
const PRESERVE_KEYS = ['_id', '_type']
const PRESERVE_KEYS = ['_id', '_type', '_upload']

let COLLECTED_ERRORS = {}

Expand Down
43 changes: 7 additions & 36 deletions packages/@sanity/components/src/imageinput/common/ImageSelect.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,24 @@
import PropTypes from 'prop-types'
import React from 'react'
import FileInput from 'part:@sanity/components/fileinput/default'
import readExif from '../utils/readExif'
import rotateImage from '../utils/rotateImage'
import continueWhen from '../utils/continueWhen'
import styles from './styles/ImageSelect.css'
import DropZone from 'part:@sanity/components/fileinput/dropzone'
import UploadIcon from 'part:@sanity/base/upload-icon'

// todo: investigate if we can use web workers to do the heavy work on the images here

export default class ImageInput extends React.PureComponent {
static propTypes = {
onSelect: PropTypes.func.isRequired,
className: PropTypes.string,
dropzone: PropTypes.bool
}
handleSelect = files => {
this.processingFiles = files

// Will turn `then`-callbacks into noops if user has selected new file(s) in the meantime
const maybeContinue = continueWhen(() => this.processingFiles === files)

Promise.all(Array.from(files).map(file => {
return readExif(file)
.then(exif => ({file, exif}))
.catch(error => {
// Exif read failed, we do not want to fail hard
error.message = `Exif read failed, continuing anyway: ${error.message}`
console.error(error) // eslint-disable-line no-console
return {file, exif: null}
})
.then(maybeContinue(image => {
return previewUrlWithCorrectedOrientation(image, (image.exif || {}).orientation)
.then(previewUrl => {
return {
...image,
previewUrl: previewUrl
}
})
}))
}))
.then(maybeContinue(this.props.onSelect))
handleSelect = files => {
const {onSelect} = this.props
// Todo: fix this so it just emits the raw files.
onSelect(Array.from(files).map(file => ({
previewUrl: window.URL.createObjectURL(file),
file
})))
}

render() {
Expand All @@ -60,9 +37,3 @@ export default class ImageInput extends React.PureComponent {
)
}
}

function previewUrlWithCorrectedOrientation(image, orientation) {
return (orientation && orientation !== 'top-left')
? rotateImage(image.file, orientation)
: Promise.resolve(window.URL.createObjectURL(image.file))
}
4 changes: 4 additions & 0 deletions packages/@sanity/form-builder/defs/part:@sanity.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
declare module 'part:@sanity' {
declare export default any;
}
declare module 'part:@sanity/base/preview' {
declare export default any;
}
declare module 'part:@sanity/components/buttons/default' {
declare export default any;
}
Expand Down Expand Up @@ -29,6 +32,7 @@ declare module 'part:@sanity/components/edititem/fold' {
}

declare module 'part:@sanity/components/lists/sortable' {
declare export type DragHandle = any;
declare export default any;
}

Expand Down
3 changes: 3 additions & 0 deletions packages/@sanity/form-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@
"@sanity/mutator": "^0.115.2",
"@sanity/observable": "^0.115.2",
"@sanity/schema": "^0.115.2",
"attr-accept": "^1.1.0",
"canvas-to-blob": "^0.0.0",
"classnames": "^2.2.5",
"debug": "^2.6.3",
"exif-component": "^1.0.1",
"get-random-values": "^1.2.0",
"get-window": "^1.1.1",
"humanize-list": "^1.0.1",
Expand Down

0 comments on commit 655e652

Please sign in to comment.