Selaa lähdekoodia

Merge branch 'master' of https://github.com/transloadit/uppy

Artur Paikin 7 vuotta sitten
vanhempi
commit
d615b1504f

+ 8 - 3
CHANGELOG.md

@@ -80,7 +80,9 @@ What we need to do to release Uppy 1.0
 - [x] uppy-server: storing tokens in user’s browser only (d040281cc9a63060e2f2685c16de0091aee5c7b4)
 
 ## 0.23.0
+To be released: 2017-12-21.
 
+- [ ] transloadit: add assembly result to the file state (or global state, since it might not be file-specific?), so that it can be used in `success` callback (transloadit jquery-sdk includes the whole Assembly Status JSON in a hidden field, form plugin will do a similar thing) (@goto-bus-stop)
 - [ ] dashboard: add image cropping, study https://github.com/MattKetmo/darkroomjs/, https://github.com/fengyuanchen/cropperjs #151
 - [ ] goldenretriever: confirmation before restore #443
 - [ ] core: i18n all strings + document them
@@ -116,9 +118,12 @@ Theme: 🎄 Christmas edition
 - [x] **⚠️ Breaking** core: renamed events to remove `core:` prefix, as been suggested already. So: `success`, `error`, `upload-started` and so on, and prefixed event names for plugins sometimes, like `dashboard:file-card` (#438 / @arturi) 
 - [x] **⚠️ Breaking** core: CSS class names have been altered to use `uppy-` namespace, so `.UppyDashboard-files` --> `.uppy-Dashboard-files` and so on
 - [x] **⚠️ Breaking** dashboard: added `metaFields` option, pass an array of settings for UI field objects `{ id: 'caption', name: 'Caption', placeholder: 'describe what the image is about' }` (#438 / @arturi, @goto-bus-stop)
-- [ ] form: add `Form`, a new plugin that is used in conjunction with any other acquirer, responsible for: 1. acquiring the metadata from `<form>`; 2. intercepting submit event on the form, opening Uppy dialog instead; 3. injecting any result (like from Transloadit encoding plugin) back into the form (jquery-sdk includes the whole Assembly Status JSON in a hidden field) (@arturi)
-- [x] core: fix various bugs and issues (@arturi)
-- [x] dashboard: improve Dashboard UI (@arturi)
+- [x] **⚠️ Breaking** core: deprecate `getMetaFromForm` in favor of new `Form` plugin (#407 / @arturi)
+- [x] form: add `Form`, a new plugin that is used in conjunction with any acquirer, responsible for: 1. acquiring the metadata from `<form>` when upload starts in Uppy; 2. injecting result array of succesful and failed files back into the form (#407 / @arturi)
+- [x] core: add more extensions for mimetype detection (#452 / @ifedapoolarewaju)
+- [x] docs: more docs for plugins (#456 / @goto-bus-stop)
+- [x] core: misc bugs fixes and improvements in Webcam, Dashboard, Provider and others (#451 / @arturi)
+- [x] dashboard: improved Dashboard UI (@arturi)
 - [x] uppy-server: remove pause/resume socket listeners when upload is done (@ifedapoolarewaju)
 - [x] uppy/uppy-server: remote server error handler (#446 / @ifedapoolarewaju)
 - [x] provider: dropbox thumbnail view seems not to be working (@ifedapoolarewaju)

+ 9 - 14
examples/bundled-example/index.html

@@ -12,27 +12,22 @@
         text-align: center;
       }
 
-      .MyForm {
+      #upload-form {
         max-width: 400px;
         margin: auto;
-        text-align: initial;
+        text-align: left;
       }
     </style>
     <main>
       <h1>Uppy is here</h1>
-      <button id="uppyModalOpener">Choose Files</button>
-      <button id="uppyModalOpener2">Choose Files</button>
 
-      <div class="Uppy">
-        <form class="MyForm" action="/">
-          <input type="file" />
-          <input type="checkbox" name="check_test" value="1" checked>
-          <input type="hidden" name="bla" value="12333">
-          <input type="text" name="yo" value="1">
-          <button type="submit">Upload</button>
-          <input type="submit">
-        </form>
-      </div>
+      <form id="upload-form" action="/">
+        <button type="button" id="pick-files">Pick Files</button><br>
+        True ? <input type="checkbox" name="check_test" value="1" checked><br>
+        Something: <input type="text" name="yo" value="1"><br>
+        <input type="hidden" name="bla" value="12333"><br>
+        <button type="submit">Submit</button>
+      </form>
     </main>
 
     <link href="uppy.min.css" rel="stylesheet">

+ 28 - 30
examples/bundled-example/main.js

@@ -1,10 +1,12 @@
 const Uppy = require('../../src/core')
 const Dashboard = require('../../src/plugins/Dashboard')
-const GoogleDrive = require('../../src/plugins/GoogleDrive')
-const Dropbox = require('../../src/plugins/Dropbox')
 const Instagram = require('../../src/plugins/Instagram')
+const GoogleDrive = require('../../src/plugins/GoogleDrive')
 const Webcam = require('../../src/plugins/Webcam')
 const Tus = require('../../src/plugins/Tus')
+const Form = require('../../src/plugins/Form')
+
+// const Dropbox = require('../../src/plugins/Dropbox')
 // const XHRUpload = require('../../src/plugins/XHRUpload')
 // const FileInput = require('../../src/plugins/FileInput')
 // const MetaData = require('../../src/plugins/MetaData')
@@ -43,42 +45,38 @@ const uppy = Uppy({
   // }
 })
   .use(Dashboard, {
-    trigger: '#uppyModalOpener',
-    target: '.MyForm',
-    // maxWidth: 350,
-    // maxHeight: 400,
-    inline: false,
-    disableStatusBar: false,
-    disableInformer: false,
-    getMetaFromForm: true,
-    replaceTargetContent: false,
-    hideUploadButton: false,
-    closeModalOnClickOutside: false,
-    locale: {
-      strings: { browse: 'browse' }
-    },
+    trigger: '#pick-files',
     metaFields: [
       { id: 'license', name: 'License', placeholder: 'specify license' },
       { id: 'caption', name: 'Caption', placeholder: 'describe what the image is about' }
     ]
-    // note: 'Images and video only, 300kb or less'
+    // target: '.uppy-target',
+    // inline: true,
+    // maxWidth: 500,
+    // maxHeight: 350,
+    // replaceTargetContent: true,
+    // closeModalOnClickOutside: false,
+    // note: 'Images and video only, 300kb or less',
+    // locale: {
+    //   strings: { browse: 'browse' }
+    // }
   })
-  .use(GoogleDrive, {target: Dashboard, host: 'http://localhost:3020'})
-  .use(Dropbox, {target: Dashboard, host: 'http://localhost:3020'})
-  .use(Instagram, {target: Dashboard, host: 'http://localhost:3020'})
-  .use(Webcam, {target: Dashboard})
-  .use(Tus, {endpoint: TUS_ENDPOINT, resume: true})
+  .use(GoogleDrive, { target: Dashboard, host: 'http://localhost:3020' })
+  .use(Instagram, { target: Dashboard, host: 'http://localhost:3020' })
+  .use(Webcam, { target: Dashboard })
+  .use(Tus, { endpoint: TUS_ENDPOINT })
+  .use(Form, { target: '#upload-form' })
   // .use(GoldenRetriever, {serviceWorker: true})
   .run()
 
-uppy.on('complete', ({ successful, failed }) => {
-  if (failed.length === 0) {
-    console.log('UPLOAD SUCCESSFUL!!!')
+uppy.on('complete', (result) => {
+  if (result.failed.length === 0) {
+    console.log('Upload successful 😀')
   } else {
-    console.warn('UPLOAD FAILED!!!')
+    console.warn('Upload failed 😞')
   }
-  console.log('successful files:', successful)
-  console.log('failed files:', failed)
+  console.log('successful files:', result.successful)
+  console.log('failed files:', result.failed)
 })
 
 if ('serviceWorker' in navigator) {
@@ -92,5 +90,5 @@ if ('serviceWorker' in navigator) {
     })
 }
 
-var modalTrigger = document.querySelector('#uppyModalOpener')
-if (modalTrigger) modalTrigger.click()
+// var modalTrigger = document.querySelector('#uppyModalOpener')
+// if (modalTrigger) modalTrigger.click()

+ 21 - 12
src/core/Core.js

@@ -227,10 +227,22 @@ class Uppy {
   }
 
   setMeta (data) {
-    const newMeta = Object.assign({}, this.getState().meta, data)
+    const updatedMeta = Object.assign({}, this.getState().meta, data)
+    const updatedFiles = Object.assign({}, this.getState().files)
+
+    Object.keys(updatedFiles).forEach((fileID) => {
+      updatedFiles[fileID] = Object.assign({}, updatedFiles[fileID], {
+        meta: Object.assign({}, updatedFiles[fileID].meta, data)
+      })
+    })
+
     this.log('Adding metadata:')
     this.log(data)
-    this.setState({meta: newMeta})
+
+    this.setState({
+      meta: updatedMeta,
+      files: updatedFiles
+    })
   }
 
   setFileMeta (fileID, data) {
@@ -602,13 +614,14 @@ class Uppy {
 
   /**
    * Registers listeners for all global actions, like:
-   * `file-add`, `file-remove`, `upload-progress`, `reset`
+   * `error`, `file-added`, `file-removed`, `upload-progress`
    *
    */
   actions () {
-    // this.bus.on('*', (payload) => {
-    //   console.log('emitted: ', this.event)
-    //   console.log('with payload: ', payload)
+    // const log = this.log
+    // this.on('*', function (payload) {
+    //   log(`[Core] Event: ${this.event}`)
+    //   log(payload)
     // })
 
     // stress-test re-rendering
@@ -856,10 +869,6 @@ class Uppy {
     this.iteratePlugins((plugin) => {
       plugin.uninstall()
     })
-
-    // if (this.socket) {
-    //   this.socket.close()
-    // }
   }
 
   /**
@@ -1058,8 +1067,8 @@ class Uppy {
 
     return lastStep.then(() => {
       const files = fileIDs.map((fileID) => this.getFile(fileID))
-      const successful = files.filter((file) => !file.error)
-      const failed = files.filter((file) => file.error)
+      const successful = files.filter((file) => file && !file.error)
+      const failed = files.filter((file) => file && file.error)
       this.emit('complete', { successful, failed })
 
       // Compatibility with pre-0.21

+ 0 - 10
src/core/Plugin.js

@@ -1,6 +1,5 @@
 const preact = require('preact')
 const { findDOMElement } = require('../core/Utils')
-const getFormData = require('get-form-data')
 
 /**
  * Boilerplate that all Plugins share - and should not be used
@@ -16,9 +15,6 @@ module.exports = class Plugin {
     this.uppy = uppy
     this.opts = opts || {}
 
-    // clear everything inside the target selector
-    // this.opts.replaceTargetContent = this.opts.replaceTargetContent !== undefined ? this.opts.replaceTargetContent : true
-
     this.update = this.update.bind(this)
     this.mount = this.mount.bind(this)
     this.install = this.install.bind(this)
@@ -68,12 +64,6 @@ module.exports = class Plugin {
 
       this.uppy.log(`Installing ${callerPluginName} to a DOM element`)
 
-      // attempt to extract meta from form element
-      if (this.opts.getMetaFromForm && targetElement.nodeName === 'FORM') {
-        const formMeta = getFormData(targetElement)
-        this.uppy.setMeta(formMeta)
-      }
-
       // clear everything inside the target container
       if (this.opts.replaceTargetContent) {
         targetElement.innerHTML = ''

+ 3 - 3
src/plugins/Dashboard/FileItem.js

@@ -99,9 +99,9 @@ module.exports = function fileItem (props) {
       </div>
     </div>
     <div class="uppy-DashboardItem-info">
-      <h4 class="uppy-DashboardItem-name" title="{fileName}">
+      <h4 class="uppy-DashboardItem-name" title={fileName}>
         {file.uploadURL
-          ? <a href="{file.uploadURL}" target="_blank">
+          ? <a href={file.uploadURL} target="_blank">
             {file.extension ? truncatedFileName + '.' + file.extension : truncatedFileName}
           </a>
           : file.extension ? truncatedFileName + '.' + file.extension : truncatedFileName
@@ -111,7 +111,7 @@ module.exports = function fileItem (props) {
         {file.data.size && <div class="uppy-DashboardItem-statusSize">{prettyBytes(file.data.size)}</div>}
         {file.source && <div class="uppy-DashboardItem-sourceIcon">
             {acquirers.map(acquirer => {
-              if (acquirer.id === file.source) return <span title="{props.i18n('fileSource')}: {acquirer.name}">{acquirer.icon()}</span>
+              if (acquirer.id === file.source) return <span title={`${props.i18n('fileSource')}: ${acquirer.name}`}>{acquirer.icon()}</span>
             })}
           </div>
         }

+ 0 - 4
src/plugins/Dashboard/index.js

@@ -63,7 +63,6 @@ module.exports = class Dashboard extends Plugin {
     // set default options
     const defaultOptions = {
       target: 'body',
-      getMetaFromForm: true,
       metaFields: [],
       trigger: '#uppy-select-files',
       inline: false,
@@ -440,13 +439,10 @@ module.exports = class Dashboard extends Plugin {
       handlePaste: this.handlePaste,
       showProgressDetails: this.opts.showProgressDetails,
       inline: this.opts.inline,
-      semiTransparent: this.opts.semiTransparent,
       showPanel: this.showPanel,
       hideAllPanels: this.hideAllPanels,
       log: this.uppy.log,
       i18n: this.i18n,
-      // pauseAll: this.pauseAll,
-      // resumeAll: this.resumeAll,
       addFile: this.uppy.addFile,
       removeFile: this.uppy.removeFile,
       info: this.uppy.info,

+ 0 - 1
src/plugins/DragDrop/index.js

@@ -25,7 +25,6 @@ module.exports = class DragDrop extends Plugin {
     // Default options
     const defaultOpts = {
       target: null,
-      getMetaFromForm: true,
       width: '100%',
       height: '100%',
       note: '',

+ 0 - 2
src/plugins/FileInput.js

@@ -19,8 +19,6 @@ module.exports = class FileInput extends Plugin {
     // Default options
     const defaultOptions = {
       target: null,
-      getMetaFromForm: true,
-      replaceTargetContent: false,
       allowMultipleFiles: true,
       pretty: true,
       locale: defaultLocale,

+ 97 - 0
src/plugins/Form.js

@@ -0,0 +1,97 @@
+const Plugin = require('../core/Plugin')
+const { findDOMElement } = require('../core/Utils')
+const getFormData = require('get-form-data')
+
+/**
+ * Form
+ */
+module.exports = class Form extends Plugin {
+  constructor (uppy, opts) {
+    super(uppy, opts)
+    this.type = 'acquirer'
+    this.id = 'Form'
+    this.title = 'Form'
+
+    // set default options
+    const defaultOptions = {
+      target: null,
+      resultName: 'uppyResult',
+      getMetaFromForm: true,
+      addResultToForm: true,
+      submitOnSuccess: false,
+      triggerUploadOnSubmit: false
+    }
+
+    // merge default options with the ones set by user
+    this.opts = Object.assign({}, defaultOptions, opts)
+
+    this.handleFormSubmit = this.handleFormSubmit.bind(this)
+    this.handleUploadStart = this.handleUploadStart.bind(this)
+    this.handleSuccess = this.handleSuccess.bind(this)
+    this.addResultToForm = this.addResultToForm.bind(this)
+    this.getMetaFromForm = this.getMetaFromForm.bind(this)
+  }
+
+  handleUploadStart () {
+    if (this.opts.getMetaFromForm) {
+      this.getMetaFromForm()
+    }
+  }
+
+  handleSuccess (result) {
+    if (this.opts.addResultToForm) {
+      this.addResultToForm(result)
+    }
+
+    if (this.opts.submitOnSuccess) {
+      this.form.submit()
+    }
+  }
+
+  handleFormSubmit (ev) {
+    if (this.opts.triggerUploadOnSubmit) {
+      ev.preventDefault()
+      this.uppy.upload()
+    }
+  }
+
+  addResultToForm (result) {
+    this.uppy.log('[Form] Adding result to the original form:')
+    this.uppy.log(result)
+
+    let resultInput = this.form.querySelector(`[name="${this.opts.resultName}"]`)
+    if (resultInput) {
+      resultInput.value = JSON.stringify(result)
+      return
+    }
+
+    resultInput = document.createElement('input')
+    resultInput.name = this.opts.resultName
+    resultInput.type = 'hidden'
+    resultInput.value = JSON.stringify(result)
+    this.form.appendChild(resultInput)
+  }
+
+  getMetaFromForm () {
+    const formMeta = getFormData(this.form)
+    this.uppy.setMeta(formMeta)
+  }
+
+  install () {
+    this.form = findDOMElement(this.opts.target)
+    if (!this.form || !this.form.nodeName === 'FORM') {
+      console.error('Form plugin requires a <form> target element passed in options to operate, none was found', 'error')
+      return
+    }
+
+    this.form.addEventListener('submit', this.handleFormSubmit)
+    this.uppy.on('upload', this.handleUploadStart)
+    this.uppy.on('complete', this.handleSuccess)
+  }
+
+  uninstall () {
+    this.form.removeEventListener('submit', this.handleFormSubmit)
+    this.uppy.off('upload', this.handleUploadStart)
+    this.uppy.off('complete', this.handleSuccess)
+  }
+}

+ 1 - 1
src/plugins/StatusBar/StatusBar.js

@@ -5,7 +5,7 @@ function progressDetails (props) {
   return <span>{props.totalProgress || 0}%・{props.complete} / {props.inProgress}・{props.totalUploadedSize} / {props.totalSize}・↑ {props.totalSpeed}/s・{props.totalETA}</span>
 }
 
-const ThrottledProgressDetails = throttle(progressDetails, 1000, {leading: true, trailing: true})
+const ThrottledProgressDetails = throttle(progressDetails, 500, {leading: true, trailing: true})
 
 const STATE_ERROR = 'error'
 const STATE_WAITING = 'waiting'