mirror of
https://github.com/transloadit/uppy.git
synced 2026-07-23 10:18:40 +00:00
Merge branch 'master' of github.com:transloadit/uppy
This commit is contained in:
commit
02cfe4b229
50 changed files with 1070 additions and 419 deletions
266
CHANGELOG.md
266
CHANGELOG.md
|
|
@ -53,6 +53,8 @@ Ideas that will be planned and find their way into a release at one point
|
|||
- [ ] test: add deepFreeze to test that state in not mutated anywhere by accident #320
|
||||
- [ ] audio: audio recording similar to Webcam #143
|
||||
- [ ] add typescript definitions and JSDoc everywhere? https://github.com/Microsoft/TypeScript/wiki/Type-Checking-JavaScript-Files
|
||||
- [ ] transloadit plugin: maybe add option to disable uppy server endpoint overrides
|
||||
- [ ] dragdrop: change border color when files doesn’t pass restrictions on drag https://github.com/transloadit/uppy/issues/607
|
||||
|
||||
## 1.0 Goals
|
||||
|
||||
|
|
@ -79,61 +81,89 @@ What we need to do to release Uppy 1.0
|
|||
- [x] uppy-server: security audit
|
||||
- [x] uppy-server: storing tokens in user’s browser only (d040281cc9a63060e2f2685c16de0091aee5c7b4)
|
||||
|
||||
# next
|
||||
|
||||
## 0.24.0
|
||||
|
||||
To be released: 2018-03-01.
|
||||
|
||||
- [ ] dashboard: allow minimizing the Dashboard during upload (Uppy then becomes just a tiny progress indicator) (@arturi)
|
||||
- [ ] dashboard: cancel button for any kind of uploads? currently resume/pause only for tus, and cancel for XHR (@arturi, @goto-bus-stop)
|
||||
- [ ] dashboard: cancel button for transloadit assemblies (@arturi, @goto-bus-stop)
|
||||
- [ ] dashboard: disallow removing files if `bundle: true` in XHRUpload (@arturi)
|
||||
- [ ] dashboard: optional alert `onbeforeunload` while upload is in progress, safeguarding from accidentaly navigating away from a page with an ongoing upload
|
||||
- [ ] dashboard: add image cropping, study https://github.com/MattKetmo/darkroomjs/, https://github.com/fengyuanchen/cropperjs #151
|
||||
- [ ] core: css-in-js, while keeping non-random classnames (ideally prefixed) and useful preprocessor features. also see simple https://github.com/codemirror/CodeMirror/blob/master/lib/codemirror.css (@arturi, @goto-bus-stop)
|
||||
- [ ] core: all: reset or !important styles to be immune to any environment/page, look at screenshots in #446. Maybe `postcss-safe-important`, http://cleanslatecss.com/ or https://github.com/maximkoretskiy/postcss-autoreset or increase specificity (with .uppy prefix) (@arturi)
|
||||
- [ ] url: refactor things into Provider, see comments in https://github.com/transloadit/uppy/pull/588 (@ifedapoolarewaju)
|
||||
- [ ] url: refactor things into Provider, see comments in https://github.com/transloadit/uppy/pull/588 (@ifedapoolarewaju, @arturi)
|
||||
- [ ] dashboard: option for Boolean metadata #454 (@arturi)
|
||||
- [ ] look into text-based file type icons to save space, or more icons for file types? (@nqst, @arturi)
|
||||
- [ ] core: figure out per-plugin locales and i18n strings packs #491
|
||||
- [ ] goldenretriever: confirmation before restore #443
|
||||
- [ ] goldenretriever: add “ghost” files (@arturi)
|
||||
- [ ] core: i18n all strings + document them
|
||||
|
||||
# next
|
||||
|
||||
## 0.23.0
|
||||
|
||||
To be released: 2018-02-10.
|
||||
|
||||
- [x] core: return `processing` results among with `upload` results in `success` event and `upload()` promise (#527 / @goto-bus-stop)
|
||||
- [x] 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) (#527 / @goto-bus-stop)
|
||||
- [x] transloadit: add `transloadit:assembly-executing` event (#547 / @goto-bus-stop)
|
||||
- [ ] transloadit: add error reporting (@goto-bus-stop)
|
||||
- [x] url: new plugin that imports files from urls (588 / @arturi, @ifedapoolarewaju)
|
||||
- [ ] core: fix blank preview thumbnails for .png images in Safari 10.1 #458
|
||||
- [x] core: Move limiting to different point, to fix StatusBar and other UI issues #468 (#524, #526 / @goto-bus-stop)
|
||||
- [ ] s3: rename `AWS S3` to something more general if it works with Google Cloud Storage too? See #460
|
||||
- [ ] dashboard: try adding optional whitelabel “powered by uppy.io”, maybe muted small uppy logo that gains color on hover (@nqst, @arturi)
|
||||
- [x] dashboard: restore focus after modal has been closed (@arturi)
|
||||
- [x] dashboard: refactor provider views (#554 / @arturi)
|
||||
- [x] dashboard: use more accessible tip lib: https://github.com/ghosh/microtip
|
||||
- [x] core: move preview generation to separate plugin, add queuing (#431 / @richardwillars)
|
||||
- [ ] core: warn, not error, when file cannot be added due to restrictions? (@goto-bus-stop)
|
||||
- [ ] core: update file-type
|
||||
- [ ] goldenretriever: warn, not error, when files cannot be saved by goldenretriever (@goto-bus-stop)
|
||||
- [ ] docs: quick start guide: https://community.transloadit.com/t/quick-start-guide-would-be-really-helpful/14605 (@arturi)
|
||||
- [ ] docs: on writing plugins (@goto-bus-stop)
|
||||
- [ ] docs: all useful events (@arturi)
|
||||
- [ ] core: warn, not error, when files don’t pass restrictions: Unhandled Promise Rejection when file doesn't pass restrictions #492
|
||||
- [x] webcam: mirror image preview, add option to select which camera is used to capture, try filling the whole Dashboard with webcam preview image, remove URL.createObjectURL() (#574 / @arturi, @nqst)
|
||||
- [x] xhrupload: add bundle option to send multiple files in one request (#442 / @goto-bus-stop)
|
||||
- [ ] xhrupload: emit a final `upload-progress` event in the XHRUpload plugin just before firing `upload-complete` (tus-js-client already handles this internally) (@arturi)
|
||||
- [x] uppy-server: security audit, ask @acconut
|
||||
- [ ] s3: rename `AWS S3` to something more general if it works with Google Cloud Storage too? See #460
|
||||
- [ ] dashboard: try adding optional whitelabel “powered by uppy.io”, maybe muted small uppy logo that gains color on hover (@nqst, @arturi)
|
||||
- [ ] transloadit: add error reporting (@goto-bus-stop)
|
||||
- [ ] uppy-server: benchmarks / stress test, large file, uppy-server / tus / S3 (10 GB)
|
||||
- [ ] meta: add https://twin.github.io/better-file-uploads-with-shrine-direct-uploads to the next blog post
|
||||
- [ ] uppy-server: document docker image setup for uppy-server (@ifedapoolarewaju)
|
||||
- [x] meta: automatically host releases on edgly and use that as our main CDN (#558 / @kvz)
|
||||
- [x] uppy-server: pass response from uppy-server upload’s endpoint (@ifedapoolarewaju)
|
||||
- [x] uppy-server: support localhost urls as endpoints (@ifedapoolarewaju)
|
||||
- [x] uppy-server: schedule job to delete stale upload files (@ifedapoolarewaju)
|
||||
- [x] uppy-server: detect file upload size from the server (@ifedapoolarewaju)
|
||||
- [x] uppy-server: load standalone server options via config path (@ifedapoolarewaju)
|
||||
- [x] uppy-server: fix circular json stringify error (@ifedapoolarewaju)
|
||||
- [ ] xhrupload: emit a final `upload-progress` event in the XHRUpload plugin just before firing `upload-complete` (tus-js-client already handles this internally) (@arturi)
|
||||
- [x] s3: fix xhr response handlers (#625, @goto-bus-stop)
|
||||
- [ ] test: add typescript with JSDoc (@arturi)
|
||||
|
||||
## 0.23.0
|
||||
|
||||
Released: 2018-02-11.
|
||||
|
||||
- core: Allow plugins to add data to result object. Return `processing` results among with `upload` results in `complete` event and `upload()` promise (#527 / @goto-bus-stop)
|
||||
- core: Move limiting to different point, to fix StatusBar and other UI issues #468 (#524, #526 / @goto-bus-stop)
|
||||
- core: Add uploadID to complete event (#569 / @richardwillars)
|
||||
- core: Allow chanining after .on() and .off() to improve ergonomics (#597 / @arturi)
|
||||
- core: Allow user to override sass variables (#555 / @chao)
|
||||
- core: Move preview generation to separate plugin, add queuing (#431 / @richardwillars)
|
||||
- core: Third-party extension, uppy-store-ngrx https://github.com/rimlin/uppy-store-ngrx/ (#532 / @rimlin)
|
||||
- core: Warn, not error, when file cannot be added due to restrictions? (#604, #492 / @goto-bus-stop)
|
||||
- dashboard: Add more i18n strings (#565 / @arturi)
|
||||
- dashboard: Fix modal and page scroll (#564 / @arturi)
|
||||
- dashboard: Refactor provider views (#554 / @arturi)
|
||||
- dashboard: Restore focus after modal has been closed (#536 / @arturi)
|
||||
- dashboard: Use empty input value so same file can be selected multiple times (@arturi / #534)
|
||||
- dashboard: Use more accessible tip lib microtip (#536 / @arturi)
|
||||
- docs: Add PHP snippets to XHRUpload docs (#567 / @goto-bus-stop)
|
||||
- meta: Added instruction to fork the repo first (#512 / muhammadInam)
|
||||
- meta: Automatically host releases on edgly and use that as our main CDN (#558 / @kvz)
|
||||
- meta: Dependency version updates (#523 / @goto-bus-stop)
|
||||
- meta: Remove unused files from published package (#586 / @goto-bus-stop)
|
||||
- s3: Respect `limit` option for upload parameter requests too; fix isXml() check when no content-type is available (#545, #544, #528 / @goto-bus-stop)
|
||||
- statusbar: Fix status text still showing when statusbar is hidden (#525 / @goto-bus-stop)
|
||||
- test: Alter jest testPathPattern to current dir, add chai (#583 / @arturi)
|
||||
- thumbnail: Add thumbnail generation plugin (#461 / @richardwillars)
|
||||
- thumbnail: Fix blank preview thumbnails for images in Safari; use slightly different stap scaling (#458, #584 / @arturi)
|
||||
- transloadit: Add `transloadit:assembly-executing` event (#547 / @goto-bus-stop)
|
||||
- transloadit: Add assembly results to to the `complete` callback (#527 / @goto-bus-stop)
|
||||
- transloadit: Easily pass form fields (#593 / @goto-bus-stop)
|
||||
- tus: `resume: false` — don’t store url (@arturi / #507)
|
||||
- uppy-server: Detect file upload size from the server (@ifedapoolarewaju)
|
||||
- uppy-server: Fix circular json stringify error (@ifedapoolarewaju)
|
||||
- uppy-server: Load standalone server options via config path (@ifedapoolarewaju)
|
||||
- uppy-server: Pass response from uppy-server upload’s endpoint (#591 / @ifedapoolarewaju)
|
||||
- uppy-server: Schedule job to delete stale upload files (@ifedapoolarewaju)
|
||||
- uppy-server: Security audit, ask @acconut
|
||||
- uppy-server: Support localhost urls as endpoints (@ifedapoolarewaju)
|
||||
- url: New plugin that imports files from urls (#588 / @arturi, @ifedapoolarewaju)
|
||||
- webcam: Font styling for Webcam option (#509 / @muhammadInam)
|
||||
- webcam: Mirror image preview, add option to select which camera is used to capture, try filling the whole Dashboard with webcam preview image, remove URL.createObjectURL() (#574 / @arturi, @nqst)
|
||||
- website: Add Transloadit example to website (#603 / @arturi)
|
||||
- website: Doc fixes (#563 / @arturi)
|
||||
- website: Improve the Contributing guide (#578 / @arturi)
|
||||
- xhrupload: Add bundle option to send multiple files in one request (#442 / @goto-bus-stop)
|
||||
- xhrupload: Prevent files from being uploaded multiple times in separate uploads (#552 / @richardwillars)
|
||||
- xhrupload: Refactor response and error handling (#591 / @goto-bus-stop, @arturi, @ifedapoolarewaju)
|
||||
|
||||
## 0.22.1
|
||||
|
||||
|
|
@ -197,127 +227,127 @@ Released: 2017-12-10.
|
|||
|
||||
Released: 2017-11-14.
|
||||
|
||||
- [x] accessibility: add tabindex="0" to buttons and tabs, aria-labels, focus (#414 / @arturi)
|
||||
- [x] core: allow setting custom `id` for plugins to allow a plugin to be used multiple times (#418 / @arturi)
|
||||
- [x] core: do not check isPreviewSupported for unknown filetypes (#417 / @sadovnychyi)
|
||||
- [x] core: refactor `uppy-base` (#382 / @goto-bus-stop)
|
||||
- [x] core: remove functions from state object (#408 / @goto-bus-stop)
|
||||
- [x] core: return `{ successful, failed }` from `uppy.upload()` (#404 / @goto-bus-stop)
|
||||
- [x] core: update state with error messages rather than error objects (#406 / @richardwillars)
|
||||
- [x] core: use `tinyify` for the unpkg bundle. (#371 / @goto-bus-stop)
|
||||
- [x] dashboard: Fix pasting files, default `image` file name, add type to meta, file type refactor (#395 / @arturi)
|
||||
- [x] dragdrop: Fix of the .uppy-DragDrop-inner spacing on small screens (#405 / @nqst)
|
||||
- [x] react: fix `uppy` PropType, closes (#416 / @goto-bus-stop)
|
||||
- [x] s3: automatically wrap XHRUpload. **Users should remove `.use(XHRUpload)` when using S3.** (#408 / @goto-bus-stop)
|
||||
- [x] test: refactored end-to-end tests to not use website, switched to Webdriver.io, added tests for Edge, Safari, Android and iOS (#410 / @arturi)
|
||||
- [x] tus: Rename Tus10 → Tus (#285 / @goto-bus-stop)
|
||||
- [x] uppy-serer: mask sensitive data from request logs (@ifedapoolarewaju)
|
||||
- [x] uppy-server: add request body validators (@ifedapoolarewaju)
|
||||
- [x] uppy-server: migrate dropbox to use v2 API (#386 / @ifedapoolarewaju)
|
||||
- [x] uppy-server: store tokens in user’s browser only (@ifedapoolarewaju)
|
||||
- [x] webcam: only show the webcam tab when browser support is available (media recorder API) (#421 / @arturi, @goto-bus-stop)
|
||||
- [x] webcam: simplify and refactor webcam plugin (modern browser APIs only) (#382 / @goto-bus-stop)
|
||||
- [x] xhrupload: set a timeout in the onprogress event handler to detect stale network (#378 / @goto-bus-stop)
|
||||
- [x] uppy-server: allow flexible whitelist endpoint protocols (@ifedapoolarewaju)
|
||||
- accessibility: add tabindex="0" to buttons and tabs, aria-labels, focus (#414 / @arturi)
|
||||
- core: allow setting custom `id` for plugins to allow a plugin to be used multiple times (#418 / @arturi)
|
||||
- core: do not check isPreviewSupported for unknown filetypes (#417 / @sadovnychyi)
|
||||
- core: refactor `uppy-base` (#382 / @goto-bus-stop)
|
||||
- core: remove functions from state object (#408 / @goto-bus-stop)
|
||||
- core: return `{ successful, failed }` from `uppy.upload()` (#404 / @goto-bus-stop)
|
||||
- core: update state with error messages rather than error objects (#406 / @richardwillars)
|
||||
- core: use `tinyify` for the unpkg bundle. (#371 / @goto-bus-stop)
|
||||
- dashboard: Fix pasting files, default `image` file name, add type to meta, file type refactor (#395 / @arturi)
|
||||
- dragdrop: Fix of the .uppy-DragDrop-inner spacing on small screens (#405 / @nqst)
|
||||
- react: fix `uppy` PropType, closes (#416 / @goto-bus-stop)
|
||||
- s3: automatically wrap XHRUpload. **Users should remove `.use(XHRUpload)` when using S3.** (#408 / @goto-bus-stop)
|
||||
- test: refactored end-to-end tests to not use website, switched to Webdriver.io, added tests for Edge, Safari, Android and iOS (#410 / @arturi)
|
||||
- tus: Rename Tus10 → Tus (#285 / @goto-bus-stop)
|
||||
- uppy-serer: mask sensitive data from request logs (@ifedapoolarewaju)
|
||||
- uppy-server: add request body validators (@ifedapoolarewaju)
|
||||
- uppy-server: migrate dropbox to use v2 API (#386 / @ifedapoolarewaju)
|
||||
- uppy-server: store tokens in user’s browser only (@ifedapoolarewaju)
|
||||
- webcam: only show the webcam tab when browser support is available (media recorder API) (#421 / @arturi, @goto-bus-stop)
|
||||
- webcam: simplify and refactor webcam plugin (modern browser APIs only) (#382 / @goto-bus-stop)
|
||||
- xhrupload: set a timeout in the onprogress event handler to detect stale network (#378 / @goto-bus-stop)
|
||||
- uppy-server: allow flexible whitelist endpoint protocols (@ifedapoolarewaju)
|
||||
|
||||
## 0.20.3
|
||||
|
||||
Released: 2017-10-18.
|
||||
|
||||
- [x] Start a completely new upload when retrying. (#390 / @goto-bus-stop)
|
||||
- [x] dashboard: Show errors that occurred during processing on the file items. (#391 / @goto-bus-stop)
|
||||
- [x] transloadit: Mark files as having errored if their assembly fails. (#392 / @goto-bus-stop)
|
||||
- [x] core: Clear file upload progress when an upload starts. (#393 / @goto-bus-stop)
|
||||
- [x] tus: Clean up `tus.Upload` instance and events when an upload starts, finishes, or fails. (#390 / @goto-bus-stop)
|
||||
- Start a completely new upload when retrying. (#390 / @goto-bus-stop)
|
||||
- dashboard: Show errors that occurred during processing on the file items. (#391 / @goto-bus-stop)
|
||||
- transloadit: Mark files as having errored if their assembly fails. (#392 / @goto-bus-stop)
|
||||
- core: Clear file upload progress when an upload starts. (#393 / @goto-bus-stop)
|
||||
- tus: Clean up `tus.Upload` instance and events when an upload starts, finishes, or fails. (#390 / @goto-bus-stop)
|
||||
|
||||
## 0.20.2
|
||||
|
||||
Released: 2017-10-11.
|
||||
|
||||
- [x] docs: fix `getMetaFromForm` documentation (@arturi)
|
||||
- [x] core: fix generating thumbnails for images with transparent background (#380 / @goto-bus-stop)
|
||||
- [x] transloadit: use Translator class for localised strings (#383 / @goto-bus-stop)
|
||||
- [x] goldenretriever: don't crash when required server-side (#384 / @goto-bus-stop)
|
||||
- docs: fix `getMetaFromForm` documentation (@arturi)
|
||||
- core: fix generating thumbnails for images with transparent background (#380 / @goto-bus-stop)
|
||||
- transloadit: use Translator class for localised strings (#383 / @goto-bus-stop)
|
||||
- goldenretriever: don't crash when required server-side (#384 / @goto-bus-stop)
|
||||
|
||||
## 0.20.1
|
||||
|
||||
Released: 2017-10-05.
|
||||
|
||||
- [x] redux: add plugin for syncing uppy state with a Redux store (#376 / @richardwillars)
|
||||
- redux: add plugin for syncing uppy state with a Redux store (#376 / @richardwillars)
|
||||
|
||||
## 0.20.0
|
||||
|
||||
Released: 2017-10-03.
|
||||
Theme: React and Retry
|
||||
|
||||
- [x] core: retry/error when upload can’t start or fails (offline, connection lost, wrong endpoint); add error in file progress state, UI, question mark button (#307 / @arturi)
|
||||
- [x] core: support for retry in Tus plugin (#307 / @arturi)
|
||||
- [x] core: support for retry in XHRUpload plugin (#307 / @arturi)
|
||||
- [x] core: Add support for Redux DevTools via a plugin (#373 / @arturi)
|
||||
- [x] core: improve and merge the React PR (#170 / @goto-bus-stop, @arturi)
|
||||
- [x] core: improve core.log method, add timestamps (#372 / @arturi)
|
||||
- [x] dragdrop: redesign, add note, width/height options, arrow icon (#374 / @arturi)
|
||||
- [x] uploaders: upload resolution changes, followup to #323 (#347 / @goto-bus-stop)
|
||||
- [x] uploaders: issue warning when no uploading plugins are used (#372 / @arturi)
|
||||
- [x] core: fix `replaceTargetContent` and add tests for `Plugin` (#354 / @gavboulton)
|
||||
- [x] goldenretriever: Omit completed uploads from saved file state—previously, when an upload was finished and the user refreshed the page, all the finished files would still be there because we saved the entire list of files. Changed this to only store files that are part of an in-progress upload, or that have yet to be uploaded (#358, #324 / @goto-bus-stop)
|
||||
- [x] goldenretriever: Remove files from cache when upload finished—this uses the deleteBlobs function when core:success fires (#358, #324 / @goto-bus-stop)
|
||||
- [x] goldenretriever: add a timestamp to cached blobs, and to delete old blobs on boot (#358, #324 / @goto-bus-stop)
|
||||
- [x] s3: have some way to configure content-disposition for uploads, see #243 (@goto-bus-stop)
|
||||
- [x] core: move `setPluginState` and add `getPluginState` to `Plugin` class (#363 / @goto-bus-stop)
|
||||
- core: retry/error when upload can’t start or fails (offline, connection lost, wrong endpoint); add error in file progress state, UI, question mark button (#307 / @arturi)
|
||||
- core: support for retry in Tus plugin (#307 / @arturi)
|
||||
- core: support for retry in XHRUpload plugin (#307 / @arturi)
|
||||
- core: Add support for Redux DevTools via a plugin (#373 / @arturi)
|
||||
- core: improve and merge the React PR (#170 / @goto-bus-stop, @arturi)
|
||||
- core: improve core.log method, add timestamps (#372 / @arturi)
|
||||
- dragdrop: redesign, add note, width/height options, arrow icon (#374 / @arturi)
|
||||
- uploaders: upload resolution changes, followup to #323 (#347 / @goto-bus-stop)
|
||||
- uploaders: issue warning when no uploading plugins are used (#372 / @arturi)
|
||||
- core: fix `replaceTargetContent` and add tests for `Plugin` (#354 / @gavboulton)
|
||||
- goldenretriever: Omit completed uploads from saved file state—previously, when an upload was finished and the user refreshed the page, all the finished files would still be there because we saved the entire list of files. Changed this to only store files that are part of an in-progress upload, or that have yet to be uploaded (#358, #324 / @goto-bus-stop)
|
||||
- goldenretriever: Remove files from cache when upload finished—this uses the deleteBlobs function when core:success fires (#358, #324 / @goto-bus-stop)
|
||||
- goldenretriever: add a timestamp to cached blobs, and to delete old blobs on boot (#358, #324 / @goto-bus-stop)
|
||||
- s3: have some way to configure content-disposition for uploads, see #243 (@goto-bus-stop)
|
||||
- core: move `setPluginState` and add `getPluginState` to `Plugin` class (#363 / @goto-bus-stop)
|
||||
|
||||
## 0.19.1
|
||||
|
||||
Released: 2017-09-20.
|
||||
|
||||
- [x] goldenretriever: fix restorefiles with id (#351 / @arturi)
|
||||
- [x] goldenretriever: Clean up blobs that are not related to a file in state (#349 / @goto-bus-stop)
|
||||
- [x] core: set the newState before emiting `core:state-update` (#341 / @sunil-shrestha, @arturi)
|
||||
- [x] docs: Document StatusBar plugin (#350 / @goto-bus-stop)
|
||||
- goldenretriever: fix restorefiles with id (#351 / @arturi)
|
||||
- goldenretriever: Clean up blobs that are not related to a file in state (#349 / @goto-bus-stop)
|
||||
- core: set the newState before emiting `core:state-update` (#341 / @sunil-shrestha, @arturi)
|
||||
- docs: Document StatusBar plugin (#350 / @goto-bus-stop)
|
||||
|
||||
## 0.19.0
|
||||
|
||||
Released: 2017-09-15.
|
||||
Theme: Tests and better APIs
|
||||
|
||||
- [x] goldenretriever: allow passing options to `IndexedDbStore` (#339 / sunil-shrestha)
|
||||
- [x] core: add Uppy instance ID option, namespace serviceWorker action types, add example using multiple Uppy instances with Goldenretriever (#333 / @goto-bus-stop)
|
||||
- [x] core: fix `calculateTotalProgress` - NaN (#342 / @arturi)
|
||||
- [x] core: fix and refactor restrictions (#345 / @arturi)
|
||||
- [x] core: Better `generateFileID` (#330 / @arturi)
|
||||
- [x] core: improve `isOnline()` (#319 / @richardwillars)
|
||||
- [x] core: remove unused bootstrap styles (#329 / @arturi)
|
||||
- [x] core: experiment with yo-yo --> preact and picodom (#297 / @arturi)
|
||||
- [x] dashboard: fix FileItem source icon position and copy (@arturi)
|
||||
- [x] dashboard: expose and document the show/hide/isOpen API (@arturi)
|
||||
- [x] dashboard: allow multiple `triggers` of the same class `.open-uppy` (#328 / @arturi)
|
||||
- [x] plugins: add `aria-hidden` to all SVG icons for accessibility (#4e808ca3d26f06499c58bb77abbf1c3c2b510b4d / @arturi)
|
||||
- [x] core: Handle sync returns and throws in possibly-async function options (#315 / @goto-bus-stop)
|
||||
- [x] core: switch to Jest tests, add more tests for Core and Utils (#310 / @richardwillars)
|
||||
- [x] website: Minify bundle for `disc` (#332 / @goto-bus-stop)
|
||||
- [x] transloadit: remove `this.state` getter (#331 / @goto-bus-stop)
|
||||
- [x] server: option to define valid upload urls (@ifedapoolarewaju)
|
||||
- [x] server: more automated tests (@ifedapoolarewaju)
|
||||
- goldenretriever: allow passing options to `IndexedDbStore` (#339 / sunil-shrestha)
|
||||
- core: add Uppy instance ID option, namespace serviceWorker action types, add example using multiple Uppy instances with Goldenretriever (#333 / @goto-bus-stop)
|
||||
- core: fix `calculateTotalProgress` - NaN (#342 / @arturi)
|
||||
- core: fix and refactor restrictions (#345 / @arturi)
|
||||
- core: Better `generateFileID` (#330 / @arturi)
|
||||
- core: improve `isOnline()` (#319 / @richardwillars)
|
||||
- core: remove unused bootstrap styles (#329 / @arturi)
|
||||
- core: experiment with yo-yo --> preact and picodom (#297 / @arturi)
|
||||
- dashboard: fix FileItem source icon position and copy (@arturi)
|
||||
- dashboard: expose and document the show/hide/isOpen API (@arturi)
|
||||
- dashboard: allow multiple `triggers` of the same class `.open-uppy` (#328 / @arturi)
|
||||
- plugins: add `aria-hidden` to all SVG icons for accessibility (#4e808ca3d26f06499c58bb77abbf1c3c2b510b4d / @arturi)
|
||||
- core: Handle sync returns and throws in possibly-async function options (#315 / @goto-bus-stop)
|
||||
- core: switch to Jest tests, add more tests for Core and Utils (#310 / @richardwillars)
|
||||
- website: Minify bundle for `disc` (#332 / @goto-bus-stop)
|
||||
- transloadit: remove `this.state` getter (#331 / @goto-bus-stop)
|
||||
- server: option to define valid upload urls (@ifedapoolarewaju)
|
||||
- server: more automated tests (@ifedapoolarewaju)
|
||||
|
||||
## 0.18.1
|
||||
|
||||
Released: 2017-09-05.
|
||||
Note: this version was released as a `@next` npm tag to unblock some users.
|
||||
|
||||
- [x] core: gradually resize image previews #275 (@goto-bus-stop)
|
||||
- [x] informer: support “explanations”, a (?) button that shows more info on hover / click (#292 / @arturi)
|
||||
- [x] fix webcam video recording (@goto-bus-stop)
|
||||
- [x] bundle: add missing plugins (s3, statusbar, restoreFiles) to unpkg bundle (#301 / @goto-bus-stop)
|
||||
- [x] xhrupload: Use error messages from the endpoint (#305 / @goto-bus-stop)
|
||||
- [x] dashboard: prevent submitting outer form when pressing enter key while editing metadata (#306 / @goto-bus-stop)
|
||||
- [x] dashboard: save metadata edits when pressing enter key (#308 / @arturi)
|
||||
- [x] transloadit: upload to S3, then import into :tl: assembly using `/add_file?s3url=${url}` (#280 / @goto-bus-stop)
|
||||
- [x] transloadit: add `alwaysRunAssembly` option to run assemblies when no files are uploaded (#290 / @goto-bus-stop)
|
||||
- [x] core: use `iteratePlugins` inside `updateAll` (#312 / @richardwillars)
|
||||
- [x] core: improve error when plugin does not have ID (#309 / @richardwillars)
|
||||
- [x] tus: Clear stored `uploadUrl` on `uppy.resetProgress()` call (#314 / @goto-bus-stop)
|
||||
- [x] website: simplify examples and code samples, prevent sidebar subheading links anywhere but in docs (@arturi)
|
||||
- [x] website: group plugin docs together in the sidebar (@arturi)
|
||||
- core: gradually resize image previews #275 (@goto-bus-stop)
|
||||
- informer: support “explanations”, a (?) button that shows more info on hover / click (#292 / @arturi)
|
||||
- fix webcam video recording (@goto-bus-stop)
|
||||
- bundle: add missing plugins (s3, statusbar, restoreFiles) to unpkg bundle (#301 / @goto-bus-stop)
|
||||
- xhrupload: Use error messages from the endpoint (#305 / @goto-bus-stop)
|
||||
- dashboard: prevent submitting outer form when pressing enter key while editing metadata (#306 / @goto-bus-stop)
|
||||
- dashboard: save metadata edits when pressing enter key (#308 / @arturi)
|
||||
- transloadit: upload to S3, then import into :tl: assembly using `/add_file?s3url=${url}` (#280 / @goto-bus-stop)
|
||||
- transloadit: add `alwaysRunAssembly` option to run assemblies when no files are uploaded (#290 / @goto-bus-stop)
|
||||
- core: use `iteratePlugins` inside `updateAll` (#312 / @richardwillars)
|
||||
- core: improve error when plugin does not have ID (#309 / @richardwillars)
|
||||
- tus: Clear stored `uploadUrl` on `uppy.resetProgress()` call (#314 / @goto-bus-stop)
|
||||
- website: simplify examples and code samples, prevent sidebar subheading links anywhere but in docs (@arturi)
|
||||
- website: group plugin docs together in the sidebar (@arturi)
|
||||
|
||||
## 0.18.0
|
||||
|
||||
|
|
|
|||
56
README.md
56
README.md
|
|
@ -64,7 +64,7 @@ $ npm install uppy --save
|
|||
|
||||
We recommend installing from npm and then using a module bundler such as [Webpack](http://webpack.github.io/), [Browserify](http://browserify.org/) or [Rollup.js](http://rollupjs.org/).
|
||||
|
||||
Add CSS [uppy.min.css](https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.min.css), either to `<head>` of your HTML page or include in JS, if your bundler of choice supports it — transforms and plugins are available for Browserify and Webpack.
|
||||
Add CSS [uppy.min.css](https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.min.css), either to `<head>` of your HTML page or include in JS, if your bundler of choice supports it — transforms and plugins are available for Browserify and Webpack.
|
||||
|
||||
Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object.
|
||||
|
||||
|
|
@ -73,12 +73,12 @@ Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly
|
|||
1\. Add a script to the bottom of `<body>`:
|
||||
|
||||
``` html
|
||||
<script src="https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.min.js"></script>
|
||||
<script src="https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.min.js"></script>
|
||||
```
|
||||
|
||||
2\. Add CSS to `<head>`:
|
||||
``` html
|
||||
<link href="https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.min.css" rel="stylesheet">
|
||||
<link href="https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.min.css" rel="stylesheet">
|
||||
```
|
||||
|
||||
3\. Initialize:
|
||||
|
|
@ -106,6 +106,7 @@ Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly
|
|||
- `Tus` — resumable uploads via the open [tus](http://tus.io) standard
|
||||
- `XHRUpload` — regular uploads for any backend out there (like Apache, Nginx)
|
||||
- `Transloadit` — support for [Transloadit](http://transloadit.com)’s robust file uploading and encoding backend
|
||||
- `AwsS3` — upload to AWS S3 (also works for Google Cloud)
|
||||
- `Dashboard` — universal UI with previews, progress bars, metadata editor and all the cool stuff
|
||||
- `DragDrop` — plain and simple drag and drop area
|
||||
- `FileInput` — even plainer “select files” button
|
||||
|
|
@ -113,11 +114,10 @@ Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly
|
|||
- `StatusBar` — more detailed progress, pause/resume/cancel buttons, percentage, speed, uploaded/total sizes (included by default with `Dashboard`)
|
||||
- `Informer` — send notifications like “smile” before taking a selfie or “upload failed” when all is lost (also included by default with `Dashboard`)
|
||||
- `GoldenRetriever` — restores files after a browser crash, like it’s nothing
|
||||
- `ThumbnailGenerator` — generates image previews (included by default with `Dashboard`)
|
||||
- `Form` — collects metadata from `<form>` right before an Uppy upload, then optionally appends results back to the form
|
||||
- `ReduxDevTools` — for your emerging [time traveling](https://github.com/gaearon/redux-devtools) needs
|
||||
- `GoogleDrive` — select files from [Google Drive](https://www.google.com/drive/)
|
||||
- `Dropbox` — select files from [Dropbox](https://www.dropbox.com/)
|
||||
- `Instagram` — you guessed right — select files from [Instagram](https://www.instagram.com/)
|
||||
- `GoogleDrive`, `Dropbox`, `Instagram`, `Url` — select files from [Google Drive](https://www.google.com/drive/), [Dropbox](https://www.dropbox.com/), [Instagram](https://www.instagram.com/) and direct urls from anywhere on the web. Note that[`uppy-server`](https://github.com/transloadit/uppy-server) is needed for these.
|
||||
- `Webcam` — snap and record those selfies 📷
|
||||
|
||||
## Browser Support
|
||||
|
|
@ -130,25 +130,43 @@ We aim to support IE10+ and recent versions of Safari, Edge, Chrome, Firefox, an
|
|||
|
||||
## FAQ
|
||||
|
||||
### React support?
|
||||
### Why not just use `<input type="file">`?
|
||||
|
||||
Yep, we have Uppy React components, please see [Uppy React docs](https://uppy.io/docs/react/).
|
||||
Having no JavaScript beats having a lot of it, so that’s a fair question! Running an uploading & encoding business for ten years though we found that in cases, the file input leaves some to be desired:
|
||||
|
||||
### Can I use it with Rails/Node/Go/PHP?
|
||||
- We received complaints about broken uploads and found that resumable uploads are important, especially for big files and to be inclusive towards people on poorer connections (we also launched [tus.io](https://tus.io) to attack that problem). Uppy uploads can survive network outages and browser crashes or accidental navigate-aways.
|
||||
- Uppy supports editing meta information before uploading (and e.g. cropping is planned).
|
||||
- There’s the situation where people are using their mobile devices and want to upload on the go, but they have their picture on Instagram, files in Dropbox, or just a plain file url from anywhere on the open web. Uppy allows to pick files from those and push it to the destination without downloading it to your mobile device first.
|
||||
- Accurate upload progress reporting is an issue on many platforms.
|
||||
- Some file validation — size, type, number of files — can be done on the client with Uppy.
|
||||
- Uppy integrates webcam support, in case your users want to upload a picture/video/audio that does not exist yet :)
|
||||
- A larger drag & drop surface can be pleasant to work with. Some people also like that you can control the styling, language, etc.
|
||||
- Uppy is aware of encoding backends. Often after an upload, the server needs to rotate, detect faces, optimize for iPad, or what have you. Uppy can track progress of this and report back to the user in different ways.
|
||||
- Sometimes you might want your uploads to happen while you continue to interact on the same single page.
|
||||
|
||||
Yes, whatever you want on the backend will work with `XHRUpload` plugin, since it just does a `POST` or `PUT` request. If you want resumability with the Tus plugin, use [one of the tus server implementations](https://tus.io/implementations.html) 👌🏼
|
||||
|
||||
### Do I need to install special service/server for it?
|
||||
|
||||
Nope, as mentioned previously, the `XHRUpload` plugin is old-school and just works with everything. However, you need [`uppy-server`](https://github.com/transloadit/uppy-server) if you’d like your users to be able to pick files from Instagram, Google Drive or Dropbox (more services coming). And you can add [tus](http://tus.io) if you want resumability.
|
||||
|
||||
### Does Uppy support S3 uploads?
|
||||
|
||||
Yes, there is an S3 plugin, check out the [docs](https://uppy.io/docs/aws-s3/) for more!
|
||||
Not all apps need all of these features. A `<input type="file">` is fine in many situations. But these were a few things that our customers hit / asked about enough to spark us to develop Uppy.
|
||||
|
||||
### Why is all this goodness free?
|
||||
|
||||
Transloadit's team is small and we have a shared ambition to make a living from open source. By giving away projects like [tus.io](https://tus.io) and [Uppy](https://uppy.io), we're hoping to advance the state of the art, make life a tiny little bit better for everyone, and in doing so have rewarding jobs and get some eyes on our commercial service: [a content ingestion & processing platform](https://transloadit.com). Our thinking is that if just a fraction of our open source userbase can see the appeal of hosted versions straight from the source, that could already be enough to sustain our work. So far this is working out! We're able to dedicate 80% of our time to open source and haven't gone bankrupt just yet :D
|
||||
Transloadit’s team is small and we have a shared ambition to make a living from open source. By giving away projects like [tus.io](https://tus.io) and [Uppy](https://uppy.io),we’re hoping to advance the state of the art, make life a tiny little bit better for everyone, and in doing so have rewarding jobs and get some eyes on our commercial service: [a content ingestion & processing platform](https://transloadit.com).
|
||||
|
||||
Our thinking is that if just a fraction of our open source userbase can see the appeal of hosted versions straight from the source, that could already be enough to sustain our work. So far this is working out! We’re able to dedicate 80% of our time to open source and haven’t gone bankrupt just yet :D
|
||||
|
||||
### Does Uppy support React?
|
||||
|
||||
Yep, we have Uppy React components, please see [Uppy React docs](https://uppy.io/docs/react/).
|
||||
|
||||
### Does Uppy support S3 uploads?
|
||||
|
||||
Yes, there is an S3 plugin, please check out the [docs](https://uppy.io/docs/aws-s3/) for more.
|
||||
|
||||
### Do I need to install special service/server for Uppy? Can I use it with Rails/Node/Go/PHP?
|
||||
|
||||
Yes, whatever you want on the backend will work with `XHRUpload` plugin, since it just does a `POST` or `PUT` request. Here’s a [PHP backend example](https://uppy.io/docs/xhrupload/#Uploading-to-a-PHP-Server).
|
||||
|
||||
If you want resumability with the Tus plugin, use [one of the tus server implementations](https://tus.io/implementations.html) 👌🏼
|
||||
|
||||
And you’ll need [`uppy-server`](https://github.com/transloadit/uppy-server) if you’d like your users to be able to pick files from Instagram, Google Drive, Dropbox or via direct urls (with more services coming).
|
||||
|
||||
## Contributions are welcome
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
# - Checks if a tag is being built (on Travis - otherwise opts to continue execution regardless)
|
||||
# - Installs AWS CLI if needed
|
||||
# - Assumed a fully built uppy is in root dir (unless a specific tag was specified, then it's fetched from npm)
|
||||
# - Runs npm pack, and stores files to e.g. https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.css
|
||||
# - Runs npm pack, and stores files to e.g. https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.css
|
||||
# - Uses local package by default, if [version] argument was specified, takes package from npm
|
||||
#
|
||||
# Run as:
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ const uppy = Uppy({
|
|||
trigger: '#pick-files',
|
||||
metaFields: [
|
||||
{ id: 'license', name: 'License', placeholder: 'specify license' },
|
||||
{ id: 'caption', name: 'Caption', placeholder: 'describe what the image is about' }
|
||||
{ id: 'caption', name: 'Caption', placeholder: 'add caption' }
|
||||
]
|
||||
// target: '.uppy-target',
|
||||
// inline: true,
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
<title></title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.min.css" rel="stylesheet">
|
||||
<link href="https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<button id="uppyModalOpener">Open Modal</button>
|
||||
<script src="https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.min.js"></script>
|
||||
<script src="https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.min.js"></script>
|
||||
<script>
|
||||
const uppy = Uppy.Core({debug: true, autoProceed: false})
|
||||
.use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
<title></title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.min.css" rel="stylesheet">
|
||||
<link href="https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<button id="uppyModalOpener">Open Modal</button>
|
||||
<script src="https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.min.js"></script>
|
||||
<script src="https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.min.js"></script>
|
||||
<script>
|
||||
const uppy = Uppy.Core({debug: true, autoProceed: false})
|
||||
.use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })
|
||||
|
|
|
|||
39
package-lock.json
generated
39
package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "uppy",
|
||||
"version": "0.22.5",
|
||||
"version": "0.23.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
@ -10171,6 +10171,12 @@
|
|||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
|
||||
"dev": true
|
||||
},
|
||||
"json3": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz",
|
||||
"integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=",
|
||||
"dev": true
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
|
||||
|
|
@ -15185,6 +15191,37 @@
|
|||
"function-bind": "1.1.0"
|
||||
}
|
||||
},
|
||||
"string.prototype.padstart": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.padstart/-/string.prototype.padstart-3.0.0.tgz",
|
||||
"integrity": "sha1-W8+tOfRkm7LQMSkuGbzwtRDUskI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"define-properties": "1.1.2",
|
||||
"es-abstract": "1.7.0",
|
||||
"function-bind": "1.1.0"
|
||||
}
|
||||
},
|
||||
"string.prototype.trimleft": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.0.0.tgz",
|
||||
"integrity": "sha1-aLaqjhYsaoDnbjqKDC50cYbicf8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"define-properties": "1.1.2",
|
||||
"function-bind": "1.1.0"
|
||||
}
|
||||
},
|
||||
"string.prototype.trimright": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.0.0.tgz",
|
||||
"integrity": "sha1-q0pW2AKgH75yk+EehPJNyBZGYd0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"define-properties": "1.1.2",
|
||||
"function-bind": "1.1.0"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "uppy",
|
||||
"version": "0.22.5",
|
||||
"version": "0.23.0",
|
||||
"description": "Extensible file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Dropbox and Google Drive, S3 and more :dog:",
|
||||
"main": "lib/index.js",
|
||||
"jsnext:main": "src/index.js",
|
||||
|
|
@ -97,7 +97,8 @@
|
|||
"wdio-mocha-framework": "^0.5.12",
|
||||
"wdio-sauce-service": "^0.4.6",
|
||||
"wdio-static-server-service": "^1.0.1",
|
||||
"webdriverio": "^4.10.1"
|
||||
"webdriverio": "^4.10.1",
|
||||
"json3": "^3.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.5",
|
||||
|
|
|
|||
151
src/core/Core.js
151
src/core/Core.js
|
|
@ -87,8 +87,8 @@ class Uppy {
|
|||
this.upload = this.upload.bind(this)
|
||||
|
||||
this.emitter = ee()
|
||||
this.on = this.emitter.on.bind(this.emitter)
|
||||
this.off = this.emitter.off.bind(this.emitter)
|
||||
this.on = this.on.bind(this)
|
||||
this.off = this.off.bind(this)
|
||||
this.once = this.emitter.once.bind(this.emitter)
|
||||
this.emit = this.emitter.emit.bind(this.emitter)
|
||||
|
||||
|
|
@ -126,6 +126,16 @@ class Uppy {
|
|||
}
|
||||
}
|
||||
|
||||
on (event, callback) {
|
||||
this.emitter.on(event, callback)
|
||||
return this
|
||||
}
|
||||
|
||||
off (event, callback) {
|
||||
this.emitter.off(event, callback)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate on all plugins and run `update` on them.
|
||||
* Called each time state changes.
|
||||
|
|
@ -278,10 +288,8 @@ class Uppy {
|
|||
_checkMinNumberOfFiles () {
|
||||
const {minNumberOfFiles} = this.opts.restrictions
|
||||
if (Object.keys(this.getState().files).length < minNumberOfFiles) {
|
||||
this.info(`${this.i18n('youHaveToAtLeastSelectX', { smart_count: minNumberOfFiles })}`, 'error', 5000)
|
||||
return false
|
||||
throw new Error(`${this.i18n('youHaveToAtLeastSelectX', { smart_count: minNumberOfFiles })}`)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -289,7 +297,6 @@ class Uppy {
|
|||
* maxNumberOfFiles and allowedFileTypes.
|
||||
*
|
||||
* @param {object} file object to check
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
_checkRestrictions (file) {
|
||||
|
|
@ -297,8 +304,7 @@ class Uppy {
|
|||
|
||||
if (maxNumberOfFiles) {
|
||||
if (Object.keys(this.getState().files).length + 1 > maxNumberOfFiles) {
|
||||
this.info(`${this.i18n('youCanOnlyUploadX', { smart_count: maxNumberOfFiles })}`, 'error', 5000)
|
||||
return false
|
||||
throw new Error(`${this.i18n('youCanOnlyUploadX', { smart_count: maxNumberOfFiles })}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -310,19 +316,15 @@ class Uppy {
|
|||
|
||||
if (!isCorrectFileType) {
|
||||
const allowedFileTypesString = allowedFileTypes.join(', ')
|
||||
this.info(`${this.i18n('youCanOnlyUploadFileTypes')} ${allowedFileTypesString}`, 'error', 5000)
|
||||
return false
|
||||
throw new Error(`${this.i18n('youCanOnlyUploadFileTypes')} ${allowedFileTypesString}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (maxFileSize) {
|
||||
if (file.data.size > maxFileSize) {
|
||||
this.info(`${this.i18n('exceedsSize')} ${prettyBytes(maxFileSize)}`, 'error', 5000)
|
||||
return false
|
||||
throw new Error(`${this.i18n('exceedsSize')} ${prettyBytes(maxFileSize)}`)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -333,17 +335,12 @@ class Uppy {
|
|||
* @param {object} file object to add
|
||||
*/
|
||||
addFile (file) {
|
||||
// Wrap this in a Promise `.then()` handler so errors will reject the Promise
|
||||
// instead of throwing.
|
||||
const beforeFileAdded = Promise.resolve()
|
||||
return Promise.resolve()
|
||||
// Wrap this in a Promise `.then()` handler so errors will reject the Promise
|
||||
// instead of throwing.
|
||||
.then(() => this.opts.onBeforeFileAdded(file, this.getState().files))
|
||||
|
||||
return beforeFileAdded.catch((err) => {
|
||||
const message = typeof err === 'object' ? err.message : err
|
||||
this.info(message, 'error', 5000)
|
||||
return Promise.reject(new Error(`onBeforeFileAdded: ${message}`))
|
||||
}).then(() => {
|
||||
return Utils.getFileType(file).then((fileType) => {
|
||||
.then(() => Utils.getFileType(file))
|
||||
.then((fileType) => {
|
||||
const updatedFiles = Object.assign({}, this.getState().files)
|
||||
let fileName
|
||||
if (file.name) {
|
||||
|
|
@ -382,10 +379,7 @@ class Uppy {
|
|||
preview: file.preview
|
||||
}
|
||||
|
||||
const isFileAllowed = this._checkRestrictions(newFile)
|
||||
if (!isFileAllowed) {
|
||||
return Promise.reject(new Error('File not allowed'))
|
||||
}
|
||||
this._checkRestrictions(newFile)
|
||||
|
||||
updatedFiles[fileID] = newFile
|
||||
this.setState({files: updatedFiles})
|
||||
|
|
@ -402,7 +396,12 @@ class Uppy {
|
|||
}, 4)
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
const message = typeof err === 'object' ? err.message : err
|
||||
this.log(message)
|
||||
this.info(message, 'error', 5000)
|
||||
return Promise.reject(typeof err === 'object' ? err : new Error(err))
|
||||
})
|
||||
}
|
||||
|
||||
removeFile (fileID) {
|
||||
|
|
@ -437,7 +436,7 @@ class Uppy {
|
|||
})
|
||||
|
||||
this._calculateTotalProgress()
|
||||
this.emit('file-removed', fileID)
|
||||
this.emit('file-removed', removedFile)
|
||||
|
||||
// Clean up object URLs.
|
||||
if (removedFile.preview && Utils.isObjectURL(removedFile.preview)) {
|
||||
|
|
@ -558,7 +557,7 @@ class Uppy {
|
|||
|
||||
// skip progress event for a file that’s been removed
|
||||
if (!this.getFile(fileID)) {
|
||||
this.log('Trying to set progress for a file that’s been removed: ', fileID)
|
||||
this.log(`Not setting progress for a file that has been removed: ${fileID}`)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -615,11 +614,11 @@ class Uppy {
|
|||
this.setState({ error: error.message })
|
||||
})
|
||||
|
||||
this.on('upload-error', (fileID, error) => {
|
||||
this.setFileState(fileID, { error: error.message })
|
||||
this.on('upload-error', (file, error) => {
|
||||
this.setFileState(file.id, { error: error.message })
|
||||
this.setState({ error: error.message })
|
||||
|
||||
const fileName = this.getState().files[fileID].name
|
||||
const fileName = file.name
|
||||
let message = `Failed to upload ${fileName}`
|
||||
if (typeof error === 'object' && error.message) {
|
||||
message = { message: message, details: error.message }
|
||||
|
|
@ -640,6 +639,10 @@ class Uppy {
|
|||
})
|
||||
|
||||
this.on('upload-started', (fileID, upload) => {
|
||||
if (!this.getFile(fileID)) {
|
||||
this.log(`Not setting progress for a file that has been removed: ${fileID}`)
|
||||
return
|
||||
}
|
||||
const file = this.getFile(fileID)
|
||||
this.setFileState(fileID, {
|
||||
progress: Object.assign({}, file.progress, {
|
||||
|
|
@ -656,13 +659,13 @@ class Uppy {
|
|||
// connection to the remote server. Therefore, we are throtteling them to
|
||||
// prevent accessive function calls.
|
||||
// see also: https://github.com/tus/tus-js-client/commit/9940f27b2361fd7e10ba58b09b60d82422183bbb
|
||||
const _throttledCalculateProgress = throttle(this._calculateProgress, 100, { leading: true, trailing: false })
|
||||
const _throttledCalculateProgress = throttle(this._calculateProgress, 100, { leading: true, trailing: true })
|
||||
|
||||
this.on('upload-progress', _throttledCalculateProgress)
|
||||
|
||||
this.on('upload-success', (fileID, uploadResp, uploadURL) => {
|
||||
this.setFileState(fileID, {
|
||||
progress: Object.assign({}, this.getState().files[fileID].progress, {
|
||||
this.on('upload-success', (file, uploadResp, uploadURL) => {
|
||||
this.setFileState(file.id, {
|
||||
progress: Object.assign({}, this.state.files[file.id].progress, {
|
||||
uploadComplete: true,
|
||||
percentage: 100
|
||||
}),
|
||||
|
|
@ -674,6 +677,10 @@ class Uppy {
|
|||
})
|
||||
|
||||
this.on('preprocess-progress', (fileID, progress) => {
|
||||
if (!this.getFile(fileID)) {
|
||||
this.log(`Not setting progress for a file that has been removed: ${fileID}`)
|
||||
return
|
||||
}
|
||||
this.setFileState(fileID, {
|
||||
progress: Object.assign({}, this.getState().files[fileID].progress, {
|
||||
preprocess: progress
|
||||
|
|
@ -682,6 +689,10 @@ class Uppy {
|
|||
})
|
||||
|
||||
this.on('preprocess-complete', (fileID) => {
|
||||
if (!this.getFile(fileID)) {
|
||||
this.log(`Not setting progress for a file that has been removed: ${fileID}`)
|
||||
return
|
||||
}
|
||||
const files = Object.assign({}, this.getState().files)
|
||||
files[fileID] = Object.assign({}, files[fileID], {
|
||||
progress: Object.assign({}, files[fileID].progress)
|
||||
|
|
@ -692,6 +703,10 @@ class Uppy {
|
|||
})
|
||||
|
||||
this.on('postprocess-progress', (fileID, progress) => {
|
||||
if (!this.getFile(fileID)) {
|
||||
this.log(`Not setting progress for a file that has been removed: ${fileID}`)
|
||||
return
|
||||
}
|
||||
this.setFileState(fileID, {
|
||||
progress: Object.assign({}, this.getState().files[fileID].progress, {
|
||||
postprocess: progress
|
||||
|
|
@ -700,6 +715,10 @@ class Uppy {
|
|||
})
|
||||
|
||||
this.on('postprocess-complete', (fileID) => {
|
||||
if (!this.getFile(fileID)) {
|
||||
this.log(`Not setting progress for a file that has been removed: ${fileID}`)
|
||||
return
|
||||
}
|
||||
const files = Object.assign({}, this.getState().files)
|
||||
files[fileID] = Object.assign({}, files[fileID], {
|
||||
progress: Object.assign({}, files[fileID].progress)
|
||||
|
|
@ -989,6 +1008,10 @@ class Uppy {
|
|||
* @param {object} data Data properties to add to the result object.
|
||||
*/
|
||||
addResultData (uploadID, data) {
|
||||
if (!this._getUpload(uploadID)) {
|
||||
this.log(`Not setting result for an upload that has been removed: ${uploadID}`)
|
||||
return
|
||||
}
|
||||
const currentUploads = this.getState().currentUploads
|
||||
const currentUpload = Object.assign({}, currentUploads[uploadID], {
|
||||
result: Object.assign({}, currentUploads[uploadID].result, data)
|
||||
|
|
@ -1069,6 +1092,11 @@ class Uppy {
|
|||
this.addResultData(uploadID, { successful, failed, uploadID })
|
||||
|
||||
const { currentUploads } = this.getState()
|
||||
if (!currentUploads[uploadID]) {
|
||||
this.log(`Not setting result for an upload that has been removed: ${uploadID}`)
|
||||
return
|
||||
}
|
||||
|
||||
const result = currentUploads[uploadID].result
|
||||
this.emit('complete', result)
|
||||
// Compatibility with pre-0.21
|
||||
|
|
@ -1090,35 +1118,32 @@ class Uppy {
|
|||
this.log('No uploader type plugins are used', 'warning')
|
||||
}
|
||||
|
||||
const isMinNumberOfFilesReached = this._checkMinNumberOfFiles()
|
||||
if (!isMinNumberOfFilesReached) {
|
||||
return Promise.reject(new Error('Minimum number of files has not been reached'))
|
||||
}
|
||||
|
||||
const beforeUpload = Promise.resolve()
|
||||
return Promise.resolve()
|
||||
.then(() => this.opts.onBeforeUpload(this.getState().files))
|
||||
.then(() => this._checkMinNumberOfFiles())
|
||||
.then(() => {
|
||||
const { currentUploads } = this.getState()
|
||||
// get a list of files that are currently assigned to uploads
|
||||
const currentlyUploadingFiles = Object.keys(currentUploads).reduce((prev, curr) => prev.concat(currentUploads[curr].fileIDs), [])
|
||||
|
||||
return beforeUpload.catch((err) => {
|
||||
const message = typeof err === 'object' ? err.message : err
|
||||
this.info(message, 'error', 5000)
|
||||
return Promise.reject(new Error(`onBeforeUpload: ${message}`))
|
||||
}).then(() => {
|
||||
const { currentUploads } = this.getState()
|
||||
// get a list of files that are currently assigned to uploads
|
||||
const currentlyUploadingFiles = Object.keys(currentUploads).reduce((prev, curr) => prev.concat(currentUploads[curr].fileIDs), [])
|
||||
const waitingFileIDs = []
|
||||
Object.keys(this.getState().files).forEach((fileID) => {
|
||||
const file = this.getFile(fileID)
|
||||
// if the file hasn't started uploading and hasn't already been assigned to an upload..
|
||||
if ((!file.progress.uploadStarted) && (currentlyUploadingFiles.indexOf(fileID) === -1)) {
|
||||
waitingFileIDs.push(file.id)
|
||||
}
|
||||
})
|
||||
|
||||
const waitingFileIDs = []
|
||||
Object.keys(this.getState().files).forEach((fileID) => {
|
||||
const file = this.getFile(fileID)
|
||||
// if the file hasn't started uploading and hasn't already been assigned to an upload..
|
||||
if ((!file.progress.uploadStarted) && (currentlyUploadingFiles.indexOf(fileID) === -1)) {
|
||||
waitingFileIDs.push(file.id)
|
||||
}
|
||||
const uploadID = this._createUpload(waitingFileIDs)
|
||||
return this._runUpload(uploadID)
|
||||
})
|
||||
.catch((err) => {
|
||||
const message = typeof err === 'object' ? err.message : err
|
||||
this.log(message)
|
||||
this.info(message, 'error', 4000)
|
||||
return Promise.reject(typeof err === 'object' ? err : new Error(err))
|
||||
})
|
||||
|
||||
const uploadID = this._createUpload(waitingFileIDs)
|
||||
return this._runUpload(uploadID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -610,7 +610,7 @@ describe('src/Core', () => {
|
|||
throw new Error('File was allowed through')
|
||||
})
|
||||
.catch(e => {
|
||||
expect(e.message).toEqual('File not allowed')
|
||||
expect(e.message).toEqual('You can only upload: image/gif')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -625,7 +625,7 @@ describe('src/Core', () => {
|
|||
name: 'foo.jpg',
|
||||
type: 'image/jpeg',
|
||||
data: null
|
||||
})).rejects.toMatchObject(new Error('onBeforeFileAdded: a plain string'))
|
||||
})).rejects.toMatchObject(new Error('a plain string'))
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -651,8 +651,9 @@ describe('src/Core', () => {
|
|||
const core = new Core().run()
|
||||
core.addUploader((fileIDs) => {
|
||||
fileIDs.forEach((fileID) => {
|
||||
if (/bar/.test(core.getFile(fileID).name)) {
|
||||
core.emit('upload-error', fileID, new Error('This is bar and I do not like bar'))
|
||||
const file = core.getFile(fileID)
|
||||
if (/bar/.test(file.name)) {
|
||||
core.emit('upload-error', file, new Error('This is bar and I do not like bar'))
|
||||
}
|
||||
})
|
||||
return Promise.resolve()
|
||||
|
|
@ -717,10 +718,11 @@ describe('src/Core', () => {
|
|||
totalProgress: 50
|
||||
})
|
||||
|
||||
const file = core.getFile(fileId)
|
||||
core.removeFile(fileId)
|
||||
|
||||
expect(Object.keys(core.state.files).length).toEqual(0)
|
||||
expect(fileRemovedEventMock.mock.calls[0][0]).toEqual(fileId)
|
||||
expect(fileRemovedEventMock.mock.calls[0][0]).toEqual(file)
|
||||
expect(core.state.totalProgress).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
|
@ -955,7 +957,7 @@ describe('src/Core', () => {
|
|||
name: 'foo2.jpg',
|
||||
type: 'image/jpeg',
|
||||
data: utils.dataURItoFile(sampleImageDataURI, {})
|
||||
})).rejects.toMatchObject(new Error('File not allowed')).then(() => {
|
||||
})).rejects.toMatchObject(new Error('You can only upload 1 file')).then(() => {
|
||||
expect(core.state.info.message).toEqual('You can only upload 1 file')
|
||||
})
|
||||
})
|
||||
|
|
@ -975,7 +977,7 @@ describe('src/Core', () => {
|
|||
name: 'foo2.jpg',
|
||||
type: 'image/jpeg',
|
||||
data: utils.dataURItoFile(sampleImageDataURI, {})
|
||||
})).rejects.toMatchObject(new Error('File not allowed')).then(() => {
|
||||
})).rejects.toMatchObject(new Error('You can only upload: image/gif, image/png')).then(() => {
|
||||
expect(core.state.info.message).toEqual('You can only upload: image/gif, image/png')
|
||||
})
|
||||
})
|
||||
|
|
@ -993,7 +995,7 @@ describe('src/Core', () => {
|
|||
name: 'foo.jpg',
|
||||
type: 'image/jpeg',
|
||||
data: utils.dataURItoFile(sampleImageDataURI, {})
|
||||
})).rejects.toMatchObject(new Error('File not allowed')).then(() => {
|
||||
})).rejects.toMatchObject(new Error('This file exceeds maximum allowed size of 1.2 KB')).then(() => {
|
||||
expect(core.state.info.message).toEqual('This file exceeds maximum allowed size of 1.2 KB')
|
||||
})
|
||||
})
|
||||
|
|
@ -1013,7 +1015,7 @@ describe('src/Core', () => {
|
|||
core.state.files['fileId'] = {
|
||||
name: 'filename'
|
||||
}
|
||||
core.emit('upload-error', 'fileId', new Error('this is the error'))
|
||||
core.emit('upload-error', core.state.files['fileId'], new Error('this is the error'))
|
||||
expect(core.state.info).toEqual({'message': 'Failed to upload filename', 'details': 'this is the error', 'isHidden': false, 'type': 'error'})
|
||||
})
|
||||
|
||||
|
|
|
|||
14
src/index.js
14
src/index.js
|
|
@ -3,16 +3,14 @@ const Core = require('./core')
|
|||
// Parent
|
||||
const Plugin = require('./core/Plugin')
|
||||
|
||||
// Orchestrators
|
||||
const Dashboard = require('./plugins/Dashboard')
|
||||
|
||||
// Acquirers
|
||||
const Dummy = require('./plugins/Dummy')
|
||||
const Dashboard = require('./plugins/Dashboard')
|
||||
const DragDrop = require('./plugins/DragDrop')
|
||||
const FileInput = require('./plugins/FileInput')
|
||||
const GoogleDrive = require('./plugins/GoogleDrive')
|
||||
const Dropbox = require('./plugins/Dropbox')
|
||||
const Instagram = require('./plugins/Instagram')
|
||||
const Url = require('./plugins/Url')
|
||||
const Webcam = require('./plugins/Webcam')
|
||||
|
||||
// Progressindicators
|
||||
|
|
@ -20,8 +18,6 @@ const StatusBar = require('./plugins/StatusBar')
|
|||
const ProgressBar = require('./plugins/ProgressBar')
|
||||
const Informer = require('./plugins/Informer')
|
||||
|
||||
// Modifiers
|
||||
|
||||
// Uploaders
|
||||
const Tus = require('./plugins/Tus')
|
||||
const XHRUpload = require('./plugins/XHRUpload')
|
||||
|
|
@ -29,6 +25,8 @@ const Transloadit = require('./plugins/Transloadit')
|
|||
const AwsS3 = require('./plugins/AwsS3')
|
||||
|
||||
// Helpers and utilities
|
||||
const Form = require('./plugins/Form')
|
||||
const ThumbnailGenerator = require('./plugins/ThumbnailGenerator')
|
||||
const GoldenRetriever = require('./plugins/GoldenRetriever')
|
||||
const ReduxDevTools = require('./plugins/ReduxDevTools')
|
||||
const ReduxStore = require('./plugins/Redux')
|
||||
|
|
@ -36,7 +34,6 @@ const ReduxStore = require('./plugins/Redux')
|
|||
module.exports = {
|
||||
Core,
|
||||
Plugin,
|
||||
Dummy,
|
||||
StatusBar,
|
||||
ProgressBar,
|
||||
Informer,
|
||||
|
|
@ -44,6 +41,7 @@ module.exports = {
|
|||
GoogleDrive,
|
||||
Dropbox,
|
||||
Instagram,
|
||||
Url,
|
||||
FileInput,
|
||||
Tus,
|
||||
XHRUpload,
|
||||
|
|
@ -51,6 +49,8 @@ module.exports = {
|
|||
AwsS3,
|
||||
Dashboard,
|
||||
Webcam,
|
||||
Form,
|
||||
ThumbnailGenerator,
|
||||
GoldenRetriever,
|
||||
ReduxDevTools,
|
||||
ReduxStore
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ module.exports = class AwsS3 extends Plugin {
|
|||
responseUrlFieldName: 'location',
|
||||
timeout: this.opts.timeout,
|
||||
limit: this.opts.limit,
|
||||
getResponseData (xhr) {
|
||||
getResponseData (content, xhr) {
|
||||
// If no response, we've hopefully done a PUT request to the file
|
||||
// in the bucket on its full URL.
|
||||
if (!isXml(xhr)) {
|
||||
|
|
@ -152,7 +152,7 @@ module.exports = class AwsS3 extends Plugin {
|
|||
etag: getValue('ETag')
|
||||
}
|
||||
},
|
||||
getResponseError (xhr) {
|
||||
getResponseError (content, xhr) {
|
||||
// If no response, we don't have a specific error message, use the default.
|
||||
if (!isXml(xhr)) {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -277,6 +277,8 @@ module.exports = class Dashboard extends Plugin {
|
|||
name: file.name,
|
||||
type: file.type,
|
||||
data: blob
|
||||
}).catch(() => {
|
||||
// Ignore
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -291,6 +293,8 @@ module.exports = class Dashboard extends Plugin {
|
|||
name: file.name,
|
||||
type: file.type,
|
||||
data: file
|
||||
}).catch(() => {
|
||||
// Ignore
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -360,6 +364,8 @@ module.exports = class Dashboard extends Plugin {
|
|||
name: file.name,
|
||||
type: file.type,
|
||||
data: file
|
||||
}).catch(() => {
|
||||
// Ignore
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ module.exports = class DragDrop extends Plugin {
|
|||
name: file.name,
|
||||
type: file.type,
|
||||
data: file
|
||||
}).catch(() => {
|
||||
// Ignore
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -98,6 +100,8 @@ module.exports = class DragDrop extends Plugin {
|
|||
name: file.name,
|
||||
type: file.type,
|
||||
data: file
|
||||
}).catch(() => {
|
||||
// Ignore
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ module.exports = class Dummy extends Plugin {
|
|||
data: blob
|
||||
}
|
||||
this.props.log('Adding fake file blob')
|
||||
this.props.addFile(file)
|
||||
this.props.addFile(file).catch(() => {
|
||||
// Ignore
|
||||
})
|
||||
}
|
||||
|
||||
render (state) {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ module.exports = class FileInput extends Plugin {
|
|||
name: file.name,
|
||||
type: file.type,
|
||||
data: file
|
||||
}).catch(() => {
|
||||
// Ignore
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,7 +169,9 @@ module.exports = class View {
|
|||
tagFile.preview = this.plugin.getItemThumbnailUrl(file)
|
||||
}
|
||||
this.plugin.uppy.log('Adding remote file')
|
||||
this.plugin.uppy.addFile(tagFile)
|
||||
this.plugin.uppy.addFile(tagFile).catch(() => {
|
||||
// Ignore
|
||||
})
|
||||
if (!isCheckbox) {
|
||||
this.donePicking()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,12 +169,13 @@ const ProgressBarProcessing = (props) => {
|
|||
}
|
||||
|
||||
const ProgressBarUploading = (props) => {
|
||||
const { i18n } = props
|
||||
return (
|
||||
<div class="uppy-StatusBar-content">
|
||||
{props.isUploadStarted && !props.isAllComplete
|
||||
? !props.isAllPaused
|
||||
? <div title="Uploading">{ <PauseResumeButtons {...props} /> } Uploading... { <ThrottledProgressDetails {...props} /> }</div>
|
||||
: <div title="Paused">{ <PauseResumeButtons {...props} /> } Paused・{props.totalProgress}%</div>
|
||||
? <div title="Uploading">{ <PauseResumeButtons {...props} /> } {i18n('uploading')} { <ThrottledProgressDetails {...props} /> }</div>
|
||||
: <div title="Paused">{ <PauseResumeButtons {...props} /> } {i18n('paused')}・{props.totalProgress}%</div>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ module.exports = class StatusBar extends Plugin {
|
|||
|
||||
const defaultLocale = {
|
||||
strings: {
|
||||
uploading: 'Uploading',
|
||||
uploading: 'Uploading...',
|
||||
uploadComplete: 'Upload complete',
|
||||
uploadFailed: 'Upload failed',
|
||||
pleasePressRetry: 'Please press Retry to upload again',
|
||||
|
|
@ -59,6 +59,7 @@ module.exports = class StatusBar extends Plugin {
|
|||
this.translator = new Translator({locale: this.locale})
|
||||
this.i18n = this.translator.translate.bind(this.translator)
|
||||
|
||||
this.startUpload = this.startUpload.bind(this)
|
||||
this.render = this.render.bind(this)
|
||||
this.install = this.install.bind(this)
|
||||
}
|
||||
|
|
@ -84,6 +85,12 @@ module.exports = class StatusBar extends Plugin {
|
|||
return Math.round(totalBytesRemaining / totalSpeed * 10) / 10
|
||||
}
|
||||
|
||||
startUpload () {
|
||||
return this.uppy.upload().catch(() => {
|
||||
// Ignore
|
||||
})
|
||||
}
|
||||
|
||||
render (state) {
|
||||
const files = state.files
|
||||
|
||||
|
|
@ -159,7 +166,7 @@ module.exports = class StatusBar extends Plugin {
|
|||
resumeAll: this.uppy.resumeAll,
|
||||
retryAll: this.uppy.retryAll,
|
||||
cancelAll: this.uppy.cancelAll,
|
||||
startUpload: this.uppy.upload,
|
||||
startUpload: this.startUpload,
|
||||
complete: completeFiles.length,
|
||||
newFiles: newFiles.length,
|
||||
inProgress: uploadStartedFiles.length,
|
||||
|
|
|
|||
|
|
@ -57,82 +57,73 @@ module.exports = class ThumbnailGenerator extends Plugin {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the image doesn’t exceed browser/device canvas limits.
|
||||
* For ios with 256 RAM and ie
|
||||
*/
|
||||
protect (image) {
|
||||
// https://stackoverflow.com/questions/6081483/maximum-size-of-a-canvas-element
|
||||
|
||||
var ratio = image.width / image.height
|
||||
|
||||
var maxSquare = 5000000 // ios max canvas square
|
||||
var maxSize = 4096 // ie max canvas dimensions
|
||||
|
||||
var maxW = Math.floor(Math.sqrt(maxSquare * ratio))
|
||||
var maxH = Math.floor(maxSquare / Math.sqrt(maxSquare * ratio))
|
||||
if (maxW > maxSize) {
|
||||
maxW = maxSize
|
||||
maxH = Math.round(maxW / ratio)
|
||||
}
|
||||
if (maxH > maxSize) {
|
||||
maxH = maxSize
|
||||
maxW = Math.round(ratio * maxH)
|
||||
}
|
||||
if (image.width > maxW) {
|
||||
var canvas = document.createElement('canvas')
|
||||
canvas.width = maxW
|
||||
canvas.height = maxH
|
||||
canvas.getContext('2d').drawImage(image, 0, 0, maxW, maxH)
|
||||
image.src = 'about:blank'
|
||||
image.width = 1
|
||||
image.height = 1
|
||||
image = canvas
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize an image to the target `width` and `height`.
|
||||
*
|
||||
* Returns a Canvas with the resized image on it.
|
||||
*/
|
||||
resizeImage (image, targetWidth, targetHeight) {
|
||||
let sourceWidth = image.width
|
||||
let sourceHeight = image.height
|
||||
// Resizing in steps refactored to use a solution from
|
||||
// https://blog.uploadcare.com/image-resize-in-browsers-is-broken-e38eed08df01
|
||||
|
||||
if (targetHeight < image.height / 2) {
|
||||
const steps = Math.floor(
|
||||
Math.log(image.width / targetWidth) / Math.log(2)
|
||||
)
|
||||
const stepScaled = this.downScaleInSteps(image, steps)
|
||||
image = stepScaled.image
|
||||
sourceWidth = stepScaled.sourceWidth
|
||||
sourceHeight = stepScaled.sourceHeight
|
||||
image = this.protect(image)
|
||||
|
||||
var steps = Math.ceil(Math.log2(image.width / targetWidth))
|
||||
if (steps < 1) {
|
||||
steps = 1
|
||||
}
|
||||
var sW = targetWidth * Math.pow(2, steps - 1)
|
||||
var sH = targetHeight * Math.pow(2, steps - 1)
|
||||
var x = 2
|
||||
|
||||
while (steps--) {
|
||||
var canvas = document.createElement('canvas')
|
||||
canvas.width = sW
|
||||
canvas.height = sH
|
||||
canvas.getContext('2d').drawImage(image, 0, 0, sW, sH)
|
||||
image = canvas
|
||||
|
||||
sW = Math.round(sW / x)
|
||||
sH = Math.round(sH / x)
|
||||
}
|
||||
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = targetWidth
|
||||
canvas.height = targetHeight
|
||||
|
||||
const context = canvas.getContext('2d')
|
||||
context.drawImage(
|
||||
image,
|
||||
0,
|
||||
0,
|
||||
sourceWidth,
|
||||
sourceHeight,
|
||||
0,
|
||||
0,
|
||||
targetWidth,
|
||||
targetHeight
|
||||
)
|
||||
return canvas
|
||||
}
|
||||
|
||||
/**
|
||||
* Downscale an image by 50% `steps` times.
|
||||
*/
|
||||
downScaleInSteps (image, steps) {
|
||||
let source = image
|
||||
let currentWidth = source.width
|
||||
let currentHeight = source.height
|
||||
|
||||
for (let i = 0; i < steps; i += 1) {
|
||||
const canvas = document.createElement('canvas')
|
||||
const context = canvas.getContext('2d')
|
||||
canvas.width = currentWidth / 2
|
||||
canvas.height = currentHeight / 2
|
||||
context.drawImage(
|
||||
source,
|
||||
// The entire source image. We pass width and height here,
|
||||
// because we reuse this canvas, and should only scale down
|
||||
// the part of the canvas that contains the previous scale step.
|
||||
0,
|
||||
0,
|
||||
currentWidth,
|
||||
currentHeight,
|
||||
// Draw to 50% size
|
||||
0,
|
||||
0,
|
||||
currentWidth / 2,
|
||||
currentHeight / 2
|
||||
)
|
||||
currentWidth /= 2
|
||||
currentHeight /= 2
|
||||
source = canvas
|
||||
}
|
||||
|
||||
return {
|
||||
image: source,
|
||||
sourceWidth: currentWidth,
|
||||
sourceHeight: currentHeight
|
||||
}
|
||||
return image
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ describe('uploader/ThumbnailGeneratorPlugin', () => {
|
|||
URL.createObjectURL = originalURLCreateObjectURL
|
||||
})
|
||||
|
||||
it('should scale down the image by the specified number of steps', () => {
|
||||
xit('should scale down the image by the specified number of steps', () => {
|
||||
const core = {}
|
||||
const plugin = new ThumbnailGeneratorPlugin(core)
|
||||
const image = {
|
||||
|
|
@ -304,14 +304,31 @@ describe('uploader/ThumbnailGeneratorPlugin', () => {
|
|||
width: 1000,
|
||||
height: 800
|
||||
}
|
||||
plugin.downScaleInSteps = jest.fn().mockReturnValue({
|
||||
image: {
|
||||
height: 160,
|
||||
width: 200
|
||||
},
|
||||
sourceWidth: 200,
|
||||
sourceHeight: 160
|
||||
const context = {
|
||||
drawImage: jest.fn()
|
||||
}
|
||||
const canvas = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
getContext: jest.fn().mockReturnValue(context)
|
||||
}
|
||||
document.createElement = jest.fn().mockReturnValue(canvas)
|
||||
|
||||
const result = plugin.resizeImage(image, 200, 160)
|
||||
expect(result).toEqual({
|
||||
width: 200,
|
||||
height: 160,
|
||||
getContext: canvas.getContext
|
||||
})
|
||||
})
|
||||
|
||||
it('should upsize if original image is smaller than target size', () => {
|
||||
const core = {}
|
||||
const plugin = new ThumbnailGeneratorPlugin(core)
|
||||
const image = {
|
||||
width: 100,
|
||||
height: 80
|
||||
}
|
||||
const context = {
|
||||
drawImage: jest.fn()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
*/
|
||||
module.exports = class Client {
|
||||
constructor (opts = {}) {
|
||||
this.apiUrl = 'https://api2.transloadit.com'
|
||||
this.opts = opts
|
||||
}
|
||||
|
||||
|
|
@ -32,7 +31,7 @@ module.exports = class Client {
|
|||
})
|
||||
data.append('num_expected_upload_files', expectedFiles)
|
||||
|
||||
return fetch(`${this.apiUrl}/assemblies`, {
|
||||
return fetch(`${this.opts.service}/assemblies`, {
|
||||
method: 'post',
|
||||
body: data
|
||||
}).then((response) => response.json()).then((assembly) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const Translator = require('../../core/Translator')
|
||||
const Plugin = require('../../core/Plugin')
|
||||
const Tus = require('../Tus')
|
||||
const Client = require('./Client')
|
||||
const StatusSocket = require('./Socket')
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ module.exports = class Transloadit extends Plugin {
|
|||
}
|
||||
|
||||
const defaultOptions = {
|
||||
service: 'https://api2.transloadit.com',
|
||||
waitForEncoding: false,
|
||||
waitForMetadata: false,
|
||||
alwaysRunAssembly: false,
|
||||
|
|
@ -54,12 +56,15 @@ module.exports = class Transloadit extends Plugin {
|
|||
this.onFileUploadURLAvailable = this.onFileUploadURLAvailable.bind(this)
|
||||
this.onRestored = this.onRestored.bind(this)
|
||||
this.getPersistentData = this.getPersistentData.bind(this)
|
||||
this.removeFileFromPluginState = this.removeFileFromPluginState.bind(this)
|
||||
|
||||
if (this.opts.params) {
|
||||
this.validateParams(this.opts.params)
|
||||
}
|
||||
|
||||
this.client = new Client()
|
||||
this.client = new Client({
|
||||
service: this.opts.service
|
||||
})
|
||||
this.sockets = {}
|
||||
}
|
||||
|
||||
|
|
@ -87,11 +92,27 @@ module.exports = class Transloadit extends Plugin {
|
|||
|
||||
getAssemblyOptions (fileIDs) {
|
||||
const options = this.opts
|
||||
|
||||
const normalizeAssemblyOptions = (file, assemblyOptions) => {
|
||||
if (Array.isArray(assemblyOptions.fields)) {
|
||||
const fieldNames = assemblyOptions.fields
|
||||
assemblyOptions.fields = {}
|
||||
fieldNames.forEach((fieldName) => {
|
||||
assemblyOptions.fields[fieldName] = file.meta[fieldName]
|
||||
})
|
||||
}
|
||||
if (!assemblyOptions.fields) {
|
||||
assemblyOptions.fields = {}
|
||||
}
|
||||
return assemblyOptions
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
fileIDs.map((fileID) => {
|
||||
const file = this.uppy.getFile(fileID)
|
||||
const promise = Promise.resolve()
|
||||
.then(() => options.getAssemblyOptions(file, options))
|
||||
.then((assemblyOptions) => normalizeAssemblyOptions(file, assemblyOptions))
|
||||
return promise.then((assemblyOptions) => {
|
||||
this.validateParams(assemblyOptions.params)
|
||||
|
||||
|
|
@ -149,7 +170,6 @@ module.exports = class Transloadit extends Plugin {
|
|||
function attachAssemblyMetadata (file, assembly) {
|
||||
// Attach meta parameters for the Tus plugin. See:
|
||||
// https://github.com/tus/tusd/wiki/Uploading-to-Transloadit-using-tus#uploading-using-tus
|
||||
// TODO Should this `meta` be moved to a `tus.meta` property instead?
|
||||
const tlMeta = {
|
||||
assembly_url: assembly.assembly_url,
|
||||
filename: file.name,
|
||||
|
|
@ -158,14 +178,7 @@ module.exports = class Transloadit extends Plugin {
|
|||
const meta = Object.assign({}, file.meta, tlMeta)
|
||||
// Add assembly-specific Tus endpoint.
|
||||
const tus = Object.assign({}, file.tus, {
|
||||
endpoint: assembly.tus_url,
|
||||
// Only send assembly metadata to the tus endpoint.
|
||||
metaFields: Object.keys(tlMeta),
|
||||
// Make sure tus doesn't resume a previous upload.
|
||||
uploadUrl: null,
|
||||
// Disable tus-js-client fingerprinting, otherwise uploading the same file at different times
|
||||
// will upload to the same assembly.
|
||||
resume: false
|
||||
endpoint: assembly.tus_url
|
||||
})
|
||||
|
||||
// Set uppy server location.
|
||||
|
|
@ -285,6 +298,10 @@ module.exports = class Transloadit extends Plugin {
|
|||
onFileUploadComplete (assemblyId, uploadedFile) {
|
||||
const state = this.getPluginState()
|
||||
const file = this.findFile(uploadedFile)
|
||||
if (!file) {
|
||||
this.uppy.log('[Transloadit] Couldn’t file the file, it was likely removed in the process')
|
||||
return
|
||||
}
|
||||
this.setPluginState({
|
||||
files: Object.assign({}, state.files, {
|
||||
[uploadedFile.id]: {
|
||||
|
|
@ -580,6 +597,14 @@ module.exports = class Transloadit extends Plugin {
|
|||
fileIDs.forEach((fileID) => {
|
||||
this.uppy.emit('preprocess-complete', fileID)
|
||||
})
|
||||
}).catch((err) => {
|
||||
// Clear preprocessing state when the assembly could not be created,
|
||||
// otherwise the UI gets confused about the lingering progress keys
|
||||
fileIDs.forEach((fileID) => {
|
||||
this.uppy.emit('preprocess-complete', fileID)
|
||||
this.uppy.emit('upload-error', fileID, err)
|
||||
})
|
||||
throw err
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -744,17 +769,37 @@ module.exports = class Transloadit extends Plugin {
|
|||
})
|
||||
}
|
||||
|
||||
removeFileFromPluginState (fileID) {
|
||||
const updatedFiles = this.getPluginState().files
|
||||
delete updatedFiles[fileID]
|
||||
|
||||
this.setPluginState({
|
||||
files: updatedFiles
|
||||
})
|
||||
}
|
||||
|
||||
install () {
|
||||
this.uppy.addPreProcessor(this.prepareUpload)
|
||||
this.uppy.addPostProcessor(this.afterUpload)
|
||||
|
||||
if (this.opts.importFromUploadURLs) {
|
||||
// No uploader needed when importing; instead we take the upload URL from an existing uploader.
|
||||
this.uppy.on('upload-success', this.onFileUploadURLAvailable)
|
||||
} else {
|
||||
this.uppy.use(Tus, {
|
||||
// Disable tus-js-client fingerprinting, otherwise uploading the same file at different times
|
||||
// will upload to the same assembly.
|
||||
resume: false,
|
||||
// Only send assembly metadata to the tus endpoint.
|
||||
metaFields: ['assembly_url', 'filename', 'fieldname']
|
||||
})
|
||||
}
|
||||
|
||||
this.uppy.on('restore:get-data', this.getPersistentData)
|
||||
this.uppy.on('restored', this.onRestored)
|
||||
|
||||
// this.uppy.on('file-removed', this.removeFileFromPluginState)
|
||||
|
||||
this.setPluginState({
|
||||
// Contains assembly status objects, indexed by their ID.
|
||||
assemblies: {},
|
||||
|
|
@ -771,6 +816,8 @@ module.exports = class Transloadit extends Plugin {
|
|||
this.uppy.removePreProcessor(this.prepareUpload)
|
||||
this.uppy.removePostProcessor(this.afterUpload)
|
||||
|
||||
// this.uppy.off('file-removed', this.removeFileFromPluginState)
|
||||
|
||||
if (this.opts.importFromUploadURLs) {
|
||||
this.uppy.off('upload-success', this.onFileUploadURLAvailable)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,6 +168,6 @@ describe('Transloadit', () => {
|
|||
}
|
||||
})
|
||||
|
||||
return expect(uppy.upload()).rejects.toBe('short-circuited')
|
||||
return expect(uppy.upload()).rejects.toEqual(new Error('short-circuited'))
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ module.exports = class Tus extends Plugin {
|
|||
|
||||
optsTus.onError = (err) => {
|
||||
this.uppy.log(err)
|
||||
this.uppy.emit('upload-error', file.id, err)
|
||||
this.uppy.emit('upload-error', file, err)
|
||||
err.message = `Failed because: ${err.message}`
|
||||
|
||||
this.resetUploaderReferences(file.id)
|
||||
|
|
@ -148,7 +148,7 @@ module.exports = class Tus extends Plugin {
|
|||
}
|
||||
|
||||
optsTus.onSuccess = () => {
|
||||
this.uppy.emit('upload-success', file.id, upload, upload.url)
|
||||
this.uppy.emit('upload-success', file, upload, upload.url)
|
||||
|
||||
if (upload.url) {
|
||||
this.uppy.log('Download ' + upload.file.name + ' from ' + upload.url)
|
||||
|
|
@ -300,12 +300,12 @@ module.exports = class Tus extends Plugin {
|
|||
socket.on('progress', (progressData) => emitSocketProgress(this, progressData, file))
|
||||
|
||||
socket.on('error', (errData) => {
|
||||
this.uppy.emit('upload-error', file.id, new Error(errData.error))
|
||||
this.uppy.emit('upload-error', file, new Error(errData.error))
|
||||
reject(new Error(errData.error))
|
||||
})
|
||||
|
||||
socket.on('success', (data) => {
|
||||
this.uppy.emit('upload-success', file.id, data, data.url)
|
||||
this.uppy.emit('upload-success', file, data, data.url)
|
||||
this.resetUploaderReferences(file.id)
|
||||
resolve()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ class UrlUI extends Component {
|
|||
class="uppy-Url-input"
|
||||
type="text"
|
||||
placeholder={this.props.i18n('enterUrlToImport')}
|
||||
ref={(input) => { this.input = input }}
|
||||
value="" />
|
||||
ref={(input) => { this.input = input }} />
|
||||
<button
|
||||
class="uppy-Url-importButton"
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@ const Translator = require('../../core/Translator')
|
|||
const { h } = require('preact')
|
||||
const Provider = require('../Provider')
|
||||
const UrlUI = require('./UrlUI.js')
|
||||
// const Utils = require('../../core/Utils')
|
||||
require('whatwg-fetch')
|
||||
|
||||
/**
|
||||
* Link
|
||||
* Url
|
||||
*
|
||||
*/
|
||||
module.exports = class Link extends Plugin {
|
||||
module.exports = class Url extends Plugin {
|
||||
constructor (uppy, opts) {
|
||||
super(uppy, opts)
|
||||
this.id = this.opts.id || 'Url'
|
||||
|
|
@ -29,7 +28,8 @@ module.exports = class Link extends Plugin {
|
|||
strings: {
|
||||
addUrl: 'Add url',
|
||||
import: 'Import',
|
||||
enterUrlToImport: 'Enter file url to import'
|
||||
enterUrlToImport: 'Enter file url to import',
|
||||
failedToFetch: 'Uppy Server failed to fetch this URL'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -47,6 +47,10 @@ module.exports = class Link extends Plugin {
|
|||
|
||||
this.hostname = this.opts.host
|
||||
|
||||
if (!this.hostname) {
|
||||
throw new Error('Uppy Server hostname is required, please consult https://uppy.io/docs/server')
|
||||
}
|
||||
|
||||
// Bind all event handlers for referencability
|
||||
this.getMeta = this.getMeta.bind(this)
|
||||
this.addFile = this.addFile.bind(this)
|
||||
|
|
@ -108,6 +112,14 @@ module.exports = class Link extends Plugin {
|
|||
if (dashboard) dashboard.hideAllPanels()
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
const errorMsg = `${err.message}. Could be CORS issue?`
|
||||
this.uppy.log(errorMsg, 'error')
|
||||
this.uppy.info({
|
||||
message: this.i18n('failedToFetch'),
|
||||
details: errorMsg
|
||||
}, 'error', 4000)
|
||||
})
|
||||
}
|
||||
|
||||
render (state) {
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ module.exports = class Webcam extends Plugin {
|
|||
})
|
||||
return this.getVideo()
|
||||
})
|
||||
.then(this.uppy.addFile)
|
||||
.then((file) => this.uppy.addFile(file))
|
||||
.then(() => {
|
||||
this.recordingChunks = null
|
||||
this.recorder = null
|
||||
|
|
@ -233,9 +233,11 @@ module.exports = class Webcam extends Plugin {
|
|||
return this.getImage()
|
||||
}).then((tagFile) => {
|
||||
this.captureInProgress = false
|
||||
this.uppy.addFile(tagFile)
|
||||
const dashboard = this.uppy.getPlugin('Dashboard')
|
||||
if (dashboard) dashboard.hideAllPanels()
|
||||
return this.uppy.addFile(tagFile).catch(() => {
|
||||
// Ignore
|
||||
})
|
||||
}, (error) => {
|
||||
this.captureInProgress = false
|
||||
throw error
|
||||
|
|
|
|||
|
|
@ -9,6 +9,20 @@ const {
|
|||
limitPromises
|
||||
} = require('../core/Utils')
|
||||
|
||||
function buildResponseError (xhr, error) {
|
||||
// No error message
|
||||
if (!error) error = new Error('Upload error')
|
||||
// Got an error message string
|
||||
if (typeof error === 'string') error = new Error(error)
|
||||
// Got something else
|
||||
if (!(error instanceof Error)) {
|
||||
error = Object.assign(new Error('Upload error'), { data: error })
|
||||
}
|
||||
|
||||
error.request = xhr
|
||||
return error
|
||||
}
|
||||
|
||||
module.exports = class XHRUpload extends Plugin {
|
||||
constructor (uppy, opts) {
|
||||
super(uppy, opts)
|
||||
|
|
@ -29,7 +43,7 @@ module.exports = class XHRUpload extends Plugin {
|
|||
method: 'post',
|
||||
metaFields: null,
|
||||
responseUrlFieldName: 'url',
|
||||
bundle: true,
|
||||
bundle: false,
|
||||
headers: {},
|
||||
locale: defaultLocale,
|
||||
timeout: 30 * 1000,
|
||||
|
|
@ -45,12 +59,14 @@ module.exports = class XHRUpload extends Plugin {
|
|||
* @param {XMLHttpRequest | respObj} responseObject the response object
|
||||
*/
|
||||
getResponseData (responseContent, responseObject) {
|
||||
let response = {}
|
||||
try {
|
||||
return JSON.parse(responseContent)
|
||||
response = JSON.parse(responseContent)
|
||||
} catch (err) {
|
||||
this.uppy.log(err, 'error')
|
||||
return err
|
||||
}
|
||||
|
||||
return response
|
||||
},
|
||||
/**
|
||||
*
|
||||
|
|
@ -168,7 +184,7 @@ module.exports = class XHRUpload extends Plugin {
|
|||
|
||||
const timer = this.createProgressTimeout(opts.timeout, (error) => {
|
||||
xhr.abort()
|
||||
this.uppy.emit('upload-error', file.id, error)
|
||||
this.uppy.emit('upload-error', file, error)
|
||||
reject(error)
|
||||
})
|
||||
|
||||
|
|
@ -200,10 +216,18 @@ module.exports = class XHRUpload extends Plugin {
|
|||
timer.done()
|
||||
|
||||
if (ev.target.status >= 200 && ev.target.status < 300) {
|
||||
const resp = opts.getResponseData(xhr.responseText, xhr)
|
||||
const uploadURL = resp[opts.responseUrlFieldName]
|
||||
const body = opts.getResponseData(xhr.responseText, xhr)
|
||||
const uploadURL = body[opts.responseUrlFieldName]
|
||||
|
||||
this.uppy.emit('upload-success', file.id, resp, uploadURL)
|
||||
const response = {
|
||||
status: ev.target.status,
|
||||
body,
|
||||
uploadURL
|
||||
}
|
||||
|
||||
this.uppy.setFileState(file.id, { response })
|
||||
|
||||
this.uppy.emit('upload-success', file, body, uploadURL)
|
||||
|
||||
if (uploadURL) {
|
||||
this.uppy.log(`Download ${file.name} from ${file.uploadURL}`)
|
||||
|
|
@ -211,9 +235,17 @@ module.exports = class XHRUpload extends Plugin {
|
|||
|
||||
return resolve(file)
|
||||
} else {
|
||||
const error = opts.getResponseError(xhr.responseText, xhr) || new Error('Upload error')
|
||||
error.request = xhr
|
||||
this.uppy.emit('upload-error', file.id, error)
|
||||
const body = opts.getResponseData(xhr)
|
||||
const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr))
|
||||
|
||||
const response = {
|
||||
status: ev.target.status,
|
||||
body
|
||||
}
|
||||
|
||||
this.uppy.setFileState(file.id, { response })
|
||||
|
||||
this.uppy.emit('upload-error', file, error)
|
||||
return reject(error)
|
||||
}
|
||||
})
|
||||
|
|
@ -222,8 +254,8 @@ module.exports = class XHRUpload extends Plugin {
|
|||
this.uppy.log(`[XHRUpload] ${id} errored`)
|
||||
timer.done()
|
||||
|
||||
const error = opts.getResponseError(xhr.responseText, xhr) || new Error('Upload error')
|
||||
this.uppy.emit('upload-error', file.id, error)
|
||||
const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr))
|
||||
this.uppy.emit('upload-error', file, error)
|
||||
return reject(error)
|
||||
})
|
||||
|
||||
|
|
@ -292,7 +324,7 @@ module.exports = class XHRUpload extends Plugin {
|
|||
socket.on('success', (data) => {
|
||||
const resp = opts.getResponseData(data.response.responseText, data.response)
|
||||
const uploadURL = resp[opts.responseUrlFieldName]
|
||||
this.uppy.emit('upload-success', file.id, resp, uploadURL)
|
||||
this.uppy.emit('upload-success', file, resp, uploadURL)
|
||||
socket.close()
|
||||
return resolve()
|
||||
})
|
||||
|
|
@ -300,7 +332,7 @@ module.exports = class XHRUpload extends Plugin {
|
|||
socket.on('error', (errData) => {
|
||||
const resp = errData.response
|
||||
const error = resp ? opts.getResponseError(resp.responseText, resp) : new Error(errData.error)
|
||||
this.uppy.emit('upload-error', file.id, error)
|
||||
this.uppy.emit('upload-error', file, error)
|
||||
reject(new Error(errData.error))
|
||||
})
|
||||
})
|
||||
|
|
@ -329,7 +361,7 @@ module.exports = class XHRUpload extends Plugin {
|
|||
|
||||
const emitError = (error) => {
|
||||
files.forEach((file) => {
|
||||
this.uppy.emit('upload-error', file.id, error)
|
||||
this.uppy.emit('upload-error', file, error)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -359,7 +391,7 @@ module.exports = class XHRUpload extends Plugin {
|
|||
if (ev.target.status >= 200 && ev.target.status < 300) {
|
||||
const resp = this.opts.getResponseData(xhr.responseText, xhr)
|
||||
files.forEach((file) => {
|
||||
this.uppy.emit('upload-success', file.id, resp)
|
||||
this.uppy.emit('upload-success', file, resp)
|
||||
})
|
||||
return resolve()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
color: $color-white;
|
||||
background-color: $color-cornflower-blue;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
|
|
|
|||
180
website/src/_posts/2018-02-0.23.md
Normal file
180
website/src/_posts/2018-02-0.23.md
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
---
|
||||
title: "Uppy 0.23: Import from Url, refactored thumbnail generation, XHR bundle"
|
||||
date: 2018-02-12
|
||||
author: arturi
|
||||
published: false
|
||||
---
|
||||
|
||||
Hi all! We are back from holidays with a shiny new `0.23.0` release for you! It’s packed with a new `Url` plugin (imports files from urls), refactored `ThumbnailGenerator`, `Webcam` improvements and more.
|
||||
|
||||
## Import from Url
|
||||
|
||||
Meet our new “Provider” plugin, `Url`. It’s simple, but powerful: paste a link to any file on the web, and Uppy with Uppy Server will upload it wherever you need.
|
||||
|
||||
```js
|
||||
uppy.use(Url, { host: YOUR_UPPY_SERVER_URL })
|
||||
```
|
||||
|
||||
<figure class="wide"><video alt="Demo video showing Uppy with Url plugin that imports files from urls" controls autoplay><source src="/images/blog/0.23/uppy-url-demo.mp4" type="video/mp4">Your browser does not support the video tag, you can <a href="/images/blog/0.23/uppy-url-demo.mp4">download the video</a> to watch it.</video></figure>
|
||||
|
||||
<!--more-->
|
||||
|
||||
## ThumbnailGenerator
|
||||
|
||||
Thumbnail generation has been refactored to a separate `ThumbnailGenerator` plugin, thanks to a PR from [@richardwillars](https://github.com/richardwillars). So Uppy Core is now more lightweight for when you don’t need previews.
|
||||
|
||||
`ThumbnailGenerator` is still bundled with our most feature complete Dashboard plugin, so you don’t have to change anything if you were using that.
|
||||
|
||||
## Processing results
|
||||
|
||||
Processing results, from encoding plugins like Transloadit, are now added to `complete` event:
|
||||
|
||||
```js
|
||||
uppy.on('complete', (result) => {
|
||||
console.log(result)
|
||||
// result: {
|
||||
// failed: [...],
|
||||
// successful: [...],
|
||||
// transloadit: [...],
|
||||
// uploadID: "cjdnzj2zy0000c___iewu9m5y"
|
||||
//}
|
||||
})
|
||||
```
|
||||
|
||||
And to upload promise too:
|
||||
|
||||
```js
|
||||
uppy.upload().then((result) => {
|
||||
console.log(result)
|
||||
})
|
||||
```
|
||||
|
||||
You can still use events like `transloadit:result`, its just easier when things are in the same place. Plugins can use new `uppy.addResultData()` API to add data to the `result`, like so:
|
||||
|
||||
```js
|
||||
uppy.addResultData(uploadID, { transloadit: assemblies })
|
||||
```
|
||||
|
||||
## Webcam
|
||||
|
||||
Webcam plugin now mirrors image previews by default, so that when you are making a selfie and wave with your right hand, you see that right hand of yours in the Webcam preview, just like in the mirror. This option can be disabled via `.use(Webcam, { mirror: false }`. Note that the resulting image will not be mirrored, so your hand will actually be waving on the left. This mimics the behaviour of smarphone selfie cameras.
|
||||
|
||||
We’ve also added an option to select which camera will be used to capture pictures or video: [facingMode](https://uppy.io/docs/webcam/#facingMode-39-user-39) set to `user` (front camera) by default.
|
||||
|
||||
## Server
|
||||
|
||||
- You can now specify a config path when starting the standalone Uppy Server like so `uppy-server --config /path/to/uppyconf.json`. The config file is expected to be a json file with the same schema as the [uppy-server options](https://uppy.io/docs/server/#Options).
|
||||
- A periodic cleanup job has been added to Uppy Server, to delete stale upload files from the specified `filePath`. Even though we'd rarely expect uploaded files to go undeleted immediately, there could be cases where an error occurs during an upload, and so uppy-server leaves the file undeleted to give room for upload retries. With the cleanup job in place, we can ensure that this file would eventually be deleted after the file upload is done.
|
||||
- Responses from Multipart uploads are now relayed to Uppy Client as they are received from the upload server. With this in place, you can now handle responses from local and remote XHRUpload in nearly the same way.
|
||||
|
||||
## Improvements
|
||||
|
||||
- New option to hide ProgressBar and StatusBar after upload finish (#485 / @wilkoklak)
|
||||
- Chaining API has been improved, you can now use `.on` and `.off` anywhere: `uppy.use(Dashboard).use(Tus).on('complete', handleComplete).run()`.
|
||||
- We now pass response from uppy-server upload’s endpoint.
|
||||
- Transloadit plugin now has a new `transloadit:assembly-executing` event and assembly results to to the `complete` callback (#547, #527 / @goto-bus-stop).
|
||||
- We’ve added `bundle` option to `XHRUpload` plugin send multiple files in one request (#442 / @goto-bus-stop)
|
||||
|
||||
## Fixes
|
||||
|
||||
- Fixed blank preview thumbnails for images in Safari; use slightly different stap scaling (#458, #584 / @arturi)
|
||||
- We now log in console and show an Informer message, not error in console, when file cannot be added due to restrictions (#604, #492 / @goto-bus-stop).
|
||||
- Unused files have been removed from published package (#586 / @goto-bus-stop)
|
||||
- Use empty input value so same file can be selected multiple times (@arturi / #534)
|
||||
- Fix modal and page scroll (#564 / @arturi)
|
||||
- Refactor provider views (#554 / @arturi)
|
||||
- Lots of documentation fixes, thanks to all our contributers!
|
||||
|
||||
## Misc
|
||||
|
||||
- Third-party extension for integrating Ngrx Angular state management library with Uppy has been released, [uppy-store-ngrx](https://github.com/rimlin/uppy-store-ngrx/) by @rimlin.
|
||||
- Uppy releases are now hosted on Edgly CDN by Transloadit.
|
||||
|
||||
## Press
|
||||
|
||||
Uppy has made some appearances on the internet recently.
|
||||
|
||||
**[Better File Uploads with Shrine: Direct Uploads](https://twin.github.io/better-file-uploads-with-shrine-direct-uploads)**
|
||||
|
||||
On how to set up Uppy with [Shrine](http://shrinerb.com/), a file attachment toolkit for Ruby applications. It starts off with an example of a plain `<form>` upload and builds up to a more advanced experience:
|
||||
|
||||
> - When the user submits the form with selected files, there is no indicator telling them when the upload will finish.
|
||||
> - When the user is uploading multiple files at once and the request happens to get aborted, it’s not possible to keep the files that were uploaded so far, because all files are sent in a single request. In other words, multiple uploads are all-or-nothing.
|
||||
> - Files are validated only after they have been uploaded, which means the user needs to wait until the upload finishes before they can know whether their file was even valid.
|
||||
>
|
||||
> We can improve that by asynchronously starting to upload files on the client side as soon as they’re selected. This also gives users the ability to continue filling in other fields while files are being uploaded, because the UI isn’t blocked during the upload.
|
||||
>
|
||||
> There are many popular JavaScript file upload libraries out there – jQuery-File-Upload, Dropzone.js, FineUploader etc. – but the one you should use with Shrine is definitely Uppy 🐶. Uppy is a modular library that knows how to upload files to a custom endpoint on your app, to Amazon S3, or even to a resumable endpoint, providing progress bars, drag & drop functionality, image previews, file validations etc, all while making as few assumptions as possible.
|
||||
|
||||
**Featured [in Javascript Daily](https://twitter.com/JavaScriptDaily/status/950348390268919809)**
|
||||
|
||||
**[Trending on GitHub](https://twitter.com/uppy_io/status/946485101541683201) accross all languages**
|
||||
|
||||
**[#2 Product of the Week](https://www.producthunt.com/posts/uppy-io) on ProductHunt**
|
||||
> You literally solved my biggest problem, thank you for developing such a great project.
|
||||
<cite>— Chetan Menaria</cite>
|
||||
|
||||
> Writing file upload functionality is always a pain in the arse. I’ll be checking this out today and integrating into SongBox if it’s good.
|
||||
<cite>— Mick</cite>
|
||||
|
||||
**[#2 New Tool of The Year](https://stackshare.io/posts/top-developer-tools-2017) on Stackshare**
|
||||
> Uppy closed out the year as the #1 trending GitHub JavaScript project, so we think it’s only getting started.
|
||||
|
||||
## Full Changelog
|
||||
|
||||
Here is the full list of changes for version `0.23.0` (and patches `0.22.1`—`0.22.5`):
|
||||
|
||||
- core: Allow plugins to add data to result object. Return `processing` results among with `upload` results in `complete` event and `upload()` promise (#527 / @goto-bus-stop)
|
||||
- core: Move limiting to different point, to fix StatusBar and other UI issues #468 (#524, #526 / @goto-bus-stop)
|
||||
- core: Add uploadID to complete event (#569 / @richardwillars)
|
||||
- core: Allow chanining after .on() and .off() to improve ergonomics (#597 / @arturi)
|
||||
- core: Allow user to override sass variables (#555 / @chao)
|
||||
- core: Move preview generation to separate plugin, add queuing (#431 / @richardwillars)
|
||||
- core: Third-party extension, uppy-store-ngrx https://github.com/rimlin/uppy-store-ngrx/ (#532 / @rimlin)
|
||||
- core: Warn, not error, when file cannot be added due to restrictions? (#604, #492 / @goto-bus-stop)
|
||||
- dashboard: Add more i18n strings (#565 / @arturi)
|
||||
- dashboard: Fix modal and page scroll (#564 / @arturi)
|
||||
- dashboard: Refactor provider views (#554 / @arturi)
|
||||
- dashboard: Restore focus after modal has been closed (#536 / @arturi)
|
||||
- dashboard: Use empty input value so same file can be selected multiple times (@arturi / #534)
|
||||
- dashboard: Use more accessible tip lib microtip (#536 / @arturi)
|
||||
- docs: Add PHP snippets to XHRUpload docs (#567 / @goto-bus-stop)
|
||||
- meta: Added instruction to fork the repo first (#512 / muhammadInam)
|
||||
- meta: Automatically host releases on edgly and use that as our main CDN (#558 / @kvz)
|
||||
- meta: Dependency version updates (#523 / @goto-bus-stop)
|
||||
- meta: Remove unused files from published package (#586 / @goto-bus-stop)
|
||||
- s3: Respect `limit` option for upload parameter requests too; fix isXml() check when no content-type is available (#545, #544, #528 / @goto-bus-stop)
|
||||
- statusbar: Fix status text still showing when statusbar is hidden (#525 / @goto-bus-stop)
|
||||
- test: Alter jest testPathPattern to current dir, add chai (#583 / @arturi)
|
||||
- thumbnail: Add thumbnail generation plugin (#461 / @richardwillars)
|
||||
- thumbnail: Fix blank preview thumbnails for images in Safari; use slightly different stap scaling (#458, #584 / @arturi)
|
||||
- transloadit: Add `transloadit:assembly-executing` event (#547 / @goto-bus-stop)
|
||||
- transloadit: Add assembly results to to the `complete` callback (#527 / @goto-bus-stop)
|
||||
- transloadit: Easily pass form fields (#593 / @goto-bus-stop)
|
||||
- tus: `resume: false` — don’t store url (@arturi / #507)
|
||||
- uppy-server: Detect file upload size from the server (@ifedapoolarewaju)
|
||||
- uppy-server: Fix circular json stringify error (@ifedapoolarewaju)
|
||||
- uppy-server: Load standalone server options via config path (@ifedapoolarewaju)
|
||||
- uppy-server: Pass response from uppy-server upload’s endpoint (#591 / @ifedapoolarewaju)
|
||||
- uppy-server: Schedule job to delete stale upload files (@ifedapoolarewaju)
|
||||
- uppy-server: Security audit, ask @acconut
|
||||
- uppy-server: Support localhost urls as endpoints (@ifedapoolarewaju)
|
||||
- url: New plugin that imports files from urls (#588 / @arturi, @ifedapoolarewaju)
|
||||
- webcam: Font styling for Webcam option (#509 / @muhammadInam)
|
||||
- webcam: Mirror image preview, add option to select which camera is used to capture, try filling the whole Dashboard with webcam preview image, remove URL.createObjectURL() (#574 / @arturi, @nqst)
|
||||
- website: Add Transloadit example to website (#603 / @arturi)
|
||||
- website: Doc fixes (#563 / @arturi)
|
||||
- website: Improve the Contributing guide (#578 / @arturi)
|
||||
- xhrupload: Add bundle option to send multiple files in one request (#442 / @goto-bus-stop)
|
||||
- xhrupload: Prevent files from being uploaded multiple times in separate uploads (#552 / @richardwillars)
|
||||
- xhrupload: Refactor response and error handling (#591 / @goto-bus-stop, @arturi, @ifedapoolarewaju)
|
||||
- core: Fix remote uploads (#474 / @arturi)
|
||||
- statusbar, progressbar: Add option to hide progress bar after upload finish (#485 / @wilkoklak)
|
||||
- s3: Allow passing on XHRUpload options, such as "limit" to AwsS3 Plugin (#471 / @ogtfaber)
|
||||
- XHRUpload: Fix progress with `limit`ed XHRUploads (#505 / @goto-bus-stop)
|
||||
- core: fix error when `file.type === null`, shouldn’t pass that to match (@arturi)
|
||||
- dashboard: input hidden="true" should not be focusable too (@arturi)
|
||||
- webcam: Font styling for Webcam option (#509 / @muhammadInam)
|
||||
- docs: fix reference to incorrect width/height options (#475 / @xhocquet)
|
||||
- docs: Documentation fixes and improvements (#463 / @janko-m)
|
||||
- docs: Fixed several typos in docs/server and docs/uppy (#484 / @martiuslim)
|
||||
|
|
@ -43,6 +43,18 @@ When using a POST upload with a policy document, this should be the root URL of
|
|||
The `fields` field is an object with form fields to send along with the upload request.
|
||||
For presigned PUT uploads, this should be empty.
|
||||
|
||||
### `timeout: 30 * 1000`
|
||||
|
||||
When no upload progress events have been received for this amount of milliseconds, assume the connection has an issue and abort the upload. This is passed through to [XHRUpload](/docs/xhrupload#timeout-30-1000); see its documentation page for details.
|
||||
Set to `0` to disable this check.
|
||||
|
||||
The default is 30 seconds.
|
||||
|
||||
### `limit: 0`
|
||||
|
||||
Limit the amount of uploads going on at the same time. This is passed through to [XHRUpload](/docs/xhrupload#limit-0); see its documentation page for details.
|
||||
Set to `0` to disable limiting.
|
||||
|
||||
## S3 Bucket configuration
|
||||
|
||||
S3 buckets do not allow public uploads by default.
|
||||
|
|
|
|||
|
|
@ -20,8 +20,9 @@ Dashboard is a universal UI plugin for Uppy:
|
|||
```js
|
||||
uppy.use(Dashboard, {
|
||||
target: 'body',
|
||||
trigger: '#uppy-select-files',
|
||||
inline: false,
|
||||
trigger: '#uppy-select-files',
|
||||
plugins: [],
|
||||
maxWidth: 750,
|
||||
maxHeight: 550,
|
||||
semiTransparent: false,
|
||||
|
|
@ -75,6 +76,19 @@ By default Dashboard will be rendered as a modal, which is opened via clicking o
|
|||
|
||||
String with a CSS selector for a button that will trigger opening Dashboard modal. Multiple buttons or links can be used, if it’s a class selector (`.uppy-choose`, for example).
|
||||
|
||||
### `plugins: []`
|
||||
|
||||
List of plugin IDs that should be shown in the Dashboard's top bar. For example, to show the Webcam plugin:
|
||||
|
||||
```js
|
||||
uppy.use(Webcam)
|
||||
uppy.use(Dashboard, {
|
||||
plugins: ['Webcam']
|
||||
})
|
||||
```
|
||||
|
||||
Of course, you can also use the `target` option in the Webcam plugin to achieve this. However, that does not work with the React components. The `target` option may be changed in the future to only accept DOM elements, so it is recommended to use this `plugins` array instead.
|
||||
|
||||
### `maxWidth: 750`
|
||||
|
||||
Maximum width of the Dashboard in pixels. Used when `inline: true`.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ title: "Form"
|
|||
permalink: docs/form/
|
||||
---
|
||||
|
||||
Form plugin collects metadata from any specified `<form>` element, right before Uppy begins uploading/processing files. And then optionally appends results back to the form. Currently the appended result is a strigified version of a [`result`](docs/uppy/#uppy-upload) returned from `uppy.upload()` or `complete` event.
|
||||
Form plugin collects metadata from any specified `<form>` element, right before Uppy begins uploading/processing files. And then optionally appends results back to the form. Currently the appended result is a stringified version of a [`result`](docs/uppy/#uppy-upload) returned from `uppy.upload()` or `complete` event.
|
||||
|
||||
## Options
|
||||
|
||||
|
|
|
|||
|
|
@ -48,12 +48,12 @@ Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly
|
|||
1\. Add a script to the bottom of `<body>`:
|
||||
|
||||
``` html
|
||||
<script src="https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.min.js"></script>
|
||||
<script src="https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.min.js"></script>
|
||||
```
|
||||
|
||||
2\. Add CSS to `<head>`:
|
||||
``` html
|
||||
<link href="https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.min.css" rel="stylesheet">
|
||||
<link href="https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.min.css" rel="stylesheet">
|
||||
```
|
||||
|
||||
3\. Initialize:
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ Plugins are what makes Uppy useful: they help select, manipulate and upload file
|
|||
|
||||
Each plugin can have any number of options (please see specific plugin for details), but these are shared between some:
|
||||
|
||||
### `id`
|
||||
|
||||
A unique string identifying the plugin. By default, the plugin's name is used, so usually it does not need to be configured manually. Use this if you need to add multiple plugins of the same type.
|
||||
|
||||
### `target`
|
||||
|
||||
Can be a `string` CSS selector, a DOM element, or a Plugin class. Consider the following example, where `DragDrop` plugin will be rendered into a `body` element:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ On top of all the [Dashboard][] options, the `<DashboardModal />` plugin adds tw
|
|||
|
||||
- `open` - Boolean true or false, setting this to `true` opens the modal and setting it to `false` closes it.
|
||||
- `onRequestClose` - Callback called when the user attempts to close the modal, either by clicking the close button or by clicking outside the modal (if the `closeModalOnClickOutside` prop is set).
|
||||
- `plugins` - Array of plugins you need to use in Dashboard. Example: `plugins={['Webcam']}`.
|
||||
|
||||
```js
|
||||
class MusicUploadButton extends React.Component {
|
||||
|
|
@ -40,6 +41,8 @@ class MusicUploadButton extends React.Component {
|
|||
}
|
||||
|
||||
render () {
|
||||
this.props.uppy.use(Webcam); // the same as this.props.uppy.use(Webcam, { id: "Webcam" });
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.handleOpen}>Upload some music</button>
|
||||
|
|
@ -48,6 +51,7 @@ class MusicUploadButton extends React.Component {
|
|||
closeModalOnClickOutside
|
||||
open={this.state.modalOpen}
|
||||
onRequestClose={this.handleClose}
|
||||
plugins={['Webcam']}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,39 @@ Best used together with a simple file source plugin, such as [FileInput][] or [D
|
|||
|
||||
## Options
|
||||
|
||||
```js
|
||||
uppy.use(StatusBar, {
|
||||
target: 'body',
|
||||
hideUploadButton: false,
|
||||
showProgressDetails: false,
|
||||
hideAfterFinish: true
|
||||
locale: {
|
||||
strings: {
|
||||
uploading: 'Uploading',
|
||||
uploadComplete: 'Upload complete',
|
||||
uploadFailed: 'Upload failed',
|
||||
pleasePressRetry: 'Please press Retry to upload again',
|
||||
paused: 'Paused',
|
||||
error: 'Error',
|
||||
retry: 'Retry',
|
||||
pressToRetry: 'Press to retry',
|
||||
retryUpload: 'Retry upload',
|
||||
resumeUpload: 'Resume upload',
|
||||
cancelUpload: 'Cancel upload',
|
||||
pauseUpload: 'Pause upload',
|
||||
uploadXFiles: {
|
||||
0: 'Upload %{smart_count} file',
|
||||
1: 'Upload %{smart_count} files'
|
||||
},
|
||||
uploadXNewFiles: {
|
||||
0: 'Upload +%{smart_count} file',
|
||||
1: 'Upload +%{smart_count} files'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### `target: null`
|
||||
|
||||
DOM element, CSS selector, or plugin to mount the StatusBar into.
|
||||
|
|
|
|||
|
|
@ -7,40 +7,42 @@ permalink: docs/transloadit/
|
|||
|
||||
The Transloadit plugin can be used to upload files to [Transloadit](https://transloadit.com/) for all kinds of processing, such as transcoding video, resizing images, zipping/unzipping, [and more](https://transloadit.com/services/).
|
||||
|
||||
The Transloadit plugin uses the [Tus plugin](/docs/tus) for the uploading itself.
|
||||
To upload files to Transloadit directly, both the Tus and Transloadit plugins must be used:
|
||||
[Try it live](/examples/transloadit/)
|
||||
|
||||
```js
|
||||
// The `resume: false` option _must_ be provided to the Tus plugin.
|
||||
uppy.use(Tus, {
|
||||
resume: false
|
||||
})
|
||||
uppy.use(Transloadit, {
|
||||
// Transloadit plugin options
|
||||
service: 'https://api2.transloadit.com',
|
||||
waitForEncoding: false,
|
||||
waitForMetadata: false,
|
||||
importFromUploadURLs: false,
|
||||
alwaysRunAssembly: false,
|
||||
params: null,
|
||||
signature: null,
|
||||
fields: {}
|
||||
})
|
||||
```
|
||||
|
||||
NB: It is not required to use the `Tus` plugin if [importFromUploadURLs](#importFromUploadURLs) is enabled.
|
||||
As of Uppy 0.24 the Transloadit plugin includes the [Tus](/docs/tus) plugin to handle the uploading, so you no longer have to add it manually.
|
||||
|
||||
## Options
|
||||
|
||||
### `service`
|
||||
|
||||
The Transloadit API URL to use. Defaults to `https://api2.transloadit.com`, which will attempt to route traffic efficiently based on where your users are. You can set this to something like `https://api2-us-east-1.transloadit.com` if you want to use a particular region.
|
||||
|
||||
### `waitForEncoding`
|
||||
|
||||
Whether to wait for all assemblies to complete before completing the upload.
|
||||
Whether to wait for all Assemblies to complete before completing the upload.
|
||||
|
||||
### `waitForMetadata`
|
||||
|
||||
Whether to wait for metadata to be extracted from uploaded files before completing the upload.
|
||||
If `waitForEncoding` is enabled, this has no effect.
|
||||
Whether to wait for metadata to be extracted from uploaded files before completing the upload. If `waitForEncoding` is enabled, this has no effect.
|
||||
|
||||
### `importFromUploadURLs`
|
||||
|
||||
Instead of uploading to Transloadit's servers directly, allow another plugin to upload files, and then import those files into the Transloadit assembly.
|
||||
Default `false`.
|
||||
Instead of uploading to Transloadit's servers directly, allow another plugin to upload files, and then import those files into the Transloadit Assembly. Default `false`.
|
||||
|
||||
When enabling this option, Transloadit will *not* configure the Tus plugin to upload to Transloadit.
|
||||
Instead, a separate upload plugin must be used.
|
||||
Once the upload completes, the Transloadit plugin adds the uploaded file to the assembly.
|
||||
When enabling this option, Transloadit will *not* configure the Tus plugin to upload to Transloadit. Instead, a separate upload plugin must be used. Once the upload completes, the Transloadit plugin adds the uploaded file to the Assembly.
|
||||
|
||||
For example, to upload files to an S3 bucket and then transcode them:
|
||||
|
||||
|
|
@ -53,36 +55,78 @@ uppy.use(AwsS3, {
|
|||
uppy.use(Transloadit, {
|
||||
importFromUploadURLs: true,
|
||||
params: {
|
||||
auth: { key: /* secret */ },
|
||||
template_id: /* secret */
|
||||
auth: { key: 'YOUR_API_KEY' },
|
||||
template_id: 'YOUR_TEMPLATE_ID'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
In order for this to work, the upload plugin must assign a publically accessible `uploadURL` property to the uploaded file object.
|
||||
The Tus and S3 plugins both do this—for the XHRUpload plugin, you may have to specify a custom `getUploadResponse` function.
|
||||
In order for this to work, the upload plugin must assign a publically accessible `uploadURL` property to the uploaded file object. The Tus and S3 plugins both do this—for the XHRUpload plugin, you may have to specify a custom `getUploadResponse` function.
|
||||
|
||||
### `alwaysRunAssembly`
|
||||
|
||||
When true, always create and run an Assembly when `uppy.upload()` is called, even if no files were selected. This allows running Assemblies that do not receive files, but instead use a robot like [`/s3/import`](https://transloadit.com/docs/transcoding/#s3-import) to download the files from elsewhere, for example for a bulk transcoding job.
|
||||
|
||||
### `params`
|
||||
|
||||
The assembly parameters to use for the upload.
|
||||
The Assembly parameters to use for the upload. See the Transloadit documentation on [Assembly Instructions](https://transloadit.com/docs/#14-assembly-instructions). `params` should be a plain JavaScript object, or a JSON string if you are using the [`signature`](#signature) option.
|
||||
|
||||
The `auth.key` Assembly parameter is required. You can also use the `steps` or `template_id` options here as described in the Transloadit documentation.
|
||||
|
||||
```js
|
||||
uppy.use(Transloadit, {
|
||||
params: {
|
||||
auth: { key: 'YOUR_TRANSLOADIT_KEY' },
|
||||
steps: {
|
||||
encode: {
|
||||
robot: '/video/encode',
|
||||
use: {
|
||||
steps: [ ':original' ],
|
||||
fields: [ 'file_input_field2' ]
|
||||
},
|
||||
preset: 'iphone'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### `signature`
|
||||
|
||||
An optional signature for the assembly parameters.
|
||||
An optional signature for the Assembly parameters. See the Transloadit documentation on [Signature Authentication](https://transloadit.com/docs/#26-signature-authentication).
|
||||
|
||||
If a `signature` is provided, `params` should be a JSON string instead of a JavaScript object, as otherwise the generated JSON in the browser may be different from the JSON string that was used to generate the signature.
|
||||
|
||||
### `fields`
|
||||
|
||||
An object of form fields to send along to the assembly.
|
||||
An object of form fields to send along to the Assembly. Keys are field names, and values are field values. See also the Transloadit documentation on [Form Fields In Instructions](https://transloadit.com/docs/#23-form-fields-in-instructions).
|
||||
|
||||
```js
|
||||
uppy.use(Transloadit, {
|
||||
...,
|
||||
fields: {
|
||||
message: 'This is a form field'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
You can also pass an array of field names to send global or file metadata along to the Assembly. Global metadata is set using the [`meta` option](/docs/uppy/#meta) in the Uppy constructor, or using the [`setMeta` method](/docs/uppy/#uppy-setMeta-data). File metadata is set using the [`setFileMeta`](/docs/uppy/#uppy-setFileMeta-fileID-data) method. The [Form](/docs/form) plugin also sets global metadata based on the values of `<input />`s in the form, providing a handy way to use values from HTML form fields:
|
||||
|
||||
```js
|
||||
uppy.use(Form, { target: 'form#my-form', getMetaFromForm: true })
|
||||
uppy.use(Transloadit, {
|
||||
fields: ['field_name', 'other_field_name'],
|
||||
params: { ... }
|
||||
})
|
||||
```
|
||||
|
||||
Form fields can also be computed dynamically using custom logic, by using the [`getAssemblyOptions(file)`](/docs/transloadit/#getAssemblyOptions-file) option.
|
||||
|
||||
### `getAssemblyOptions(file)`
|
||||
|
||||
While `params`, `signature`, and `fields` must be determined ahead of time, the `getAssemblyOptions` allows using dynamically generated values for these options.
|
||||
This way, it is possible to use different assembly parameters for different files.
|
||||
While `params`, `signature`, and `fields` must be determined ahead of time, the `getAssemblyOptions` allows using dynamically generated values for these options. This way, it is possible to use different Assembly parameters for different files, or to use some user input in an Assembly.
|
||||
|
||||
A custom `getAssemblyOptions()` option should return an object or a Promise for an object with properties `{ params, signature, fields }`.
|
||||
For example, to add a field with some user-provided data from the `MetaData` plugin:
|
||||
A custom `getAssemblyOptions()` option should return an object or a Promise for an object with properties `{ params, signature, fields }`. For example, to add a field with some user-provided data from the `MetaData` plugin:
|
||||
|
||||
```js
|
||||
uppy.use(MetaData, {
|
||||
|
|
@ -105,10 +149,27 @@ uppy.use(Transloadit, {
|
|||
})
|
||||
```
|
||||
|
||||
Now, the `${fields.caption}` variable will be available in the assembly template.
|
||||
Now, the `${fields.caption}` variable will be available in the Assembly template.
|
||||
|
||||
`getAssemblyOptions()` may also return a Promise, so it could retrieve signed assembly parameters from a server.
|
||||
For example, assuming an endpoint `/transloadit-params` that responds with a JSON object with `{ params, signature }` properties:
|
||||
Combine the `getAssemblyOptions()` option with the [Form](/docs/form) plugin to pass user input from a `<form>` to a Transloadit Assembly:
|
||||
|
||||
```js
|
||||
// This will add form field values to each file's `.meta` object:
|
||||
uppy.use(Form, { getMetaFromForm: true })
|
||||
uppy.use(Transloadit, {
|
||||
getAssemblyOptions (file) {
|
||||
return {
|
||||
params: { ... },
|
||||
// Pass through the fields you need:
|
||||
fields: {
|
||||
message: file.meta.message
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
`getAssemblyOptions()` may also return a Promise, so it could retrieve signed Assembly parameters from a server. For example, assuming an endpoint `/transloadit-params` that responds with a JSON object with `{ params, signature }` properties:
|
||||
|
||||
```js
|
||||
uppy.use(Transloadit, {
|
||||
|
|
@ -124,12 +185,12 @@ uppy.use(Transloadit, {
|
|||
|
||||
### `transloadit:assembly-created`
|
||||
|
||||
Fired when an assembly is created.
|
||||
Fired when an Assembly is created.
|
||||
|
||||
**Parameters**
|
||||
|
||||
- `assembly` - The initial assembly status.
|
||||
- `fileIDs` - The IDs of the files that will be uploaded to this assembly.
|
||||
- `assembly` - The initial [Assembly Status][assembly-status].
|
||||
- `fileIDs` - The IDs of the files that will be uploaded to this Assembly.
|
||||
|
||||
```js
|
||||
uppy.on('transloadit:assembly-created', (assembly, fileIDs) => {
|
||||
|
|
@ -148,7 +209,7 @@ Fired when Transloadit has received an upload.
|
|||
**Parameters**
|
||||
|
||||
- `file` - The Transloadit file object that was uploaded.
|
||||
- `assembly` - The assembly status of the assembly the file was uploaded to.
|
||||
- `assembly` - The [Assembly Status][assembly-status] of the Assembly the file was uploaded to.
|
||||
|
||||
### `transloadit:assembly-executing`
|
||||
|
||||
|
|
@ -160,15 +221,15 @@ Fired when Transloadit has received all uploads, and is now executing the Assemb
|
|||
|
||||
### `transloadit:result`
|
||||
|
||||
Fired when a result came in from an assembly.
|
||||
Fired when a result came in from an Assembly.
|
||||
|
||||
**Parameters**
|
||||
|
||||
- `stepName` - The name of the assembly step that generated this result.
|
||||
- `stepName` - The name of the Assembly step that generated this result.
|
||||
- `result` - The result object from Transloadit.
|
||||
This result object contains one additional property, namely `localId`.
|
||||
This is the ID of the file in Uppy's local state, and can be used with `uppy.getFile(id)`.
|
||||
- `assembly` - The assembly status of the assembly that generated this result.
|
||||
- `assembly` - The [Assembly Status][assembly-status] of the Assembly that generated this result.
|
||||
|
||||
```js
|
||||
uppy.on('transloadit:result', (stepName, result) => {
|
||||
|
|
@ -184,11 +245,11 @@ uppy.on('transloadit:result', (stepName, result) => {
|
|||
|
||||
### `transloadit:complete`
|
||||
|
||||
Fired when an assembly completed.
|
||||
Fired when an Assembly completed.
|
||||
|
||||
**Parameters**
|
||||
|
||||
- `assembly` - The final assembly status of the completed assembly.
|
||||
- `assembly` - The final [Assembly Status][assembly-status] of the completed Assembly.
|
||||
|
||||
```js
|
||||
uppy.on('transloadit:complete', (assembly) => {
|
||||
|
|
@ -196,3 +257,5 @@ uppy.on('transloadit:complete', (assembly) => {
|
|||
console.log(assembly.results)
|
||||
})
|
||||
```
|
||||
|
||||
[assembly-status]: https://transloadit.com/docs/api-docs/#assembly-status-response
|
||||
|
|
|
|||
|
|
@ -318,6 +318,36 @@ Subscribe to an uppy-event. See below for the full list of events.
|
|||
|
||||
Uppy exposes events that you can subscribe to in your app:
|
||||
|
||||
### `file-added`
|
||||
|
||||
Fired each time file is added.
|
||||
|
||||
```javascript
|
||||
uppy.on('file-added', (file) => {
|
||||
console.log('Added file', file)
|
||||
})
|
||||
```
|
||||
|
||||
### `file-removed`
|
||||
|
||||
Fired each time file is removed.
|
||||
|
||||
```javascript
|
||||
uppy.on('file-removed', (file) => {
|
||||
console.log('Removed file', file)
|
||||
})
|
||||
```
|
||||
|
||||
### `upload`
|
||||
|
||||
Fired when upload starts.
|
||||
|
||||
```javascript
|
||||
uppy.on('upload', () => {
|
||||
console.log('Starting upload...')
|
||||
})
|
||||
```
|
||||
|
||||
### `upload-progress`
|
||||
|
||||
Fired each time file upload progress is available, `data` object looks like this:
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ headers: {
|
|||
|
||||
Send all files in a single multipart request. When `bundle` is `true`, `formData` must also be set to `true`.
|
||||
|
||||
> Note: When `bundle` is `true`, file metadata is **not** sent to the endpoint. This is because it's not obvious how metadata should be sent when there are multiple files in a single request. If you need this, please open an issue and we'll try to figure it out together.
|
||||
|
||||
All files will be appended to the provided `fieldName` field in the request. To upload files on different fields, use [`uppy.setFileState()`](/docs/uppy#uppy-setFileState-fileID-state) to set the `xhrUpload.fieldName` property on the file:
|
||||
|
||||
```js
|
||||
|
|
@ -72,11 +74,16 @@ uppy.setFileState(otherFileID, {
|
|||
|
||||
### `getResponseData(xhr)`
|
||||
|
||||
When an upload has completed, Uppy will extract response data from the upload endpoint and send it back in the `upload-success` event:
|
||||
When an upload has completed, Uppy will extract response data from the upload endpoint. This response data will be available on the file's `.response` property, and be emitted in the `upload-success` event:
|
||||
|
||||
```js
|
||||
uppy.on('upload-success', (fileId, resp, uploadURL) => {
|
||||
// do something with resp
|
||||
uppy.getFile(fileID).response
|
||||
// { status: HTTP status code,
|
||||
// body: extracted response data }
|
||||
|
||||
uppy.on('upload-success', (fileID, body) => {
|
||||
// do something with extracted response data
|
||||
// (`body` is equivalent to `uppy.getFile(fileID).response.body`)
|
||||
})
|
||||
```
|
||||
|
||||
|
|
@ -101,7 +108,7 @@ getResponseData (xhr) {
|
|||
}
|
||||
```
|
||||
|
||||
### `getResponseError(xhr)`
|
||||
### `getResponseError(xhr.responseText, xhr)`
|
||||
|
||||
If the upload endpoint responds with a non-2xx status code, the upload is assumed to have failed.
|
||||
The endpoint might have responded with some information about the error, though.
|
||||
|
|
@ -110,7 +117,7 @@ Pass in a `getResponseError` function to extract error data from the `XMLHttpReq
|
|||
For example, if the endpoint responds with a JSON object containing a `{ message }` property, this would show that message to the user:
|
||||
|
||||
```js
|
||||
getResponseError (xhr) {
|
||||
getResponseError (responseText, xhr) {
|
||||
return new Error(JSON.parse(xhr.response).message)
|
||||
}
|
||||
```
|
||||
|
|
@ -131,6 +138,31 @@ The default is 30 seconds.
|
|||
|
||||
Limit the amount of uploads going on at the same time. Passing `0` means no limit.
|
||||
|
||||
## POST Parameters / Form Fields
|
||||
|
||||
When using XHRUpload with `formData: true`, file metadata is sent along with each upload request. You can set metadata for a file using [`uppy.setFileMeta(fileID, data)`](/docs/uppy#uppy-setFileMeta-fileID-data), or for all files simultaneously using [`uppy.setMeta(data)`](/docs/uppy#uppy-setMeta-data).
|
||||
|
||||
It may be useful to set metadata depending on some file properties, such as the size. You can use the [`file-added`](/docs/uppy/#file-added) event and the [`uppy.setFileMeta(fileID, data)`](/docs/uppy#uppy-setFileMeta-fileID-data) method to do this:
|
||||
|
||||
```js
|
||||
uppy.on('file-added', (file) => {
|
||||
uppy.setFileMeta(file.id, {
|
||||
size: file.size
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
Now, a form field named `size` will be sent along to the [`endpoint`](#endpoint-39-39) once the upload starts.
|
||||
|
||||
By default, all metadata is sent, including Uppy's default `name` and `type` metadata. If you do not want the `name` and `type` metadata properties to be sent to your upload endpoint, you can use the [`metaFields`](#metaFields-null) option to restrict the field names that should be sent.
|
||||
|
||||
```js
|
||||
uppy.use(XHRUpload, {
|
||||
// Only send our own `size` metadata field.
|
||||
metaFields: ['size']
|
||||
})
|
||||
```
|
||||
|
||||
## Uploading to a PHP Server
|
||||
|
||||
The XHRUpload plugin works similarly to a `<form>` upload. You can use the `$_FILES` variable on the server to work with uploaded files. See the PHP documentation on [Handling file uploads][PHP.file-upload].
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ const Dashboard = require('uppy/lib/plugins/Dashboard')
|
|||
const GoogleDrive = require('uppy/lib/plugins/GoogleDrive')
|
||||
const Dropbox = require('uppy/lib/plugins/Dropbox')
|
||||
const Instagram = require('uppy/lib/plugins/Instagram')
|
||||
const Url = require('uppy/lib/plugins/Url')
|
||||
const Webcam = require('uppy/lib/plugins/Webcam')
|
||||
const Tus = require('uppy/lib/plugins/Tus')
|
||||
|
||||
|
|
@ -58,6 +59,10 @@ function uppyInit () {
|
|||
uppy.use(Instagram, { target: Dashboard, host: UPPY_SERVER })
|
||||
}
|
||||
|
||||
if (opts.Url) {
|
||||
uppy.use(Url, { target: Dashboard, host: UPPY_SERVER })
|
||||
}
|
||||
|
||||
if (opts.Webcam) {
|
||||
uppy.use(Webcam, { target: Dashboard })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
<input type="checkbox" id="opts-GoogleDrive" checked/><label for="opts-GoogleDrive">Google Drive</label>
|
||||
<input type="checkbox" id="opts-Dropbox" checked/><label for="opts-Dropbox">Dropbox</label>
|
||||
<input type="checkbox" id="opts-Instagram" checked/><label for="opts-Instagram">Instagram</label>
|
||||
<input type="checkbox" id="opts-Url" checked/><label for="opts-Url">Url</label>
|
||||
</div>
|
||||
|
||||
<!-- Modal trigger -->
|
||||
|
|
@ -27,6 +28,7 @@
|
|||
GoogleDrive: document.querySelector('#opts-GoogleDrive'),
|
||||
Dropbox: document.querySelector('#opts-Dropbox'),
|
||||
Instagram: document.querySelector('#opts-Instagram'),
|
||||
Url: document.querySelector('#opts-Url'),
|
||||
autoProceed: document.querySelector('#opts-autoProceed'),
|
||||
restrictions: document.querySelector('#opts-restrictions')
|
||||
}
|
||||
|
|
@ -37,6 +39,7 @@
|
|||
GoogleDrive: true,
|
||||
Instagram: true,
|
||||
Dropbox: false,
|
||||
Url: true,
|
||||
autoProceed: false,
|
||||
restrictions: false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<!-- Basic Uppy styles. You can use Transloadit's CDN, Edgly:
|
||||
https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.min.css -->
|
||||
https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.min.css -->
|
||||
<link rel="stylesheet" href="/uppy/uppy.min.css">
|
||||
|
||||
<div class="UppyDragDrop"></div>
|
||||
|
||||
<!-- Load Uppy pre-built bundled version. You can use Transloadit's CDN, Edgly:
|
||||
https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.min.js -->
|
||||
https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.min.js -->
|
||||
<script src="/uppy/uppy.min.js"></script>
|
||||
<script>
|
||||
var uppy = Uppy.Core({ debug: true });
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ const Uppy = require('uppy/lib/core/Core')
|
|||
const Dashboard = require('uppy/lib/plugins/Dashboard')
|
||||
const Webcam = require('uppy/lib/plugins/Webcam')
|
||||
const Transloadit = require('uppy/lib/plugins/Transloadit')
|
||||
const Tus = require('uppy/lib/plugins/Tus')
|
||||
const Instagram = require('uppy/lib/plugins/Instagram')
|
||||
|
||||
function initUppy () {
|
||||
if (window.uppy) {
|
||||
|
|
@ -21,13 +21,12 @@ function initUppy () {
|
|||
})
|
||||
|
||||
uppy
|
||||
.use(Tus, { resume: false })
|
||||
.use(Transloadit, {
|
||||
params: {
|
||||
auth: {
|
||||
key: window.TRANSLOADIT_API_KEY
|
||||
},
|
||||
// It's always better to use a template_id and enable
|
||||
// It's more secure to use a template_id and enable
|
||||
// Signature Authentication
|
||||
steps: {
|
||||
resize: {
|
||||
|
|
@ -58,7 +57,7 @@ function initUppy () {
|
|||
target: '#uppy-dashboard-container',
|
||||
note: 'Images and video only, 1–2 files, up to 1 MB'
|
||||
})
|
||||
// .use(Instagram, { target: Uppy.Dashboard, host: 'https://api2.transloadit.com/uppy-server' })
|
||||
.use(Instagram, { target: Dashboard, host: 'https://api2.transloadit.com/uppy-server' })
|
||||
.use(Webcam, { target: Dashboard })
|
||||
.run()
|
||||
|
||||
|
|
@ -73,7 +72,9 @@ function initUppy () {
|
|||
<a href="${result.ssl_url}">View</a>
|
||||
</div>
|
||||
`
|
||||
document.getElementById('uppy-transloadit-result').appendChild(resultContainer)
|
||||
document
|
||||
.getElementById('uppy-transloadit-result')
|
||||
.appendChild(resultContainer)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,23 +6,50 @@ order: 5
|
|||
---
|
||||
|
||||
{% blockquote %}
|
||||
Here’s how you can use <a href="http://transloadit.com">Transloadit</a>’s robust file uploading and encoding backend with Uppy. This example takes your images, resizes them to 250px and adds a copyright caption text at the bottom right corner.
|
||||
Transloadit is the inventor of <a href="https://tus.io" target="_blank">tus.io</a> and Uppy. Besides a robust encoding platform, Transloadit offers hosted versions of tusd and Uppy Server. This means you can enjoy video encoding, watermarking, face detection, resumable file uploads, fetching from Instagram, etc — all without running a single server yourself.
|
||||
|
||||
This example demonstrates how to unlock Transloadit’s features within Uppy.
|
||||
{% endblockquote %}
|
||||
|
||||
<p>For this demo to work, please <a href="https://transloadit.com/signup/" target="_blank">create a free Transloadit account</a> (takes 2 minutes, you can login via GitHub too!) and enter your API key below.</p>
|
||||
<p>
|
||||
In this particular example we take your images (from Instagram), resize them to 250px and add the copyright caption text: <code>© 2018 Transloadit.com</code> at the bottom right corner.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For this demo to work you'll need a (free) Transloadit account. To get one:
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="https://transloadit.com/signup/" target="_blank">Claim your account</a>. It will activate instantly. You can also signup via GitHub to avoid password hassle.
|
||||
</li>
|
||||
<li>
|
||||
Copy the API Key that you can find on
|
||||
<a href="https://transloadit.com/accounts/credentials" target="_blank">this page</a> and paste it below.
|
||||
</li>
|
||||
<li>
|
||||
Happy encoding & fetching from Instagram :)
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
<label for="transloadit-api-key"
|
||||
style="display: block; font-size: 13px; text-transform: uppercase; font-weight: bold;">
|
||||
Transloadit API Key:</label>
|
||||
<input type="text"
|
||||
<input type="text"
|
||||
style="font-size: 15px; width: 300px; max-width: 100%; border: 0; border-bottom: 1px solid black; padding: 6px 8px; margin-bottom: 20px;"
|
||||
id="transloadit-api-key"
|
||||
placeholder="Your Transloadit API key">
|
||||
placeholder="Your Transloadit API Key">
|
||||
</p>
|
||||
|
||||
<link rel="stylesheet" href="app.css">
|
||||
<% include app.html %>
|
||||
|
||||
<hr />
|
||||
|
||||
<p id="console-wrapper">Console output (latest logs are at the top):
|
||||
<br />
|
||||
</p>
|
||||
|
||||
<script src="app.js"></script>
|
||||
<script>
|
||||
var apiKeyEl = document.getElementById('transloadit-api-key')
|
||||
|
|
@ -54,7 +81,7 @@ const Uppy = require('uppy/lib/core/Core')
|
|||
const Dashboard = require('uppy/lib/plugins/Dashboard')
|
||||
const Webcam = require('uppy/lib/plugins/Webcam')
|
||||
const Transloadit = require('uppy/lib/plugins/Transloadit')
|
||||
const Tus = require('uppy/lib/plugins/Tus')
|
||||
const Instagram = require('uppy/lib/plugins/Instagram')
|
||||
|
||||
const uppy = Uppy({
|
||||
debug: true,
|
||||
|
|
@ -68,13 +95,12 @@ const uppy = Uppy({
|
|||
})
|
||||
|
||||
uppy
|
||||
.use(Tus, { resume: false })
|
||||
.use(Transloadit, {
|
||||
params: {
|
||||
auth: {
|
||||
key: YOUR_TRANSLOADIT_API_KEY
|
||||
},
|
||||
// It's always better to use a template_id and enable
|
||||
// It's more secure to use a template_id and enable
|
||||
// Signature Authentication
|
||||
steps: {
|
||||
resize: {
|
||||
|
|
@ -99,6 +125,7 @@ uppy
|
|||
},
|
||||
waitForEncoding: true
|
||||
})
|
||||
.use(Instagram, { target: Dashboard, host: 'https://api2.transloadit.com/uppy-server' })
|
||||
.use(Dashboard, {
|
||||
inline: true,
|
||||
maxHeight: 400,
|
||||
|
|
@ -106,9 +133,6 @@ uppy
|
|||
note: 'Images and video only, 1–2 files, up to 1 MB'
|
||||
})
|
||||
.use(Webcam, { target: Dashboard })
|
||||
.run()
|
||||
|
||||
uppy
|
||||
.on('transloadit:result', (stepName, result) => {
|
||||
const file = uppy.getFile(result.localId)
|
||||
var resultContainer = document.createElement('div')
|
||||
|
|
@ -119,6 +143,9 @@ uppy
|
|||
<a href="${result.ssl_url}">View</a>
|
||||
</div>
|
||||
`
|
||||
document.getElementById('uppy-transloadit-result').appendChild(resultContainer)
|
||||
document
|
||||
.getElementById('uppy-transloadit-result')
|
||||
.appendChild(resultContainer)
|
||||
})
|
||||
.run()
|
||||
{% endcodeblock %}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
<div class="UppyForm">
|
||||
<form action="//api2.transloadit.com">
|
||||
<h5>Uppy was not loaded — slow connection, unsupported browser, werid JS error on a page — but upload still works, because HTML is cool like that</h5>
|
||||
<h5>Uppy was not loaded — slow connection, unsupported browser, weird JS error on a page — but upload still works, because HTML is cool like that</h5>
|
||||
<input type="file" name="files[]" multiple="">
|
||||
<button type="submit">Dumb Fallback Form Upload</button>
|
||||
<button type="submit">Fallback Form Upload</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
BIN
website/src/images/blog/0.23/uppy-url-demo.mp4
Normal file
BIN
website/src/images/blog/0.23/uppy-url-demo.mp4
Normal file
Binary file not shown.
|
|
@ -79,8 +79,8 @@
|
|||
<p>© <%- date(Date.now(), 'YYYY') %> <a href="https://transloadit.com" target="_blank">Transloadit</a></p>
|
||||
</footer>
|
||||
|
||||
<link href="https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.min.css" rel="stylesheet">
|
||||
<script src="https://transloadit.edgly.net/releases/uppy/v0.22.5/dist/uppy.min.js"></script>
|
||||
<link href="https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.min.css" rel="stylesheet">
|
||||
<script src="https://transloadit.edgly.net/releases/uppy/v0.23.0/dist/uppy.min.js"></script>
|
||||
|
||||
<script>
|
||||
var PROTOCOL = location.protocol === 'https:' ? 'https' : 'http'
|
||||
|
|
|
|||
|
|
@ -259,8 +259,14 @@
|
|||
|
||||
p {
|
||||
font-weight: 600;
|
||||
line-height: 1.45;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
iframe { margin: 0.7em 0; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue