Browse Source

Merge branch 'master' into feature/react-native

Kevin van Zonneveld 6 years ago
parent
commit
224af0d0d4
100 changed files with 1068 additions and 381 deletions
  1. 1 1
      .github/CONTRIBUTING.md
  2. 2 2
      .github/ISSUE_TEMPLATE/integration_help.md
  3. 65 5
      CHANGELOG.md
  4. 44 33
      README.md
  5. 10 9
      bin/sync-version-numbers
  6. 1 1
      bin/upload-to-cdn.sh
  7. 2 2
      examples/cdn-example/index.html
  8. 1 1
      examples/transloadit-textarea/index.html
  9. 2 2
      examples/uppy-with-companion/client/index.html
  10. 55 3
      package-lock.json
  11. 3 3
      package.json
  12. 4 4
      packages/@uppy/aws-s3-multipart/package.json
  13. 8 8
      packages/@uppy/aws-s3-multipart/types/aws-s3-multipart-tests.ts
  14. 5 5
      packages/@uppy/aws-s3/package.json
  15. 3 3
      packages/@uppy/aws-s3/types/aws-s3-tests.ts
  16. 1 1
      packages/@uppy/companion-client/package.json
  17. 2 2
      packages/@uppy/companion/Dockerfile
  18. 3 3
      packages/@uppy/companion/Dockerfile.test
  19. 1 1
      packages/@uppy/companion/README.md
  20. 15 2
      packages/@uppy/companion/examples/serverless/index.js
  21. 10 6
      packages/@uppy/companion/examples/serverless/package.json
  22. 9 2
      packages/@uppy/companion/examples/serverless/serverless.yml
  23. 60 0
      packages/@uppy/companion/package-lock.json
  24. 5 5
      packages/@uppy/companion/package.json
  25. 65 26
      packages/@uppy/companion/src/server/Uploader.js
  26. 2 2
      packages/@uppy/companion/src/server/provider/drive/adapter.js
  27. 1 0
      packages/@uppy/companion/src/server/provider/drive/index.js
  28. 4 0
      packages/@uppy/companion/src/server/provider/dropbox/adapter.js
  29. 2 1
      packages/@uppy/companion/src/server/provider/dropbox/index.js
  30. 2 2
      packages/@uppy/core/package.json
  31. 4 4
      packages/@uppy/core/src/__snapshots__/index.test.js.snap
  32. 14 11
      packages/@uppy/core/src/index.js
  33. 68 6
      packages/@uppy/core/src/index.test.js
  34. 8 8
      packages/@uppy/dashboard/package.json
  35. 1 0
      packages/@uppy/dashboard/src/components/Dashboard.js
  36. 26 6
      packages/@uppy/dashboard/src/style.scss
  37. 3 3
      packages/@uppy/dashboard/types/dashboard-tests.ts
  38. 3 3
      packages/@uppy/drag-drop/package.json
  39. 5 5
      packages/@uppy/dropbox/package.json
  40. 3 3
      packages/@uppy/file-input/package.json
  41. 3 3
      packages/@uppy/form/package.json
  42. 3 3
      packages/@uppy/golden-retriever/package.json
  43. 5 5
      packages/@uppy/google-drive/package.json
  44. 3 3
      packages/@uppy/informer/package.json
  45. 5 5
      packages/@uppy/instagram/package.json
  46. 3 3
      packages/@uppy/progress-bar/package.json
  47. 3 3
      packages/@uppy/provider-views/package.json
  48. 7 7
      packages/@uppy/react/package.json
  49. 2 2
      packages/@uppy/redux-dev-tools/package.json
  50. 12 12
      packages/@uppy/robodog/package.json
  51. 3 3
      packages/@uppy/status-bar/package.json
  52. 2 2
      packages/@uppy/status-bar/src/StatusBar.js
  53. 1 5
      packages/@uppy/status-bar/src/style.scss
  54. 3 3
      packages/@uppy/thumbnail-generator/package.json
  55. 6 6
      packages/@uppy/transloadit/package.json
  56. 14 14
      packages/@uppy/transloadit/types/transloadit-tests.ts
  57. 5 5
      packages/@uppy/tus/package.json
  58. 4 4
      packages/@uppy/url/package.json
  59. 1 1
      packages/@uppy/utils/package.json
  60. 3 3
      packages/@uppy/webcam/package.json
  61. 4 4
      packages/@uppy/xhr-upload/package.json
  62. 10 2
      packages/@uppy/xhr-upload/src/index.js
  63. 42 0
      packages/@uppy/xhr-upload/src/index.test.js
  64. 24 24
      packages/uppy/package.json
  65. 46 25
      packages/uppy/types/index.d.ts
  66. BIN
      rockdog.png
  67. 12 10
      test/endtoend/typescript/main.ts
  68. 1 1
      tsconfig.json
  69. 1 1
      website/src/_posts/2019-04-liftoff-08.md
  70. 49 0
      website/src/_posts/2019-04-liftoff-09.md
  71. 40 0
      website/src/_posts/2019-04-liftoff-10.md
  72. 48 0
      website/src/_posts/2019-04-liftoff-11.md
  73. 7 10
      website/src/_posts/2019-04-liftoff-12.md
  74. 17 0
      website/src/_posts/2019-04-liftoff-13.md
  75. 42 0
      website/src/_posts/2019-04-liftoff-14.md
  76. 45 0
      website/src/_posts/2019-04-liftoff-15.md
  77. 19 0
      website/src/_posts/2019-04-liftoff-31.md
  78. 1 1
      website/src/_template/contributing.md
  79. 2 2
      website/src/_template/integration_help.md
  80. 0 0
      website/src/disc.html
  81. 2 1
      website/src/docs/aws-s3-multipart.md
  82. 2 1
      website/src/docs/aws-s3.md
  83. 3 2
      website/src/docs/companion.md
  84. 1 0
      website/src/docs/contributing.md
  85. 2 1
      website/src/docs/dashboard.md
  86. 2 1
      website/src/docs/dragdrop.md
  87. 3 1
      website/src/docs/dropbox.md
  88. 2 2
      website/src/docs/fileinput.md
  89. 2 1
      website/src/docs/form.md
  90. 2 1
      website/src/docs/golden-retriever.md
  91. 3 1
      website/src/docs/google-drive.md
  92. 6 5
      website/src/docs/index.md
  93. 2 1
      website/src/docs/informer.md
  94. 3 1
      website/src/docs/instagram.md
  95. 2 1
      website/src/docs/plugins.md
  96. 2 1
      website/src/docs/progressbar.md
  97. 2 1
      website/src/docs/providers.md
  98. 2 1
      website/src/docs/react-dashboard-modal.md
  99. 2 1
      website/src/docs/react-dashboard.md
  100. 2 1
      website/src/docs/react-dragdrop.md

+ 1 - 1
.github/CONTRIBUTING.md

@@ -128,7 +128,7 @@ npm install && cd website && npm install && cd ..
 For local previews on http://localhost:4000, type:
 
 ```bash
-npm start
+npm run web:start # that gets you just the website. if you need companion, etc. you can use `npm start` instead
 ```
 
 This will watch the website, as well as Uppy, as the examples, and rebuild everything and refresh your browser as files change.

+ 2 - 2
.github/ISSUE_TEMPLATE/integration_help.md

@@ -5,10 +5,10 @@ about: Do you need assistance with building the Uppy client in your bundler, or
 labels: Not Accepted
 ---
 
-Transloadit is providing Uppy free of charge. If you want you can self-host all components and never pay us a dime. There's docs and tests and your Bugs and Feature Requests are always welcome on GitHub. 
+Transloadit is providing Uppy free of charge. If you want you can self-host all components and never pay us a dime. There's docs and tests and your Bug Reports and Feature Requests are always welcome on GitHub. 
 
 There is also a different category of support that we'd like to call Integration Help: making things work for you, that are already reported working for the larger community.
 
 As much as we'd like to provide detailed integration help to each non-paying user, Uppy has reached a point where this is no longer sustainable for our small crew. We end up investing our time in a million different projects, but without money flowing back, we can't ramp up our team along with the demand, spreading our team ever thinner, eventually grinding development to a halt.
 
-This is not where we want to be, so in order to offer enthusiasts, businesses, and enterprises assistance in a sustainable way, we're providing community based integration help for free at <https://community.transloadit.com/c/uppy>, and offer business paid support via <https://uppy.io/support>.
+This is not where we want to be, so in order to offer enthusiasts, businesses, and enterprises assistance in a sustainable way, we're providing community based integration help for free at <https://community.transloadit.com/c/uppy>, and offer paid support via <https://uppy.io/support>.

+ 65 - 5
CHANGELOG.md

@@ -19,11 +19,9 @@ last Friday of every new month.
 Ideas that will be planned and find their way into a release at one point.
 PRs are welcome! Please do open an issue to discuss first if it's a big feature, priorities may have changed after something was added here.
 
-- [ ] core: Decouple rendering from the Plugin base class?
 - [ ] core: Make sure Uppy works well in VR
 - [ ] test: Human should check http://www.webpagetest.org and https://developers.google.com/web/tools/lighthouse/, use it sometimes to test website and Uppy. Will show response/loading times and other issues
 - [ ] test: Human should test with real screen reader to identify accessibility problems
-- [ ] test: setup an HTML page with all sorts of crazy styles, resets & bootstrap to see what brakes Uppy (@arturi)
 - [ ] dependencies: es6-promise --> lie https://github.com/calvinmetcalf/lie ?
 - [ ] core: accessibility research: https://chrome.google.com/webstore/detail/accessibility-developer-t/fpkknkljclfencbdbgkenhalefipecmb, http://khan.github.io/tota11y/
 - [ ] core: consider adding presets, see https://github.com/cssinjs/jss-preset-default/blob/master/src/index.js (@arturi)
@@ -89,6 +87,11 @@ PRs are welcome! Please do open an issue to discuss first if it's a big feature,
 - [ ] dragdrop: allow customizing arrow icon https://github.com/transloadit/uppy/pull/374#issuecomment-334116208 (@arturi)
 - [ ] show thumbnails when connecting with Google Drive #1162 (@ifedapoolarewaju)
 
+## 1.1
+
+- [ ] dashboard: optional alert `onbeforeunload` while upload is in progress, safeguarding from accidentaly navigating away from a page with an ongoing upload
+- [ ] dashboard: Bring back "Drop Here" screen for dragged URLs without introducing flickering (tricky! see PR #1400)
+
 ## 1.0 Goals
 
 What we need to do to release Uppy 1.0
@@ -101,13 +104,12 @@ What we need to do to release Uppy 1.0
 - [ ] feature: basic React Native support (@arturi, @ifedapoolarewaju)
 - [ ] QA: add one integration test (or add to existing test) that uses more exotic (tus) options such as `useFastRemoteRetry` or `removeFingerprintOnSuccess` https://github.com/transloadit/uppy/issues/1327 (@arturi, @ifedapoolarewaju)
 - [ ] QA: manually test in multiple browsers and mobile devices again (SauceLabs can do Android/iOS too) (@nqst)
-- [ ] website: replace transloadit example with robodog example <-- add transloadit test key with restricted usage (no need to sign up yourself to try it)
+- [x] website: replace transloadit example with robodog example <-- add transloadit test key with restricted usage (no need to sign up yourself to try it)
 - [ ] website: big release blog post or series
 - [ ] website: design polish
 - [ ] companion: rename `serverUrl` and `serverPattern` to `companionUrl` and `companionAllowedHosts` (@ifedapoolarewaju)
 - [ ] transloadit: add error reporting, see https://github.com/transloadit/jquery-sdk/blob/891e99b08dd8142d8d8adc0553e6511967635ad7/js/lib/Modal.js#L122-L136 (@goto-bus-stop, @arturi)
-- [ ] transloadit: should adhere cancel event and abort assembly (@arturi, @goto-bus-stop)
-- [ ] dashboard: optional alert `onbeforeunload` while upload is in progress, safeguarding from accidentaly navigating away from a page with an ongoing upload
+- [ ] transloadit: should adhere cancel event and abort assembly (@goto-bus-stop)
 - [x] chore: remove the not-working npm scripts (@kvz, @arturi)
 - [x] build: (BREAKING) `npm run dev` no longer starts Companion by default, use `npm run dev:with-companion` for that (@arturi)
 - [x] core: uppy should not crash or be slow for many files. Specifically: be able to drop 5 files (or 7mb) without the upload button to take 2 seconds to appear
@@ -134,6 +136,64 @@ 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)
 
+## 0.30.4
+
+Released: 2019-04-04
+
+| Package | Version | Package | Version |
+|-|-|-|-|
+| @uppy/aws-s3-multipart | 0.30.4 | @uppy/progress-bar | 0.30.4 |
+| @uppy/aws-s3 | 0.30.4 | @uppy/provider-views | 0.30.4 |
+| @uppy/companion-client | 0.28.4 | @uppy/react | 0.30.4 |
+| @uppy/companion | 0.17.4 | @uppy/redux-dev-tools | 0.30.4 |
+| @uppy/core | 0.30.4 | @uppy/robodog | 0.30.4 |
+| @uppy/dashboard | 0.30.4 | @uppy/status-bar | 0.30.4 |
+| @uppy/drag-drop | 0.30.4 | @uppy/thumbnail-generator | 0.30.4 |
+| @uppy/dropbox | 0.30.4 | @uppy/transloadit | 0.30.4 |
+| @uppy/file-input | 0.30.4 | @uppy/tus | 0.30.4 |
+| @uppy/form | 0.30.4 | @uppy/url | 0.30.4 |
+| @uppy/golden-retriever | 0.30.4 | @uppy/utils | 0.30.4 |
+| @uppy/google-drive | 0.30.4 | @uppy/webcam | 0.30.4 |
+| @uppy/informer | 0.30.4 | @uppy/xhr-upload | 0.30.4 |
+| @uppy/instagram | 0.30.4 | uppy | 0.30.4 |
+
+- build: ⚠️ remove !important (postcss-safe-important) (#1344 / @arturi)
+- @uppy/core: un-hardcode concat in `youCanOnlyUploadFileTypes` locale: In some languages, it probably doesn't make much sense to put the list
+of allowed file types at the end. The list of mime types/extensions may not be desired at all, so now you can omit %{types} to not show it. (#1374 / @goto-bus-stop)
+- @uppy/core: ⚠️ YMPT™: Yet More Progress Tweaks — #1093 accidentally omitted file size reporting for GDrive/Dropbox uploads, this adds it back.
+Unsized files (like instagram photos) now are stored with size: null instead of 0 (#1376 / @goto-bus-stop)
+- @uppy/core: make allowedFileTypes extensions case insensitive (#1341 / @goto-bus-stop)
+- @uppy/companion: ⚠️ fix instagram hanging uploads (#1274 / @ifedapoolarewaju)
+- @uppy/companion-client: remove the use of window.location for React Native support (#1393 / @ifedapoolarewaju)
+- typescript: ⚠️ fix uppy package use with allowSyntheticImports: false (#1396 / @goto-bus-stop)
+- @uppy/core: ⚠️ remove console.dir, since it’s probably unnessesary now and not supported in React Native (@arturi / a4f94a8d6b475657837f7c51dfb0670cc77fc3de)
+- @uppy/xhr-upload: allow customized option to set upload status (#1360 / @Mactaivsh)
+- @uppy/dashboard: fix icons jiggling on hover in safari (#1410 / @lakesare)
+- @uppy/dashboard: the list items are now even out (#1398 / @lakesare)
+- @uppy/dashboard: remove jumpiness (mobile --> desktop) when uppy loads (#1383 / @lakesare)
+- @uppy/dashboard: Protect some more styles from bleeding (#1362 / @arturi)
+- build: Refactor npm scripts, clean up unused ones (#1392 / @kvz, @arturi)
+- build: Speed up website deploys (73f89f08d9dde9e096285a952528976a69d923cf / @kvz)
+- @uppy/xhr-upload: ⚠️ load CompanionClient appropriately (c1abfea33d0c3e80809814c1048b156028c8fcf9 / @ifedapoolarewaju)
+- @uppy/companion: ⚠️ send null when download is complete (@ifedapoolarewaju / de04c7978c6713995cbf1717f6ca7ffd292cdeb1)
+- @uppy/companion: overwrite bytestotal for only tus uploads (d9ec8d28f4c97da4c0dcb46fbf5804a0ee484eeb / @ifedapoolarewaju)
+- @uppy/companion: install git so we can fetch tus-js-client fork (#1404 / @goto-bus-stop)
+- @uppy/companion-client: ⚠️ return 401 for invalid access token (#1298 / @ifedapoolarewaju)
+- @uppy/companion-client: ⚠️ add asyn wrapper around token storage (#1369 / @ifedapoolarewaju)
+- @uppy/companion: Updated the callback URIs to reflect their correct location (#1366 / @HughbertD)
+- @uppy/companion: do not use Uploader instance if options validation failed #1354
+- @uppy/status-bar: fix StatusBar error tooltip positioning (f83b4b06d958a1f7e78885a61b645c3371feb1ae / @arturi)
+- @uppy/google-drive Show thumbnails instead of a generic icon in Google Drive (#1363 / @arturi)
+- @uppy/dropbox: HTTP-header-safe JSON for dropbox (#1371 / @yonahforst)
+- @uppy/informer: made the tooltip not overflow the uppy container (#1382 / @lakesare)
+- @uppy/aws-s3-multipart: for remote aws-s3 uploads (#1350 / @ifedapoolarewaju)
+- examples: use template + demo key for transloadit-textarea example (#1375 / @goto-bus-stop)
+- website: add markdown snippets example (#1379 / @arturi)
+- website: provide simple framework for doing blog post series (#1373 / @kvz)
+- locales: Remove outdated locales for now (#1355 / @arturi)
+- @uppy/thumbnail-generator: do not export tainted canvas, fixes #1321 (#1343 / @goto-bus-stop)
+- @uppy/companion: replace text only when text is valid (985fd62ed6017ea3786eefd2c222caeb26d7998e / @ifedapoolarewaju)
+
 ## 0.30.3
 
 Released: 2019-03-08

+ 44 - 33
README.md

@@ -65,7 +65,7 @@ $ npm install @uppy/core @uppy/dashboard @uppy/tus
 
 We recommend installing from npm and then using a module bundler such as [Webpack](https://webpack.js.org/), [Browserify](http://browserify.org/) or [Rollup.js](http://rollupjs.org/).
 
-Add CSS [uppy.min.css](https://transloadit.edgly.net/releases/uppy/v0.30.3/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.30.4/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,10 +73,10 @@ Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly
 
 ```html
 <!-- 1. Add CSS to `<head>` -->
-<link href="https://transloadit.edgly.net/releases/uppy/v0.30.3/uppy.min.css" rel="stylesheet">
+<link href="https://transloadit.edgly.net/releases/uppy/v0.30.4/uppy.min.css" rel="stylesheet">
 
 <!-- 2. Add JS before the closing `</body>` -->
-<script src="https://transloadit.edgly.net/releases/uppy/v0.30.3/uppy.min.js"></script>
+<script src="https://transloadit.edgly.net/releases/uppy/v0.30.4/uppy.min.js"></script>
 
 <!-- 3. Initialize -->
 <div class="UppyDragDrop"></div>
@@ -163,7 +163,7 @@ If you're using Uppy via a script tag, you can load the polyfills from [JSDelivr
 ```html
 <script src="https://cdn.jsdelivr.net/npm/es6-promise@4.2.5/dist/es6-promise.auto.min.js"></script>
 <script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@3.0.0/dist/fetch.umd.min.js"></script>
-<script src="https://transloadit.edgly.net/releases/uppy/v0.30.3/uppy.min.js"></script>
+<script src="https://transloadit.edgly.net/releases/uppy/v0.30.4/uppy.min.js"></script>
 ```
 
 ## FAQ
@@ -220,37 +220,41 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu
 <!--contributors-->
 ## Contributors
 
-[<img alt="arturi" src="https://avatars2.githubusercontent.com/u/1199054?v=4&s=117" width="117">](https://github.com/arturi) |[<img alt="goto-bus-stop" src="https://avatars1.githubusercontent.com/u/1006268?v=4&s=117" width="117">](https://github.com/goto-bus-stop) |[<img alt="kvz" src="https://avatars2.githubusercontent.com/u/26752?v=4&s=117" width="117">](https://github.com/kvz) |[<img alt="hedgerh" src="https://avatars2.githubusercontent.com/u/2524280?v=4&s=117" width="117">](https://github.com/hedgerh) |[<img alt="ifedapoolarewaju" src="https://avatars1.githubusercontent.com/u/8383781?v=4&s=117" width="117">](https://github.com/ifedapoolarewaju) |[<img alt="AJvanLoon" src="https://avatars0.githubusercontent.com/u/15716628?v=4&s=117" width="117">](https://github.com/AJvanLoon) |
+[<img alt="arturi" src="https://avatars2.githubusercontent.com/u/1199054?v=4&s=117" width="117">](https://github.com/arturi) |[<img alt="goto-bus-stop" src="https://avatars1.githubusercontent.com/u/1006268?v=4&s=117" width="117">](https://github.com/goto-bus-stop) |[<img alt="kvz" src="https://avatars2.githubusercontent.com/u/26752?v=4&s=117" width="117">](https://github.com/kvz) |[<img alt="ifedapoolarewaju" src="https://avatars1.githubusercontent.com/u/8383781?v=4&s=117" width="117">](https://github.com/ifedapoolarewaju) |[<img alt="hedgerh" src="https://avatars2.githubusercontent.com/u/2524280?v=4&s=117" width="117">](https://github.com/hedgerh) |[<img alt="AJvanLoon" src="https://avatars0.githubusercontent.com/u/15716628?v=4&s=117" width="117">](https://github.com/AJvanLoon) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[arturi](https://github.com/arturi) |[goto-bus-stop](https://github.com/goto-bus-stop) |[kvz](https://github.com/kvz) |[hedgerh](https://github.com/hedgerh) |[ifedapoolarewaju](https://github.com/ifedapoolarewaju) |[AJvanLoon](https://github.com/AJvanLoon) |
+[arturi](https://github.com/arturi) |[goto-bus-stop](https://github.com/goto-bus-stop) |[kvz](https://github.com/kvz) |[ifedapoolarewaju](https://github.com/ifedapoolarewaju) |[hedgerh](https://github.com/hedgerh) |[AJvanLoon](https://github.com/AJvanLoon) |
 
-[<img alt="kiloreux" src="https://avatars0.githubusercontent.com/u/6282557?v=4&s=117" width="117">](https://github.com/kiloreux) |[<img alt="sadovnychyi" src="https://avatars3.githubusercontent.com/u/193864?v=4&s=117" width="117">](https://github.com/sadovnychyi) |[<img alt="richardwillars" src="https://avatars3.githubusercontent.com/u/291004?v=4&s=117" width="117">](https://github.com/richardwillars) |[<img alt="zcallan" src="https://avatars0.githubusercontent.com/u/13760738?v=4&s=117" width="117">](https://github.com/zcallan) |[<img alt="wilkoklak" src="https://avatars1.githubusercontent.com/u/17553085?v=4&s=117" width="117">](https://github.com/wilkoklak) |[<img alt="oliverpool" src="https://avatars0.githubusercontent.com/u/3864879?v=4&s=117" width="117">](https://github.com/oliverpool) |
+[<img alt="kiloreux" src="https://avatars0.githubusercontent.com/u/6282557?v=4&s=117" width="117">](https://github.com/kiloreux) |[<img alt="sadovnychyi" src="https://avatars3.githubusercontent.com/u/193864?v=4&s=117" width="117">](https://github.com/sadovnychyi) |[<img alt="richardwillars" src="https://avatars3.githubusercontent.com/u/291004?v=4&s=117" width="117">](https://github.com/richardwillars) |[<img alt="zcallan" src="https://avatars0.githubusercontent.com/u/13760738?v=4&s=117" width="117">](https://github.com/zcallan) |[<img alt="nqst" src="https://avatars0.githubusercontent.com/u/375537?v=4&s=117" width="117">](https://github.com/nqst) |[<img alt="wilkoklak" src="https://avatars1.githubusercontent.com/u/17553085?v=4&s=117" width="117">](https://github.com/wilkoklak) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[kiloreux](https://github.com/kiloreux) |[sadovnychyi](https://github.com/sadovnychyi) |[richardwillars](https://github.com/richardwillars) |[zcallan](https://github.com/zcallan) |[wilkoklak](https://github.com/wilkoklak) |[oliverpool](https://github.com/oliverpool) |
+[kiloreux](https://github.com/kiloreux) |[sadovnychyi](https://github.com/sadovnychyi) |[richardwillars](https://github.com/richardwillars) |[zcallan](https://github.com/zcallan) |[nqst](https://github.com/nqst) |[wilkoklak](https://github.com/wilkoklak) |
 
-[<img alt="nqst" src="https://avatars0.githubusercontent.com/u/375537?v=4&s=117" width="117">](https://github.com/nqst) |[<img alt="janko" src="https://avatars2.githubusercontent.com/u/795488?v=4&s=117" width="117">](https://github.com/janko) |[<img alt="mattes3" src="https://avatars2.githubusercontent.com/u/2496674?v=4&s=117" width="117">](https://github.com/mattes3) |[<img alt="DJWassink" src="https://avatars3.githubusercontent.com/u/1822404?v=4&s=117" width="117">](https://github.com/DJWassink) |[<img alt="taoqf" src="https://avatars3.githubusercontent.com/u/15901911?v=4&s=117" width="117">](https://github.com/taoqf) |[<img alt="gavboulton" src="https://avatars0.githubusercontent.com/u/3900826?v=4&s=117" width="117">](https://github.com/gavboulton) |
+[<img alt="oliverpool" src="https://avatars0.githubusercontent.com/u/3864879?v=4&s=117" width="117">](https://github.com/oliverpool) |[<img alt="samuelayo" src="https://avatars1.githubusercontent.com/u/14964486?v=4&s=117" width="117">](https://github.com/samuelayo) |[<img alt="janko" src="https://avatars2.githubusercontent.com/u/795488?v=4&s=117" width="117">](https://github.com/janko) |[<img alt="mattes3" src="https://avatars2.githubusercontent.com/u/2496674?v=4&s=117" width="117">](https://github.com/mattes3) |[<img alt="DJWassink" src="https://avatars3.githubusercontent.com/u/1822404?v=4&s=117" width="117">](https://github.com/DJWassink) |[<img alt="taoqf" src="https://avatars3.githubusercontent.com/u/15901911?v=4&s=117" width="117">](https://github.com/taoqf) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[nqst](https://github.com/nqst) |[janko](https://github.com/janko) |[mattes3](https://github.com/mattes3) |[DJWassink](https://github.com/DJWassink) |[taoqf](https://github.com/taoqf) |[gavboulton](https://github.com/gavboulton) |
+[oliverpool](https://github.com/oliverpool) |[samuelayo](https://github.com/samuelayo) |[janko](https://github.com/janko) |[mattes3](https://github.com/mattes3) |[DJWassink](https://github.com/DJWassink) |[taoqf](https://github.com/taoqf) |
 
-[<img alt="bertho-zero" src="https://avatars0.githubusercontent.com/u/8525267?v=4&s=117" width="117">](https://github.com/bertho-zero) |[<img alt="tranvansang" src="https://avatars1.githubusercontent.com/u/13043196?v=4&s=117" width="117">](https://github.com/tranvansang) |[<img alt="pauln" src="https://avatars3.githubusercontent.com/u/574359?v=4&s=117" width="117">](https://github.com/pauln) |[<img alt="toadkicker" src="https://avatars1.githubusercontent.com/u/523330?v=4&s=117" width="117">](https://github.com/toadkicker) |[<img alt="mrbatista" src="https://avatars0.githubusercontent.com/u/6544817?v=4&s=117" width="117">](https://github.com/mrbatista) |[<img alt="sunil-shrestha" src="https://avatars3.githubusercontent.com/u/2129058?v=4&s=117" width="117">](https://github.com/sunil-shrestha) |
+[<img alt="lakesare" src="https://avatars1.githubusercontent.com/u/7578559?v=4&s=117" width="117">](https://github.com/lakesare) |[<img alt="gavboulton" src="https://avatars0.githubusercontent.com/u/3900826?v=4&s=117" width="117">](https://github.com/gavboulton) |[<img alt="bertho-zero" src="https://avatars0.githubusercontent.com/u/8525267?v=4&s=117" width="117">](https://github.com/bertho-zero) |[<img alt="tranvansang" src="https://avatars1.githubusercontent.com/u/13043196?v=4&s=117" width="117">](https://github.com/tranvansang) |[<img alt="pauln" src="https://avatars3.githubusercontent.com/u/574359?v=4&s=117" width="117">](https://github.com/pauln) |[<img alt="toadkicker" src="https://avatars1.githubusercontent.com/u/523330?v=4&s=117" width="117">](https://github.com/toadkicker) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[bertho-zero](https://github.com/bertho-zero) |[tranvansang](https://github.com/tranvansang) |[pauln](https://github.com/pauln) |[toadkicker](https://github.com/toadkicker) |[mrbatista](https://github.com/mrbatista) |[sunil-shrestha](https://github.com/sunil-shrestha) |
+[lakesare](https://github.com/lakesare) |[gavboulton](https://github.com/gavboulton) |[bertho-zero](https://github.com/bertho-zero) |[tranvansang](https://github.com/tranvansang) |[pauln](https://github.com/pauln) |[toadkicker](https://github.com/toadkicker) |
 
-[<img alt="ap--" src="https://avatars1.githubusercontent.com/u/1463443?v=4&s=117" width="117">](https://github.com/ap--) |[<img alt="tim-kos" src="https://avatars1.githubusercontent.com/u/15005?v=4&s=117" width="117">](https://github.com/tim-kos) |[<img alt="ogtfaber" src="https://avatars2.githubusercontent.com/u/320955?v=4&s=117" width="117">](https://github.com/ogtfaber) |[<img alt="btrice" src="https://avatars2.githubusercontent.com/u/4358225?v=4&s=117" width="117">](https://github.com/btrice) |[<img alt="craigjennings11" src="https://avatars2.githubusercontent.com/u/1683368?v=4&s=117" width="117">](https://github.com/craigjennings11) |[<img alt="jedwood" src="https://avatars0.githubusercontent.com/u/369060?v=4&s=117" width="117">](https://github.com/jedwood) |
+[<img alt="ogtfaber" src="https://avatars2.githubusercontent.com/u/320955?v=4&s=117" width="117">](https://github.com/ogtfaber) |[<img alt="mrbatista" src="https://avatars0.githubusercontent.com/u/6544817?v=4&s=117" width="117">](https://github.com/mrbatista) |[<img alt="mcallistertyler95" src="https://avatars1.githubusercontent.com/u/14939210?v=4&s=117" width="117">](https://github.com/mcallistertyler95) |[<img alt="yonahforst" src="https://avatars3.githubusercontent.com/u/1440796?v=4&s=117" width="117">](https://github.com/yonahforst) |[<img alt="ap--" src="https://avatars1.githubusercontent.com/u/1463443?v=4&s=117" width="117">](https://github.com/ap--) |[<img alt="tim-kos" src="https://avatars1.githubusercontent.com/u/15005?v=4&s=117" width="117">](https://github.com/tim-kos) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[ap--](https://github.com/ap--) |[tim-kos](https://github.com/tim-kos) |[ogtfaber](https://github.com/ogtfaber) |[btrice](https://github.com/btrice) |[craigjennings11](https://github.com/craigjennings11) |[jedwood](https://github.com/jedwood) |
+[ogtfaber](https://github.com/ogtfaber) |[mrbatista](https://github.com/mrbatista) |[mcallistertyler95](https://github.com/mcallistertyler95) |[yonahforst](https://github.com/yonahforst) |[ap--](https://github.com/ap--) |[tim-kos](https://github.com/tim-kos) |
 
-[<img alt="pekala" src="https://avatars1.githubusercontent.com/u/4643658?v=4&s=117" width="117">](https://github.com/pekala) |[<img alt="manuelkiessling" src="https://avatars2.githubusercontent.com/u/206592?v=4&s=117" width="117">](https://github.com/manuelkiessling) |[<img alt="Martin005" src="https://avatars0.githubusercontent.com/u/10096404?v=4&s=117" width="117">](https://github.com/Martin005) |[<img alt="martiuslim" src="https://avatars2.githubusercontent.com/u/17944339?v=4&s=117" width="117">](https://github.com/martiuslim) |[<img alt="Burkes" src="https://avatars2.githubusercontent.com/u/9220052?v=4&s=117" width="117">](https://github.com/Burkes) |[<img alt="richartkeil" src="https://avatars0.githubusercontent.com/u/8680858?v=4&s=117" width="117">](https://github.com/richartkeil) |
+[<img alt="sunil-shrestha" src="https://avatars3.githubusercontent.com/u/2129058?v=4&s=117" width="117">](https://github.com/sunil-shrestha) |[<img alt="btrice" src="https://avatars2.githubusercontent.com/u/4358225?v=4&s=117" width="117">](https://github.com/btrice) |[<img alt="craigjennings11" src="https://avatars2.githubusercontent.com/u/1683368?v=4&s=117" width="117">](https://github.com/craigjennings11) |[<img alt="jedwood" src="https://avatars0.githubusercontent.com/u/369060?v=4&s=117" width="117">](https://github.com/jedwood) |[<img alt="pekala" src="https://avatars1.githubusercontent.com/u/4643658?v=4&s=117" width="117">](https://github.com/pekala) |[<img alt="Mactaivsh" src="https://avatars0.githubusercontent.com/u/12948083?v=4&s=117" width="117">](https://github.com/Mactaivsh) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[pekala](https://github.com/pekala) |[manuelkiessling](https://github.com/manuelkiessling) |[Martin005](https://github.com/Martin005) |[martiuslim](https://github.com/martiuslim) |[Burkes](https://github.com/Burkes) |[richartkeil](https://github.com/richartkeil) |
+[sunil-shrestha](https://github.com/sunil-shrestha) |[btrice](https://github.com/btrice) |[craigjennings11](https://github.com/craigjennings11) |[jedwood](https://github.com/jedwood) |[pekala](https://github.com/pekala) |[Mactaivsh](https://github.com/Mactaivsh) |
 
-[<img alt="richmeij" src="https://avatars0.githubusercontent.com/u/9741858?v=4&s=117" width="117">](https://github.com/richmeij) |[<img alt="rosenfeld" src="https://avatars1.githubusercontent.com/u/32246?v=4&s=117" width="117">](https://github.com/rosenfeld) |[<img alt="ThomasG77" src="https://avatars2.githubusercontent.com/u/642120?v=4&s=117" width="117">](https://github.com/ThomasG77) |[<img alt="zhuangya" src="https://avatars2.githubusercontent.com/u/499038?v=4&s=117" width="117">](https://github.com/zhuangya) |[<img alt="fortrieb" src="https://avatars0.githubusercontent.com/u/4126707?v=4&s=117" width="117">](https://github.com/fortrieb) |[<img alt="muhammadInam" src="https://avatars1.githubusercontent.com/u/7801708?v=4&s=117" width="117">](https://github.com/muhammadInam) |
+[<img alt="manuelkiessling" src="https://avatars2.githubusercontent.com/u/206592?v=4&s=117" width="117">](https://github.com/manuelkiessling) |[<img alt="Martin005" src="https://avatars0.githubusercontent.com/u/10096404?v=4&s=117" width="117">](https://github.com/Martin005) |[<img alt="martiuslim" src="https://avatars2.githubusercontent.com/u/17944339?v=4&s=117" width="117">](https://github.com/martiuslim) |[<img alt="Burkes" src="https://avatars2.githubusercontent.com/u/9220052?v=4&s=117" width="117">](https://github.com/Burkes) |[<img alt="richartkeil" src="https://avatars0.githubusercontent.com/u/8680858?v=4&s=117" width="117">](https://github.com/richartkeil) |[<img alt="richmeij" src="https://avatars0.githubusercontent.com/u/9741858?v=4&s=117" width="117">](https://github.com/richmeij) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[richmeij](https://github.com/richmeij) |[rosenfeld](https://github.com/rosenfeld) |[ThomasG77](https://github.com/ThomasG77) |[zhuangya](https://github.com/zhuangya) |[fortrieb](https://github.com/fortrieb) |[muhammadInam](https://github.com/muhammadInam) |
+[manuelkiessling](https://github.com/manuelkiessling) |[Martin005](https://github.com/Martin005) |[martiuslim](https://github.com/martiuslim) |[Burkes](https://github.com/Burkes) |[richartkeil](https://github.com/richartkeil) |[richmeij](https://github.com/richmeij) |
 
-[<img alt="msand" src="https://avatars2.githubusercontent.com/u/1131362?v=4&s=117" width="117">](https://github.com/msand) |[<img alt="ajschmidt8" src="https://avatars0.githubusercontent.com/u/7400326?v=4&s=117" width="117">](https://github.com/ajschmidt8) |[<img alt="asmt3" src="https://avatars1.githubusercontent.com/u/1777709?v=4&s=117" width="117">](https://github.com/asmt3) |[<img alt="amitport" src="https://avatars1.githubusercontent.com/u/1131991?v=4&s=117" width="117">](https://github.com/amitport) |[<img alt="tuoxiansp" src="https://avatars1.githubusercontent.com/u/3960056?v=4&s=117" width="117">](https://github.com/tuoxiansp) |[<img alt="radarhere" src="https://avatars2.githubusercontent.com/u/3112309?v=4&s=117" width="117">](https://github.com/radarhere) |
+[<img alt="rosenfeld" src="https://avatars1.githubusercontent.com/u/32246?v=4&s=117" width="117">](https://github.com/rosenfeld) |[<img alt="ThomasG77" src="https://avatars2.githubusercontent.com/u/642120?v=4&s=117" width="117">](https://github.com/ThomasG77) |[<img alt="zhuangya" src="https://avatars2.githubusercontent.com/u/499038?v=4&s=117" width="117">](https://github.com/zhuangya) |[<img alt="fortrieb" src="https://avatars0.githubusercontent.com/u/4126707?v=4&s=117" width="117">](https://github.com/fortrieb) |[<img alt="muhammadInam" src="https://avatars1.githubusercontent.com/u/7801708?v=4&s=117" width="117">](https://github.com/muhammadInam) |[<img alt="msand" src="https://avatars2.githubusercontent.com/u/1131362?v=4&s=117" width="117">](https://github.com/msand) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[msand](https://github.com/msand) |[ajschmidt8](https://github.com/ajschmidt8) |[asmt3](https://github.com/asmt3) |[amitport](https://github.com/amitport) |[tuoxiansp](https://github.com/tuoxiansp) |[radarhere](https://github.com/radarhere) |
+[rosenfeld](https://github.com/rosenfeld) |[ThomasG77](https://github.com/ThomasG77) |[zhuangya](https://github.com/zhuangya) |[fortrieb](https://github.com/fortrieb) |[muhammadInam](https://github.com/muhammadInam) |[msand](https://github.com/msand) |
+
+[<img alt="ajschmidt8" src="https://avatars0.githubusercontent.com/u/7400326?v=4&s=117" width="117">](https://github.com/ajschmidt8) |[<img alt="asmt3" src="https://avatars1.githubusercontent.com/u/1777709?v=4&s=117" width="117">](https://github.com/asmt3) |[<img alt="tuoxiansp" src="https://avatars1.githubusercontent.com/u/3960056?v=4&s=117" width="117">](https://github.com/tuoxiansp) |[<img alt="functino" src="https://avatars0.githubusercontent.com/u/415498?v=4&s=117" width="117">](https://github.com/functino) |[<img alt="radarhere" src="https://avatars2.githubusercontent.com/u/3112309?v=4&s=117" width="117">](https://github.com/radarhere) |[<img alt="arthurdenner" src="https://avatars0.githubusercontent.com/u/13774309?v=4&s=117" width="117">](https://github.com/arthurdenner) |
+:---: |:---: |:---: |:---: |:---: |:---: |
+[ajschmidt8](https://github.com/ajschmidt8) |[asmt3](https://github.com/asmt3) |[tuoxiansp](https://github.com/tuoxiansp) |[functino](https://github.com/functino) |[radarhere](https://github.com/radarhere) |[arthurdenner](https://github.com/arthurdenner) |
 
 [<img alt="bochkarev-artem" src="https://avatars2.githubusercontent.com/u/11025874?v=4&s=117" width="117">](https://github.com/bochkarev-artem) |[<img alt="azeemba" src="https://avatars0.githubusercontent.com/u/2160795?v=4&s=117" width="117">](https://github.com/azeemba) |[<img alt="bducharme" src="https://avatars2.githubusercontent.com/u/4173569?v=4&s=117" width="117">](https://github.com/bducharme) |[<img alt="chao" src="https://avatars2.githubusercontent.com/u/55872?v=4&s=117" width="117">](https://github.com/chao) |[<img alt="csprance" src="https://avatars0.githubusercontent.com/u/7902617?v=4&s=117" width="117">](https://github.com/csprance) |[<img alt="cbush06" src="https://avatars0.githubusercontent.com/u/15720146?v=4&s=117" width="117">](https://github.com/cbush06) |
 :---: |:---: |:---: |:---: |:---: |:---: |
@@ -260,31 +264,38 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu
 :---: |:---: |:---: |:---: |:---: |:---: |
 [danmichaelo](https://github.com/danmichaelo) |[mrboomer](https://github.com/mrboomer) |[davilima6](https://github.com/davilima6) |[yoldar](https://github.com/yoldar) |[lowsprofile](https://github.com/lowsprofile) |[FWirtz](https://github.com/FWirtz) |
 
-[<img alt="geoffappleford" src="https://avatars2.githubusercontent.com/u/731678?v=4&s=117" width="117">](https://github.com/geoffappleford) |[<img alt="gjungb" src="https://avatars0.githubusercontent.com/u/3391068?v=4&s=117" width="117">](https://github.com/gjungb) |[<img alt="JacobMGEvans" src="https://avatars1.githubusercontent.com/u/27247160?v=4&s=117" width="117">](https://github.com/JacobMGEvans) |[<img alt="jcjmcclean" src="https://avatars3.githubusercontent.com/u/1822574?v=4&s=117" width="117">](https://github.com/jcjmcclean) |[<img alt="vith" src="https://avatars1.githubusercontent.com/u/3265539?v=4&s=117" width="117">](https://github.com/vith) |[<img alt="jessica-coursera" src="https://avatars1.githubusercontent.com/u/35155465?v=4&s=117" width="117">](https://github.com/jessica-coursera) |
+[<img alt="geoffappleford" src="https://avatars2.githubusercontent.com/u/731678?v=4&s=117" width="117">](https://github.com/geoffappleford) |[<img alt="gjungb" src="https://avatars0.githubusercontent.com/u/3391068?v=4&s=117" width="117">](https://github.com/gjungb) |[<img alt="HughbertD" src="https://avatars0.githubusercontent.com/u/1580021?v=4&s=117" width="117">](https://github.com/HughbertD) |[<img alt="JacobMGEvans" src="https://avatars1.githubusercontent.com/u/27247160?v=4&s=117" width="117">](https://github.com/JacobMGEvans) |[<img alt="jcjmcclean" src="https://avatars3.githubusercontent.com/u/1822574?v=4&s=117" width="117">](https://github.com/jcjmcclean) |[<img alt="vith" src="https://avatars1.githubusercontent.com/u/3265539?v=4&s=117" width="117">](https://github.com/vith) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[geoffappleford](https://github.com/geoffappleford) |[gjungb](https://github.com/gjungb) |[JacobMGEvans](https://github.com/JacobMGEvans) |[jcjmcclean](https://github.com/jcjmcclean) |[vith](https://github.com/vith) |[jessica-coursera](https://github.com/jessica-coursera) |
+[geoffappleford](https://github.com/geoffappleford) |[gjungb](https://github.com/gjungb) |[HughbertD](https://github.com/HughbertD) |[JacobMGEvans](https://github.com/JacobMGEvans) |[jcjmcclean](https://github.com/jcjmcclean) |[vith](https://github.com/vith) |
 
-[<img alt="Dargmuesli" src="https://avatars2.githubusercontent.com/u/4778485?v=4&s=117" width="117">](https://github.com/Dargmuesli) |[<img alt="jderrough" src="https://avatars3.githubusercontent.com/u/1108358?v=4&s=117" width="117">](https://github.com/jderrough) |[<img alt="firesharkstudios" src="https://avatars1.githubusercontent.com/u/17069637?v=4&s=117" width="117">](https://github.com/firesharkstudios) |[<img alt="kyleparisi" src="https://avatars0.githubusercontent.com/u/1286753?v=4&s=117" width="117">](https://github.com/kyleparisi) |[<img alt="dviry" src="https://avatars3.githubusercontent.com/u/1230260?v=4&s=117" width="117">](https://github.com/dviry) |[<img alt="leods92" src="https://avatars0.githubusercontent.com/u/879395?v=4&s=117" width="117">](https://github.com/leods92) |
+[<img alt="jessica-coursera" src="https://avatars1.githubusercontent.com/u/35155465?v=4&s=117" width="117">](https://github.com/jessica-coursera) |[<img alt="dargmuesli" src="https://avatars2.githubusercontent.com/u/4778485?v=4&s=117" width="117">](https://github.com/dargmuesli) |[<img alt="jderrough" src="https://avatars3.githubusercontent.com/u/1108358?v=4&s=117" width="117">](https://github.com/jderrough) |[<img alt="firesharkstudios" src="https://avatars1.githubusercontent.com/u/17069637?v=4&s=117" width="117">](https://github.com/firesharkstudios) |[<img alt="kyleparisi" src="https://avatars0.githubusercontent.com/u/1286753?v=4&s=117" width="117">](https://github.com/kyleparisi) |[<img alt="dviry" src="https://avatars3.githubusercontent.com/u/1230260?v=4&s=117" width="117">](https://github.com/dviry) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[Dargmuesli](https://github.com/Dargmuesli) |[jderrough](https://github.com/jderrough) |[firesharkstudios](https://github.com/firesharkstudios) |[kyleparisi](https://github.com/kyleparisi) |[dviry](https://github.com/dviry) |[leods92](https://github.com/leods92) |
+[jessica-coursera](https://github.com/jessica-coursera) |[dargmuesli](https://github.com/dargmuesli) |[jderrough](https://github.com/jderrough) |[firesharkstudios](https://github.com/firesharkstudios) |[kyleparisi](https://github.com/kyleparisi) |[dviry](https://github.com/dviry) |
 
-[<img alt="lucaperret" src="https://avatars1.githubusercontent.com/u/1887122?v=4&s=117" width="117">](https://github.com/lucaperret) |[<img alt="mperrando" src="https://avatars2.githubusercontent.com/u/525572?v=4&s=117" width="117">](https://github.com/mperrando) |[<img alt="mnafees" src="https://avatars1.githubusercontent.com/u/1763885?v=4&s=117" width="117">](https://github.com/mnafees) |[<img alt="phillipalexander" src="https://avatars0.githubusercontent.com/u/1577682?v=4&s=117" width="117">](https://github.com/phillipalexander) |[<img alt="luarmr" src="https://avatars3.githubusercontent.com/u/817416?v=4&s=117" width="117">](https://github.com/luarmr) |[<img alt="phobos101" src="https://avatars2.githubusercontent.com/u/7114944?v=4&s=117" width="117">](https://github.com/phobos101) |
+[<img alt="leods92" src="https://avatars0.githubusercontent.com/u/879395?v=4&s=117" width="117">](https://github.com/leods92) |[<img alt="lucaperret" src="https://avatars1.githubusercontent.com/u/1887122?v=4&s=117" width="117">](https://github.com/lucaperret) |[<img alt="mperrando" src="https://avatars2.githubusercontent.com/u/525572?v=4&s=117" width="117">](https://github.com/mperrando) |[<img alt="mnafees" src="https://avatars1.githubusercontent.com/u/1763885?v=4&s=117" width="117">](https://github.com/mnafees) |[<img alt="phillipalexander" src="https://avatars0.githubusercontent.com/u/1577682?v=4&s=117" width="117">](https://github.com/phillipalexander) |[<img alt="luarmr" src="https://avatars3.githubusercontent.com/u/817416?v=4&s=117" width="117">](https://github.com/luarmr) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[lucaperret](https://github.com/lucaperret) |[mperrando](https://github.com/mperrando) |[mnafees](https://github.com/mnafees) |[phillipalexander](https://github.com/phillipalexander) |[luarmr](https://github.com/luarmr) |[phobos101](https://github.com/phobos101) |
+[leods92](https://github.com/leods92) |[lucaperret](https://github.com/lucaperret) |[mperrando](https://github.com/mperrando) |[mnafees](https://github.com/mnafees) |[phillipalexander](https://github.com/phillipalexander) |[luarmr](https://github.com/luarmr) |
 
-[<img alt="fortunto2" src="https://avatars1.githubusercontent.com/u/1236751?v=4&s=117" width="117">](https://github.com/fortunto2) |[<img alt="sergei-zelinsky" src="https://avatars2.githubusercontent.com/u/19428086?v=4&s=117" width="117">](https://github.com/sergei-zelinsky) |[<img alt="tomsaleeba" src="https://avatars0.githubusercontent.com/u/1773838?v=4&s=117" width="117">](https://github.com/tomsaleeba) |[<img alt="vially" src="https://avatars1.githubusercontent.com/u/433598?v=4&s=117" width="117">](https://github.com/vially) |[<img alt="eltercero" src="https://avatars0.githubusercontent.com/u/545235?v=4&s=117" width="117">](https://github.com/eltercero) |[<img alt="xhocquet" src="https://avatars2.githubusercontent.com/u/8116516?v=4&s=117" width="117">](https://github.com/xhocquet) |
+[<img alt="phobos101" src="https://avatars2.githubusercontent.com/u/7114944?v=4&s=117" width="117">](https://github.com/phobos101) |[<img alt="fortunto2" src="https://avatars1.githubusercontent.com/u/1236751?v=4&s=117" width="117">](https://github.com/fortunto2) |[<img alt="sergei-zelinsky" src="https://avatars2.githubusercontent.com/u/19428086?v=4&s=117" width="117">](https://github.com/sergei-zelinsky) |[<img alt="tomsaleeba" src="https://avatars0.githubusercontent.com/u/1773838?v=4&s=117" width="117">](https://github.com/tomsaleeba) |[<img alt="vially" src="https://avatars1.githubusercontent.com/u/433598?v=4&s=117" width="117">](https://github.com/vially) |[<img alt="eltercero" src="https://avatars0.githubusercontent.com/u/545235?v=4&s=117" width="117">](https://github.com/eltercero) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[fortunto2](https://github.com/fortunto2) |[sergei-zelinsky](https://github.com/sergei-zelinsky) |[tomsaleeba](https://github.com/tomsaleeba) |[vially](https://github.com/vially) |[eltercero](https://github.com/eltercero) |[xhocquet](https://github.com/xhocquet) |
+[phobos101](https://github.com/phobos101) |[fortunto2](https://github.com/fortunto2) |[sergei-zelinsky](https://github.com/sergei-zelinsky) |[tomsaleeba](https://github.com/tomsaleeba) |[vially](https://github.com/vially) |[eltercero](https://github.com/eltercero) |
 
-[<img alt="avalla" src="https://avatars1.githubusercontent.com/u/986614?v=4&s=117" width="117">](https://github.com/avalla) |[<img alt="c0b41" src="https://avatars1.githubusercontent.com/u/2834954?v=4&s=117" width="117">](https://github.com/c0b41) |[<img alt="craigcbrunner" src="https://avatars3.githubusercontent.com/u/2780521?v=4&s=117" width="117">](https://github.com/craigcbrunner) |[<img alt="franckl" src="https://avatars0.githubusercontent.com/u/3875803?v=4&s=117" width="117">](https://github.com/franckl) |[<img alt="ninesalt" src="https://avatars2.githubusercontent.com/u/7952255?v=4&s=117" width="117">](https://github.com/ninesalt) |[<img alt="luntta" src="https://avatars0.githubusercontent.com/u/14221637?v=4&s=117" width="117">](https://github.com/luntta) |
+[<img alt="xhocquet" src="https://avatars2.githubusercontent.com/u/8116516?v=4&s=117" width="117">](https://github.com/xhocquet) |[<img alt="avalla" src="https://avatars1.githubusercontent.com/u/986614?v=4&s=117" width="117">](https://github.com/avalla) |[<img alt="c0b41" src="https://avatars1.githubusercontent.com/u/2834954?v=4&s=117" width="117">](https://github.com/c0b41) |[<img alt="canvasbh" src="https://avatars3.githubusercontent.com/u/44477734?v=4&s=117" width="117">](https://github.com/canvasbh) |[<img alt="craigcbrunner" src="https://avatars3.githubusercontent.com/u/2780521?v=4&s=117" width="117">](https://github.com/craigcbrunner) |[<img alt="franckl" src="https://avatars0.githubusercontent.com/u/3875803?v=4&s=117" width="117">](https://github.com/franckl) |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[avalla](https://github.com/avalla) |[c0b41](https://github.com/c0b41) |[craigcbrunner](https://github.com/craigcbrunner) |[franckl](https://github.com/franckl) |[ninesalt](https://github.com/ninesalt) |[luntta](https://github.com/luntta) |
+[xhocquet](https://github.com/xhocquet) |[avalla](https://github.com/avalla) |[c0b41](https://github.com/c0b41) |[canvasbh](https://github.com/canvasbh) |[craigcbrunner](https://github.com/craigcbrunner) |[franckl](https://github.com/franckl) |
 
-[<img alt="rhymes" src="https://avatars3.githubusercontent.com/u/146201?v=4&s=117" width="117">](https://github.com/rhymes) |[<img alt="functino" src="https://avatars0.githubusercontent.com/u/415498?v=4&s=117" width="117">](https://github.com/functino) |
-:---: |:---: |
-[rhymes](https://github.com/rhymes) |[functino](https://github.com/functino) |
+[<img alt="ninesalt" src="https://avatars2.githubusercontent.com/u/7952255?v=4&s=117" width="117">](https://github.com/ninesalt) |[<img alt="luntta" src="https://avatars0.githubusercontent.com/u/14221637?v=4&s=117" width="117">](https://github.com/luntta) |[<img alt="rhymes" src="https://avatars3.githubusercontent.com/u/146201?v=4&s=117" width="117">](https://github.com/rhymes) |[<img alt="amitport" src="https://avatars1.githubusercontent.com/u/1131991?v=4&s=117" width="117">](https://github.com/amitport) |
+:---: |:---: |:---: |:---: |
+[ninesalt](https://github.com/ninesalt) |[luntta](https://github.com/luntta) |[rhymes](https://github.com/rhymes) |[amitport](https://github.com/amitport) |
 <!--/contributors-->
 
+## Software
+
+We use Browserstack for manual testing
+<a href="https://www.browserstack.com" target="_blank">
+  <img align="left" width="117" alt="BrowserStack logo" src="https://i.ibb.co/HDRDHmx/Browserstack-logo-2x.png">
+</a>
+
 ## License
 
 [The MIT License](LICENSE).

+ 10 - 9
bin/sync-version-numbers

@@ -13,10 +13,10 @@ set -o errexit
 set -o nounset
 
 # Set magic variables for current file & dir
-__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
-__base="$(basename ${__file} .sh)"
-__root="$(cd "$(dirname "${__dir}")" && pwd)"
+# __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+# __file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
+# __base="$(basename ${__file} .sh)"
+# __root="$(cd "$(dirname "${__dir}")" && pwd)"
 
 if [ "${ENDTOEND:=0}" = "1" ]; then
   echo "Publishing for e2e tests, skipping version number sync."
@@ -24,14 +24,15 @@ if [ "${ENDTOEND:=0}" = "1" ]; then
 fi
 
 commit_message="$(git log -1 --pretty=%B)"
-if [ "$commit_message" != "Release" ]; then
-  echo "Last commit is not a release commit, but '$commit_message'"
+if [ "${commit_message}" != "Release" ]; then
+  echo "Last commit is not a release commit, but '${commit_message}'"
   exit 1
 fi
 
 version_files="./examples/ README.md bin/upload-to-cdn.sh website/src/examples/ website/src/docs/ website/themes/uppy/layout/"
 main_package_version=$(node -p "require('./packages/uppy/package.json').version")
 # Legacy defeater for also renaming the old /dist/ locations, can be removed as soon as everything's on the new dist-less thing
-replace-x -r 'uppy/v\d+\.\d+\.\d+/dist/' "uppy/v$main_package_version/" $version_files --exclude=node_modules
-replace-x -r 'uppy/v\d+\.\d+\.\d+/' "uppy/v$main_package_version/" $version_files --exclude=node_modules
-git add $version_files # add changes to the Release commit
+replace-x -r 'uppy/v\d+\.\d+\.\d+/dist/' "uppy/v$main_package_version/" ${version_files} --exclude=node_modules
+replace-x -r 'uppy/v\d+\.\d+\.\d+/' "uppy/v$main_package_version/" ${version_files} --exclude=node_modules
+# replace-x -r 'uppy@\d+\.\d+\.\d+' "uppy@$main_package_version" ${version_files} --exclude=node_modules
+git add ${version_files} # add changes to the Release commit

+ 1 - 1
bin/upload-to-cdn.sh

@@ -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.30.3/uppy.css
+#  - Runs npm pack, and stores files to e.g. https://transloadit.edgly.net/releases/uppy/v0.30.4/uppy.css
 #  - Uses local package by default, if [version] argument was specified, takes package from npm
 #
 # Run as:

+ 2 - 2
examples/cdn-example/index.html

@@ -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.30.3/uppy.min.css" rel="stylesheet">
+    <link href="https://transloadit.edgly.net/releases/uppy/v0.30.4/uppy.min.css" rel="stylesheet">
   </head>
   <body>
     <button id="uppyModalOpener">Open Modal</button>
-    <script src="https://transloadit.edgly.net/releases/uppy/v0.30.3/uppy.min.js"></script>
+    <script src="https://transloadit.edgly.net/releases/uppy/v0.30.4/uppy.min.js"></script>
     <script>
       const uppy = Uppy.Core({debug: true, autoProceed: false})
         .use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })

+ 1 - 1
examples/transloadit-textarea/index.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html>
   <head>
-    <link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/v0.30.3/robodog.css">
+    <link rel="stylesheet" href="https://transloadit.edgly.net/releases/uppy/v0.30.4/robodog.css">
     <style>
       body {
         font-family: Roboto, Open Sans;

+ 2 - 2
examples/uppy-with-companion/client/index.html

@@ -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.30.3/uppy.min.css" rel="stylesheet">
+    <link href="https://transloadit.edgly.net/releases/uppy/v0.30.4/uppy.min.css" rel="stylesheet">
   </head>
   <body>
     <button id="uppyModalOpener">Open Modal</button>
-    <script src="https://transloadit.edgly.net/releases/uppy/v0.30.3/uppy.min.js"></script>
+    <script src="https://transloadit.edgly.net/releases/uppy/v0.30.4/uppy.min.js"></script>
     <script>
       const uppy = Uppy.Core({debug: true, autoProceed: false})
         .use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })

+ 55 - 3
package-lock.json

@@ -2789,6 +2789,11 @@
 						"punycode": "1.3.2",
 						"querystring": "0.2.0"
 					}
+				},
+				"uuid": {
+					"version": "3.3.2",
+					"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+					"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
 				}
 			}
 		},
@@ -15852,6 +15857,14 @@
 						"tough-cookie": "~2.4.3",
 						"tunnel-agent": "^0.6.0",
 						"uuid": "^3.3.2"
+					},
+					"dependencies": {
+						"uuid": {
+							"version": "3.3.2",
+							"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+							"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+							"dev": true
+						}
 					}
 				},
 				"safe-buffer": {
@@ -16152,6 +16165,14 @@
 						"tough-cookie": "~2.4.3",
 						"tunnel-agent": "^0.6.0",
 						"uuid": "^3.3.2"
+					},
+					"dependencies": {
+						"uuid": {
+							"version": "3.3.2",
+							"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+							"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+							"dev": true
+						}
 					}
 				},
 				"safe-buffer": {
@@ -21411,6 +21432,13 @@
 				"oauth-sign": "^0.8.2",
 				"qs": "^6.5.1",
 				"uuid": "^3.2.1"
+			},
+			"dependencies": {
+				"uuid": {
+					"version": "3.3.2",
+					"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+					"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
+				}
 			}
 		},
 		"request-promise-core": {
@@ -24373,9 +24401,9 @@
 			"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
 		},
 		"uuid": {
-			"version": "3.3.2",
-			"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
-			"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.2.tgz",
+			"integrity": "sha1-SL1WmPBnfjx5AaHEbvFbFkN5RyY="
 		},
 		"v8flags": {
 			"version": "2.1.1",
@@ -24822,6 +24850,14 @@
 						"tough-cookie": "~2.4.3",
 						"tunnel-agent": "^0.6.0",
 						"uuid": "^3.3.2"
+					},
+					"dependencies": {
+						"uuid": {
+							"version": "3.3.2",
+							"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+							"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+							"dev": true
+						}
 					}
 				},
 				"safe-buffer": {
@@ -25090,6 +25126,14 @@
 						"tough-cookie": "~2.4.3",
 						"tunnel-agent": "^0.6.0",
 						"uuid": "^3.3.2"
+					},
+					"dependencies": {
+						"uuid": {
+							"version": "3.3.2",
+							"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+							"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+							"dev": true
+						}
 					}
 				},
 				"safe-buffer": {
@@ -26256,6 +26300,14 @@
 						"tough-cookie": "~2.4.3",
 						"tunnel-agent": "^0.6.0",
 						"uuid": "^3.3.2"
+					},
+					"dependencies": {
+						"uuid": {
+							"version": "3.3.2",
+							"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+							"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+							"dev": true
+						}
 					}
 				},
 				"safe-buffer": {

+ 3 - 3
package.json

@@ -92,8 +92,8 @@
     "contributors:save": "replace-x -m '<!--contributors-->[\\s\\S]+<!--/contributors-->' \"<!--contributors-->\n## Contributors\n\n$(npm run --silent contributors:fetch)\n<!--/contributors-->\" README.md",
     "dev:browsersync": "browser-sync start --no-open --no-ghost-mode false --server examples/dev --port 3452 --serveStatic packages/uppy/dist --files \"examples/dev/bundle.js, packages/uppy/dist/uppy.min.css, packages/uppy/lib/**/*\"",
     "dev:watch-sandbox": "cd examples/dev && npm run watch:sandbox",
-    "dev:with-companion": "npm-run-all --parallel start:companion dev:watch-sandbox watch dev:browsersync",
-    "dev": "npm-run-all --parallel dev:watch-sandbox watch dev:browsersync",
+    "dev:with-companion": "npm-run-all --parallel start:companion dev:watch-sandbox watch:js:lib watch:css dev:browsersync",
+    "dev": "npm-run-all --parallel dev:watch-sandbox watch:js:lib watch:css dev:browsersync",
     "lint:fix": "npm run lint -- --fix",
     "lint:staged": "lint-staged",
     "lint": "eslint . --cache",
@@ -117,7 +117,7 @@
     "watch:css": "onchange 'packages/**/*.scss' --initial --verbose -- npm run build:css",
     "watch:js:bundle": "onchange 'packages/{@uppy/,}*/src/**/*.js' --initial --verbose -- npm run build:bundle",
     "watch:js:lib": "onchange 'packages/{@uppy/,}*/src/**/*.js' --initial --verbose -- npm run build:lib",
-    "watch": "npm-run-all --parallel watch:*",
+    "watch": "npm-run-all --parallel watch:**",
     "web:build-examples": "cd website && node build-examples.js",
     "web:build": "npm-run-all web:inject-bundles-misc web:generate web:build-examples web:inject-frontpagecodesample",
     "web:bundle-watch-inject": "onchange 'packages/uppy/dist/**/*.css' 'packages/uppy/dist/**/*.js' --initial --verbose -- npm run web:inject-bundles-misc",

+ 4 - 4
packages/@uppy/aws-s3-multipart/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/aws-s3-multipart",
   "description": "Upload to Amazon S3 with Uppy and S3's Multipart upload strategy",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
@@ -23,12 +23,12 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/companion-client": "0.28.3",
-    "@uppy/utils": "0.30.3",
+    "@uppy/companion-client": "0.28.4",
+    "@uppy/utils": "0.30.4",
     "resolve-url": "^0.2.1"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 8 - 8
packages/@uppy/aws-s3-multipart/types/aws-s3-multipart-tests.ts

@@ -1,34 +1,34 @@
-import Uppy, { UppyFile } from '@uppy/core';
-import AwsS3Multipart, { AwsS3Part } from '../';
+import Uppy = require('@uppy/core');
+import AwsS3Multipart = require('../');
 
 {
   const uppy = Uppy();
   uppy.use(AwsS3Multipart, {
     createMultipartUpload(file) {
-      file // $ExpectType UppyFile
+      file // $ExpectType Uppy.UppyFile
     },
     listParts(file, opts) {
-      file // $ExpectType UppyFile
+      file // $ExpectType Uppy.UppyFile
       opts.uploadId // $ExpectType string
       opts.key // $ExpectType string
     },
     prepareUploadPart(file, part) {
-      file // $ExpectType UppyFile
+      file // $ExpectType Uppy.UppyFile
       part.uploadId // $ExpectType string
       part.key // $ExpectType string
       part.body // $ExpectType Blob
       part.number // $ExpectType number
     },
     abortMultipartUpload(file, opts) {
-      file // $ExpectType UppyFile
+      file // $ExpectType Uppy.UppyFile
       opts.uploadId // $ExpectType string
       opts.key // $ExpectType string
     },
     completeMultipartUpload(file, opts) {
-      file // $ExpectType UppyFile
+      file // $ExpectType Uppy.UppyFile
       opts.uploadId // $ExpectType string
       opts.key // $ExpectType string
-      opts.parts[0] // $ExpectType AwsS3Part
+      opts.parts[0] // $ExpectType AwsS3Multipart.AwsS3Part
     },
   });
 }

+ 5 - 5
packages/@uppy/aws-s3/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/aws-s3",
   "description": "Upload to Amazon S3 with Uppy",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
@@ -22,13 +22,13 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/companion-client": "0.28.3",
-    "@uppy/utils": "0.30.3",
-    "@uppy/xhr-upload": "0.30.3",
+    "@uppy/companion-client": "0.28.4",
+    "@uppy/utils": "0.30.4",
+    "@uppy/xhr-upload": "0.30.4",
     "resolve-url": "^0.2.1"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 3 - 3
packages/@uppy/aws-s3/types/aws-s3-tests.ts

@@ -1,11 +1,11 @@
-import Uppy, { UppyFile } from '@uppy/core';
-import AwsS3 from '../';
+import Uppy = require('@uppy/core');
+import AwsS3 = require('../');
 
 {
   const uppy = Uppy();
   uppy.use(AwsS3, {
     getUploadParameters(file) {
-      file // $ExpectType UppyFile
+      file // $ExpectType Uppy.UppyFile
     }
   });
 }

+ 1 - 1
packages/@uppy/companion-client/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/companion-client",
   "description": "Client library for communication with Companion. Intended for use in Uppy plugins.",
-  "version": "0.28.3",
+  "version": "0.28.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 2 - 2
packages/@uppy/companion/Dockerfile

@@ -8,7 +8,7 @@ WORKDIR /app
 # * to optionally copy lock files that _might_ _not_ exist
 ADD package.json package-*.json yarn.* /tmp/
 RUN cd /tmp && apk --update add  --virtual native-dep \
-  make gcc g++ python libgcc libstdc++ && \
+  make gcc g++ python libgcc libstdc++ git && \
   npm  install && \
   apk del native-dep
 RUN mkdir -p /app && cd /app && ln -nfs /tmp/node_modules
@@ -18,4 +18,4 @@ ENV PATH "${PATH}:/app/node_modules/.bin"
 RUN npm run build
 CMD ["node","/app/lib/standalone/start-server.js"]
 # This can be overruled later
-EXPOSE 3020
+EXPOSE 3020

+ 3 - 3
packages/@uppy/companion/Dockerfile.test

@@ -1,18 +1,18 @@
 FROM alpine:3.6
 
 RUN apk add --update nodejs \
-	           nodejs-npm 
+	           nodejs-npm
 
 COPY package.json /app/package.json
 
 WORKDIR /app
 
 RUN apk --update add  --virtual native-dep \
-  make gcc g++ python libgcc libstdc++ && \
+  make gcc g++ python libgcc libstdc++ git && \
   npm  install && \
   apk del native-dep
 RUN apk add bash
 
 COPY . /app
 RUN npm install -g nodemon
-CMD ["npm","test"]
+CMD ["npm","test"]

+ 1 - 1
packages/@uppy/companion/README.md

@@ -15,7 +15,7 @@ Instagram, etc. **Companion is not a target to upload files to**. For this, use
 npm install @uppy/companion
 ```
 
-If you don't have a Node.js project with a `package.json` you might want to install/run Companion globally like so: `[sudo] npm install -g @uppy/companion@0.30.0`.
+If you don't have a Node.js project with a `package.json` you might want to install/run Companion globally like so: `[sudo] npm install -g @uppy/companion@0.17.4` (best check the actual latest version, and use that, so (re)installs are reproducible, and upgrades intentional).
 
 ## Usage
 

+ 15 - 2
packages/@uppy/companion/examples/serverless/index.js

@@ -3,6 +3,7 @@
 const express = require('express')
 const bodyParser = require('body-parser')
 const cors = require('cors')
+const session = require('express-session')
 const compression = require('compression')
 const awsServerlessExpress = require('aws-serverless-express')
 const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware')
@@ -12,8 +13,13 @@ const app = express()
 
 app.use(compression())
 app.use(cors())
+app.use(session({
+  secret: process.env.SESSION_SECRET,
+  resave: true,
+  saveUninitialized: true
+}))
 app.use(bodyParser.json())
-app.use(bodyParser.urlencoded({ extended: true }))
+app.use(bodyParser.urlencoded({ extended: false }))
 app.use(awsServerlessExpressMiddleware.eventContext())
 
 const host = process.env.DOMAIN.split('://')[1]
@@ -42,11 +48,18 @@ const options = {
   server: {
     host: host,
     protocol: protocol
-  }
+  },
+  filePath: '/tmp',
+  secret: process.env.UPPY_SECRET
 }
 
 app.use(uppy.app(options))
 
+app.get('/', (req, res) => {
+  res.setHeader('Content-Type', 'text/plain')
+  res.send("Hello there, here's a response from companion")
+})
+
 const server = awsServerlessExpress.createServer(app)
 
 exports.uppy = (event, context) =>

+ 10 - 6
packages/@uppy/companion/examples/serverless/package.json

@@ -2,11 +2,15 @@
   "name": "uploader",
   "version": "1.0.0",
   "dependencies": {
-    "aws-serverless-express": "^3.1.3",
-    "body-parser": "^1.18.2",
-    "compression": "^1.7.2",
-    "cors": "^2.8.4",
-    "express": "^4.16.3",
-    "@uppy/companion": "^0.11.2"
+    "@uppy/companion": "^0.17.3",
+    "aws-serverless-express": "^3.3.6",
+    "body-parser": "^1.18.3",
+    "compression": "^1.7.4",
+    "cors": "^2.8.5",
+    "express": "^4.16.4",
+    "express-session": "^1.15.6"
+  },
+  "devDependencies": {
+    "serverless-offline": "^4.9.3"
   }
 }

+ 9 - 2
packages/@uppy/companion/examples/serverless/serverless.yml

@@ -2,7 +2,7 @@ service: uppyloader
 
 provider:
   name: aws
-  runtime: nodejs6.10
+  runtime: nodejs8.10
 
   environment:
     # NOTE: Make sure you set this to the url of your service endpoint
@@ -14,6 +14,10 @@ provider:
     AWS_S3_BUCKET: <YOUR_AWS_S3_BUCKET_NAME>
     AWS_S3_REGION: <YOUR_AWS_S3_BUCKET_REGION>
 
+    SESSION_SECRET: <YOUR_SESSION_SECRET>
+
+    UPPY_SECRET: <YOUR_UPPY_SECRET>
+
     # INSTAGRAM_KEY: <YOUR_INSTAGRAM_KEY>
     # INSTAGRAM_SECRET: <YOUR_INSTAGRAM_SECRET>
 
@@ -25,8 +29,11 @@ provider:
 
 functions:
   uppy:
-    handler: handler.uppy
+    handler: index.uppy
 
     events:
       - http: ANY /
       - http: 'ANY {proxy+}'
+
+plugins:
+  - serverless-offline

+ 60 - 0
packages/@uppy/companion/package-lock.json

@@ -0,0 +1,60 @@
+{
+	"name": "@uppy/companion",
+	"version": "0.17.4",
+	"lockfileVersion": 1,
+	"requires": true,
+	"dependencies": {
+		"tus-js-client": {
+			"version": "github:ifedapoolarewaju/tus-js-client#888bcf73b66698a165f086f7bbe61951597f5c1b",
+			"from": "github:ifedapoolarewaju/tus-js-client#888bcf73b66698a165f086f7bbe61951597f5c1b",
+			"requires": {
+				"buffer-from": "^0.1.1",
+				"extend": "^3.0.0",
+				"js-base64": "^2.4.9",
+				"lodash.throttle": "^4.1.1",
+				"url-parse": "^1.4.3"
+			},
+			"dependencies": {
+				"buffer-from": {
+					"version": "0.1.2",
+					"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz",
+					"integrity": "sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg=="
+				},
+				"extend": {
+					"version": "3.0.2",
+					"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+					"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+				},
+				"js-base64": {
+					"version": "2.5.1",
+					"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
+					"integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw=="
+				},
+				"lodash.throttle": {
+					"version": "4.1.1",
+					"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+					"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
+				},
+				"querystringify": {
+					"version": "2.1.1",
+					"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz",
+					"integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA=="
+				},
+				"requires-port": {
+					"version": "1.0.0",
+					"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+					"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
+				},
+				"url-parse": {
+					"version": "1.4.4",
+					"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz",
+					"integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==",
+					"requires": {
+						"querystringify": "^2.0.0",
+						"requires-port": "^1.0.0"
+					}
+				}
+			}
+		}
+	}
+}

+ 5 - 5
packages/@uppy/companion/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@uppy/companion",
-  "version": "0.17.3",
+  "version": "0.17.4",
   "description": "OAuth helper and remote fetcher for Uppy's (https://uppy.io) 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/uppy.js",
   "types": "types/index.d.ts",
@@ -54,10 +54,10 @@
     "purest": "3.0.0",
     "redis": "2.8.0",
     "request": "2.85.0",
-    "serialize-error": "2.1.0",
-    "tus-js-client": "1.6.0",
-    "uuid": "3.3.2",
-    "validator": "9.4.1",
+    "serialize-error": "^2.1.0",
+    "tus-js-client": "github:ifedapoolarewaju/tus-js-client#888bcf73b66698a165f086f7bbe61951597f5c1b",
+    "uuid": "2.0.2",
+    "validator": "^9.0.0",
     "ws": "1.1.5"
   },
   "devDependencies": {

+ 65 - 26
packages/@uppy/companion/src/server/Uploader.js

@@ -1,4 +1,5 @@
 const fs = require('fs')
+const stream = require('stream')
 const path = require('path')
 const tus = require('tus-js-client')
 const uuid = require('uuid')
@@ -13,6 +14,10 @@ const headerSanitize = require('./header-blacklist')
 
 class Uploader {
   /**
+   * Uploads file to destination based on the supplied protocol (tus, s3-multipart, multipart)
+   * For tus uploads, the deferredLength option is enabled, because file size value can be unreliable
+   * for some providers (Instagram particularly)
+   *
    * @typedef {object} UploaderOptions
    * @property {string} endpoint
    * @property {string=} uploadUrl
@@ -38,13 +43,23 @@ class Uploader {
     this.options = options
     this.token = uuid.v4()
     this.options.path = `${this.options.pathPrefix}/${Uploader.FILE_NAME_PREFIX}-${this.token}`
-    this.writer = fs.createWriteStream(this.options.path, { mode: 0o666 }) // no executable files
+    this.streamsEnded = false
+    this.duplexStream = new stream.PassThrough()
+      .on('error', (err) => logger.error(`${this.shortToken} ${err}`, 'uploader.duplex.error'))
+    this.writeStream = fs.createWriteStream(this.options.path, { mode: 0o666 }) // no executable files
       .on('error', (err) => logger.error(`${this.shortToken} ${err}`, 'uploader.write.error'))
     /** @type {number} */
     this.emittedProgress = 0
     this.storage = options.storage
   }
 
+  /**
+   * the number of bytes written into the streams
+   */
+  get bytesWritten () {
+    return this.writeStream.bytesWritten
+  }
+
   /**
    * Validate the options passed down to the uplaoder
    *
@@ -116,28 +131,31 @@ class Uploader {
    * @param {Buffer | Buffer[]} chunk
    */
   handleChunk (chunk) {
-    logger.debug(`${this.shortToken} ${this.writer.bytesWritten} bytes`, 'uploader.download.progress')
+    logger.debug(`${this.shortToken} ${this.bytesWritten} bytes`, 'uploader.download.progress')
 
     const protocol = this.options.protocol || 'multipart'
 
     // The download has completed; close the file and start an upload if necessary.
     if (chunk === null) {
-      if (this.options.endpoint && protocol === 'multipart') {
-        this.writer.on('finish', () => {
+      this.writeStream.on('finish', () => {
+        this.streamsEnded = true
+        if (this.options.endpoint && protocol === 'multipart') {
           this.uploadMultipart()
-        })
-      }
-      return this.writer.end()
+        }
+      })
+
+      this.duplexStream.end()
+      return this.writeStream.end()
     }
 
-    this.writer.write(chunk, () => {
+    this.writeToStreams(chunk, () => {
       if (protocol === 's3-multipart' && !this.s3Upload) {
         return this.uploadS3Streaming()
       }
       if (!this.options.endpoint) return
 
       if (protocol === 'tus' && !this.tus) {
-        return this.uploadTus()
+        return this.uploadTus(true)
       }
     })
   }
@@ -147,11 +165,11 @@ class Uploader {
    * @param {object} resp
    */
   handleResponse (resp) {
-    resp.pipe(this.writer)
+    resp.pipe(this.writeStream)
 
     const protocol = this.options.protocol || 'multipart'
 
-    this.writer.on('finish', () => {
+    this.writeStream.on('finish', () => {
       if (protocol === 's3-multipart') {
         this.uploadS3Full()
       }
@@ -159,7 +177,7 @@ class Uploader {
       if (!this.options.endpoint) return
 
       if (protocol === 'tus') {
-        this.uploadTus()
+        this.uploadTus(false)
       }
       if (protocol === 'multipart') {
         this.uploadMultipart()
@@ -167,6 +185,23 @@ class Uploader {
     })
   }
 
+  /**
+   * @param {Buffer | Buffer[]} chunk
+   * @param {function} cb
+   */
+  writeToStreams (chunk, cb) {
+    const done = []
+    const onDone = () => {
+      done.push(true)
+      if (done.length >= 2) {
+        cb()
+      }
+    }
+
+    this.duplexStream.write(chunk, onDone)
+    this.writeStream.write(chunk, onDone)
+  }
+
   getResponse () {
     if (this._errRespMessage) {
       return { body: this._errRespMessage, status: 400 }
@@ -190,6 +225,9 @@ class Uploader {
    */
   emitProgress (bytesUploaded, bytesTotal) {
     bytesTotal = bytesTotal || this.options.size
+    if (this.tus && this.tus.options.uploadLengthDeferred && this.streamsEnded) {
+      bytesTotal = this.bytesWritten
+    }
     const percentage = (bytesUploaded / bytesTotal * 100)
     const formatPercentage = percentage.toFixed(2)
     logger.debug(
@@ -240,21 +278,31 @@ class Uploader {
     emitter().emit(this.token, dataToEmit)
   }
 
-  uploadTus () {
+  /**
+   *
+   * @param {boolean} deferLength
+   */
+  uploadTus (deferLength) {
     const fname = path.basename(this.options.path)
     const ftype = this.options.metadata.type
     const metadata = Object.assign({ filename: fname, filetype: ftype }, this.options.metadata || {})
-    const file = fs.createReadStream(this.options.path)
+    const file = deferLength ? this.duplexStream : fs.createReadStream(this.options.path)
     const uploader = this
+    const oneGB = 1024 * 1024 * 1024  // 1 GB
+    // chunk size can't be infinity with deferred length.
+    // cap value to 1GB to avoid buffer allocation error (RangeError)
+    const chunkSize = Math.min(this.options.size || oneGB, oneGB)
 
     // @ts-ignore
     this.tus = new tus.Upload(file, {
       endpoint: this.options.endpoint,
       uploadUrl: this.options.uploadUrl,
+      // @ts-ignore
+      uploadLengthDeferred: deferLength,
       resume: true,
-      uploadSize: this.options.size || fs.statSync(this.options.path).size,
+      uploadSize: deferLength ? null : (this.options.size || fs.statSync(this.options.path).size),
       metadata,
-      chunkSize: this.writer.bytesWritten,
+      chunkSize,
       /**
        *
        * @param {Error} error
@@ -271,15 +319,6 @@ class Uploader {
       onProgress (bytesUploaded, bytesTotal) {
         uploader.emitProgress(bytesUploaded, bytesTotal)
       },
-      /**
-       *
-       * @param {number} chunkSize
-       * @param {number} bytesUploaded
-       * @param {number} bytesTotal
-       */
-      onChunkComplete (chunkSize, bytesUploaded, bytesTotal) {
-        uploader.tus.options.chunkSize = uploader.writer.bytesWritten - bytesUploaded
-      },
       onSuccess () {
         uploader.emitSuccess(uploader.tus.url)
         uploader.cleanUp()
@@ -350,7 +389,7 @@ class Uploader {
       tail: true
     })
 
-    this.writer.on('finish', () => {
+    this.writeStream.on('finish', () => {
       file.close()
     })
 

+ 2 - 2
packages/@uppy/companion/src/server/provider/drive/adapter.js

@@ -14,8 +14,8 @@ exports.isFolder = (item) => {
   return item.mimeType === 'application/vnd.google-apps.folder' || item.kind === 'drive#teamDrive'
 }
 
-exports.getItemData = (item) => {
-  return Object.assign({}, item, { size: parseFloat(item.size) })
+exports.getItemSize = (item) => {
+  return parseInt(item.size, 10)
 }
 
 exports.getItemIcon = (item) => {

+ 1 - 0
packages/@uppy/companion/src/server/provider/drive/index.js

@@ -146,6 +146,7 @@ class Drive {
         thumbnail: uppy.buildURL(adapter.getItemThumbnailUrl(item), true),
         requestPath: adapter.getItemRequestPath(item),
         modifiedDate: adapter.getItemModifiedDate(item),
+        size: adapter.getItemSize(item),
         custom: {
           isTeamDrive: adapter.isTeamDrive(item)
         }

+ 4 - 0
packages/@uppy/companion/src/server/provider/dropbox/adapter.js

@@ -6,6 +6,10 @@ exports.isFolder = (item) => {
   return item['.tag'] === 'folder'
 }
 
+exports.getItemSize = (item) => {
+  return item.size
+}
+
 exports.getItemIcon = (item) => {
   return item['.tag']
 }

+ 2 - 1
packages/@uppy/companion/src/server/provider/dropbox/index.js

@@ -164,7 +164,8 @@ class DropBox {
         id: adapter.getItemId(item),
         thumbnail: uppy.buildURL(adapter.getItemThumbnailUrl(item), true),
         requestPath: adapter.getItemRequestPath(item),
-        modifiedDate: adapter.getItemModifiedDate(item)
+        modifiedDate: adapter.getItemModifiedDate(item),
+        size: adapter.getItemSize(item)
       })
     })
 

+ 2 - 2
packages/@uppy/core/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/core",
   "description": "Core module for the extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
@@ -21,7 +21,7 @@
   },
   "dependencies": {
     "@uppy/store-default": "0.28.3",
-    "@uppy/utils": "0.30.3",
+    "@uppy/utils": "0.30.4",
     "cuid": "^2.1.1",
     "lodash.throttle": "^4.1.1",
     "mime-match": "^1.0.2",

+ 4 - 4
packages/@uppy/core/src/__snapshots__/index.test.js.snap

@@ -27,14 +27,14 @@ Object {
       "name": "foo.jpg",
       "preview": undefined,
       "progress": Object {
-        "bytesTotal": 0,
+        "bytesTotal": null,
         "bytesUploaded": 0,
         "percentage": 0,
         "uploadComplete": false,
         "uploadStarted": false,
       },
       "remote": "",
-      "size": 0,
+      "size": null,
       "source": "jest",
       "type": "image/jpeg",
     },
@@ -50,14 +50,14 @@ Object {
       "name": "bar.jpg",
       "preview": undefined,
       "progress": Object {
-        "bytesTotal": 0,
+        "bytesTotal": null,
         "bytesUploaded": 0,
         "percentage": 0,
         "uploadComplete": false,
         "uploadStarted": false,
       },
       "remote": "",
-      "size": 0,
+      "size": null,
       "source": "jest",
       "type": "image/jpeg",
     },

+ 14 - 11
packages/@uppy/core/src/index.js

@@ -366,7 +366,8 @@ class Uppy {
       }
     }
 
-    if (maxFileSize) {
+    // We can't check maxFileSize if the size is unknown.
+    if (maxFileSize && file.data.size != null) {
       if (file.data.size > maxFileSize) {
         throw new Error(`${this.i18n('exceedsSize')} ${prettyBytes(maxFileSize)}`)
       }
@@ -427,6 +428,8 @@ class Uppy {
     meta.name = fileName
     meta.type = fileType
 
+    // `null` means the size is unknown.
+    const size = isFinite(file.data.size) ? file.data.size : null
     const newFile = {
       source: file.source || '',
       id: fileID,
@@ -438,11 +441,11 @@ class Uppy {
       progress: {
         percentage: 0,
         bytesUploaded: 0,
-        bytesTotal: file.data.size || 0,
+        bytesTotal: size,
         uploadComplete: false,
         uploadStarted: false
       },
-      size: file.data.size || 0,
+      size: size,
       isRemote: isRemote,
       remote: file.remote || '',
       preview: file.preview
@@ -629,11 +632,17 @@ class Uppy {
       return
     }
 
+    // bytesTotal may be null or zero; in that case we can't divide by it
+    const canHavePercentage = isFinite(data.bytesTotal) && data.bytesTotal > 0
     this.setFileState(file.id, {
       progress: Object.assign({}, this.getFile(file.id).progress, {
         bytesUploaded: data.bytesUploaded,
         bytesTotal: data.bytesTotal,
-        percentage: Math.floor((data.bytesUploaded / data.bytesTotal * 100).toFixed(2))
+        percentage: canHavePercentage
+          // TODO(goto-bus-stop) flooring this should probably be the choice of the UI?
+          // we get more accurate calculations if we don't round this at all.
+          ? Math.floor(data.bytesUploaded / data.bytesTotal * 100)
+          : 0
       })
     })
 
@@ -1026,13 +1035,7 @@ class Uppy {
       return
     }
 
-    if (msg === `${msg}`) {
-      console.log(message)
-    } else {
-      message = `[Uppy] [${getTimeStamp()}]`
-      console.log(message)
-      console.dir(msg)
-    }
+    console.log(message)
   }
 
   /**

+ 68 - 6
packages/@uppy/core/src/index.test.js

@@ -992,7 +992,7 @@ describe('src/Core', () => {
 
       const fileId = Object.keys(core.getState().files)[0]
       const file = core.getFile(fileId)
-      core._calculateProgress(file, {
+      core.emit('upload-progress', file, {
         bytesUploaded: 12345,
         bytesTotal: 17175
       })
@@ -1004,7 +1004,7 @@ describe('src/Core', () => {
         uploadStarted: false
       })
 
-      core._calculateProgress(file, {
+      core.emit('upload-progress', file, {
         bytesUploaded: 17175,
         bytesTotal: 17175
       })
@@ -1017,6 +1017,68 @@ describe('src/Core', () => {
       })
     })
 
+    it('should work with unsized files', async () => {
+      const core = new Core()
+      let proceedUpload
+      let finishUpload
+      const promise = new Promise((resolve) => { proceedUpload = resolve })
+      const finishPromise = new Promise((resolve) => { finishUpload = resolve })
+      core.addUploader(async ([id]) => {
+        core.emit('upload-started', core.getFile(id))
+        await promise
+        core.emit('upload-progress', core.getFile(id), {
+          bytesTotal: 3456,
+          bytesUploaded: 1234
+        })
+        await finishPromise
+        core.emit('upload-success', core.getFile(id), { uploadURL: 'lol' })
+      })
+
+      core.addFile({
+        source: 'instagram',
+        name: 'foo.jpg',
+        type: 'image/jpeg',
+        data: {}
+      })
+
+      core._calculateTotalProgress()
+
+      const uploadPromise = core.upload()
+      await new Promise((resolve) => core.once('upload-started', resolve))
+
+      expect(core.getFiles()[0].size).toBeNull()
+      expect(core.getFiles()[0].progress).toMatchObject({
+        bytesUploaded: 0,
+        // null indicates unsized
+        bytesTotal: null,
+        percentage: 0
+      })
+
+      proceedUpload()
+      // wait for progress event
+      await promise
+
+      expect(core.getFiles()[0].size).toBeNull()
+      expect(core.getFiles()[0].progress).toMatchObject({
+        bytesUploaded: 1234,
+        bytesTotal: 3456,
+        percentage: 35
+      })
+
+      finishUpload()
+      // wait for success event
+      await finishPromise
+
+      expect(core.getFiles()[0].size).toBeNull()
+      expect(core.getFiles()[0].progress).toMatchObject({
+        bytesUploaded: 3456,
+        bytesTotal: 3456,
+        percentage: 100
+      })
+
+      await uploadPromise
+    })
+
     it('should calculate the total progress of all file uploads', () => {
       const core = new Core()
 
@@ -1037,12 +1099,12 @@ describe('src/Core', () => {
       core.setFileState(file1.id, { progress: Object.assign({}, file1.progress, { uploadStarted: new Date() }) })
       core.setFileState(file2.id, { progress: Object.assign({}, file2.progress, { uploadStarted: new Date() }) })
 
-      core._calculateProgress(core.getFile(file1.id), {
+      core.emit('upload-progress', core.getFile(file1.id), {
         bytesUploaded: 12345,
         bytesTotal: 17175
       })
 
-      core._calculateProgress(core.getFile(file2.id), {
+      core.emit('upload-progress', core.getFile(file2.id), {
         bytesUploaded: 10201,
         bytesTotal: 17175
       })
@@ -1073,12 +1135,12 @@ describe('src/Core', () => {
       core.setFileState(file1.id, { progress: Object.assign({}, file1.progress, { uploadStarted: new Date() }) })
       core.setFileState(file2.id, { progress: Object.assign({}, file2.progress, { uploadStarted: new Date() }) })
 
-      core._calculateProgress(core.getFile(file1.id), {
+      core.emit('upload-progress', core.getFile(file1.id), {
         bytesUploaded: 12345,
         bytesTotal: 17175
       })
 
-      core._calculateProgress(core.getFile(file2.id), {
+      core.emit('upload-progress', core.getFile(file2.id), {
         bytesUploaded: 10201,
         bytesTotal: 17175
       })

+ 8 - 8
packages/@uppy/dashboard/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/dashboard",
   "description": "Universal UI plugin for Uppy.",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
@@ -22,11 +22,11 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/informer": "0.30.3",
-    "@uppy/provider-views": "0.30.3",
-    "@uppy/status-bar": "0.30.3",
-    "@uppy/thumbnail-generator": "0.30.3",
-    "@uppy/utils": "0.30.3",
+    "@uppy/informer": "0.30.4",
+    "@uppy/provider-views": "0.30.4",
+    "@uppy/status-bar": "0.30.4",
+    "@uppy/thumbnail-generator": "0.30.4",
+    "@uppy/utils": "0.30.4",
     "classnames": "^2.2.6",
     "cuid": "^2.1.1",
     "drag-drop": "2.13.3",
@@ -37,8 +37,8 @@
     "resize-observer-polyfill": "^1.5.0"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3",
-    "@uppy/google-drive": "0.30.3"
+    "@uppy/core": "0.30.4",
+    "@uppy/google-drive": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 1 - 0
packages/@uppy/dashboard/src/components/Dashboard.js

@@ -35,6 +35,7 @@ module.exports = function Dashboard (props) {
     { 'uppy-Dashboard--modal': !props.inline },
     { 'uppy-size--md': props.containerWidth > 576 },
     { 'uppy-size--lg': props.containerWidth > 700 },
+    { 'uppy-size--xl': props.containerWidth > 900 },
     { 'uppy-Dashboard--isAddFilesPanelVisible': props.showAddFilesPanel },
     { 'uppy-Dashboard--isInnerWrapVisible': props.areInsidesReadyToBeVisible }
   )

+ 26 - 6
packages/@uppy/dashboard/src/style.scss

@@ -328,11 +328,11 @@
     display: inline-block;
     vertical-align: text-top;
     overflow: hidden;
-    transition: transform ease-in-out 0.2s;
+    transition: transform ease-in-out .15s;
   }
 
   .uppy-DashboardTab-btn:hover svg {
-    transform: scale(1.1, 1.1);
+    transform: translateZ(0) scale(1.1, 1.1);
   }
 
 .uppy-DashboardTab-name {
@@ -620,18 +620,30 @@ a.uppy-Dashboard-poweredBy {
   padding-bottom: 10px;
   padding-left: 10px;
 
+  $rl-margin: 15px;
   .uppy-size--md & {
-    flex-direction: column;
     float: left;
-    width: 140px;
+    margin: 5px $rl-margin;
+    width: calc(33.333% - #{$rl-margin} - #{$rl-margin});
     height: 170px;
-    margin: 5px 15px;
-    border: 0;
+
+    flex-direction: column;
     background-color: initial;
+    border: 0;
     border-bottom: none;
     padding-bottom: 0;
     padding-left: 0;
   }
+
+  .uppy-size--lg & {
+    width: calc(25% - #{$rl-margin} - #{$rl-margin});
+    height: 190px;
+  }
+
+  .uppy-size--xl & {
+    width: calc(20% - #{$rl-margin} - #{$rl-margin});
+    height: 210px;
+  }
 }
 
 .uppy-DashboardItem-preview {
@@ -648,6 +660,14 @@ a.uppy-Dashboard-poweredBy {
     height: 100px;
     border: 0;
   }
+
+  .uppy-size--lg & {
+    height: 120px;
+  }
+
+  .uppy-size--xl & {
+    height: 140px;
+  }
 }
 
 .uppy-DashboardItem-previewLink {

+ 3 - 3
packages/@uppy/dashboard/types/dashboard-tests.ts

@@ -1,5 +1,5 @@
-import Uppy from '@uppy/core';
-import Dashboard from '../';
+import Uppy = require('@uppy/core')
+import Dashboard = require('../')
 
 {
   const uppy = Uppy()
@@ -7,7 +7,7 @@ import Dashboard from '../';
     target: 'body'
   })
 
-  const plugin = <Dashboard>uppy.getPlugin('Dashboard')
+  const plugin = uppy.getPlugin('Dashboard') as Dashboard
   plugin.openModal()
   plugin.isModalOpen() // $ExpectType boolean
   plugin.closeModal()

+ 3 - 3
packages/@uppy/drag-drop/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/drag-drop",
   "description": "Droppable zone UI for Uppy. Drag and drop files into it to upload.",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
@@ -25,12 +25,12 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/utils": "0.30.3",
+    "@uppy/utils": "0.30.4",
     "drag-drop": "2.13.3",
     "preact": "^8.2.9"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 5 - 5
packages/@uppy/dropbox/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/dropbox",
   "description": "Import files from Dropbox, into Uppy.",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
@@ -20,13 +20,13 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/companion-client": "0.28.3",
-    "@uppy/provider-views": "0.30.3",
-    "@uppy/utils": "0.30.3",
+    "@uppy/companion-client": "0.28.4",
+    "@uppy/provider-views": "0.30.4",
+    "@uppy/utils": "0.30.4",
     "preact": "^8.2.9"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 3 - 3
packages/@uppy/file-input/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/file-input",
   "description": "Simple UI of a file input button that works with Uppy right out of the box",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
@@ -22,11 +22,11 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/utils": "0.30.3",
+    "@uppy/utils": "0.30.4",
     "preact": "^8.2.9"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 3 - 3
packages/@uppy/form/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/form",
   "description": "Connect Uppy to an existing HTML <form>.",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
@@ -20,11 +20,11 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/utils": "0.30.3",
+    "@uppy/utils": "0.30.4",
     "get-form-data": "^2.0.0"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 3 - 3
packages/@uppy/golden-retriever/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/golden-retriever",
   "description": "The GoldenRetriever Uppy plugin saves selected files in browser cache to seamlessly resume uploding after browser crash or accidentally closed tab",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
@@ -23,11 +23,11 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/utils": "0.30.3",
+    "@uppy/utils": "0.30.4",
     "prettier-bytes": "^1.0.4"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 5 - 5
packages/@uppy/google-drive/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/google-drive",
   "description": "The Google Drive plugin for Uppy lets users import files from their Google Drive account",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
@@ -21,13 +21,13 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/companion-client": "0.28.3",
-    "@uppy/provider-views": "0.30.3",
-    "@uppy/utils": "0.30.3",
+    "@uppy/companion-client": "0.28.4",
+    "@uppy/provider-views": "0.30.4",
+    "@uppy/utils": "0.30.4",
     "preact": "^8.2.9"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 3 - 3
packages/@uppy/informer/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/informer",
   "description": "A notification and error pop-up bar for Uppy.",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
@@ -23,11 +23,11 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/utils": "0.30.3",
+    "@uppy/utils": "0.30.4",
     "preact": "^8.2.9"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 5 - 5
packages/@uppy/instagram/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/instagram",
   "description": "Import photos and videos from Instagram, into Uppy.",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
@@ -23,13 +23,13 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/companion-client": "0.28.3",
-    "@uppy/provider-views": "0.30.3",
-    "@uppy/utils": "0.30.3",
+    "@uppy/companion-client": "0.28.4",
+    "@uppy/provider-views": "0.30.4",
+    "@uppy/utils": "0.30.4",
     "preact": "^8.2.9"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 3 - 3
packages/@uppy/progress-bar/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/progress-bar",
   "description": "A progress bar UI for Uppy",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
@@ -23,11 +23,11 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/utils": "0.30.3",
+    "@uppy/utils": "0.30.4",
     "preact": "^8.2.9"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 3 - 3
packages/@uppy/provider-views/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/provider-views",
   "description": "View library for Uppy remote provider plugins.",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
@@ -19,12 +19,12 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/utils": "0.30.3",
+    "@uppy/utils": "0.30.4",
     "classnames": "^2.2.6",
     "preact": "^8.2.9"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 7 - 7
packages/@uppy/react/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/react",
   "description": "React component wrappers around Uppy's official UI plugins.",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "index.js",
   "module": "index.mjs",
@@ -22,15 +22,15 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/dashboard": "0.30.3",
-    "@uppy/drag-drop": "0.30.3",
-    "@uppy/progress-bar": "0.30.3",
-    "@uppy/status-bar": "0.30.3",
-    "@uppy/utils": "0.30.3",
+    "@uppy/dashboard": "0.30.4",
+    "@uppy/drag-drop": "0.30.4",
+    "@uppy/progress-bar": "0.30.4",
+    "@uppy/status-bar": "0.30.4",
+    "@uppy/utils": "0.30.4",
     "prop-types": "^15.6.1"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 2 - 2
packages/@uppy/redux-dev-tools/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/redux-dev-tools",
   "description": "Redux developer tools plugin for Uppy that simply syncs Uppy’s state with redux-devtools browser or JS extensions, and allows for basic time travel",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
@@ -21,7 +21,7 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 12 - 12
packages/@uppy/robodog/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/robodog",
   "description": "Transloadit SDK for browsers based on Uppy",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
@@ -28,17 +28,17 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/core": "0.30.3",
-    "@uppy/dashboard": "0.30.3",
-    "@uppy/dropbox": "0.30.3",
-    "@uppy/form": "0.30.3",
-    "@uppy/google-drive": "0.30.3",
-    "@uppy/instagram": "0.30.3",
-    "@uppy/status-bar": "0.30.3",
-    "@uppy/transloadit": "0.30.3",
-    "@uppy/url": "0.30.3",
-    "@uppy/utils": "0.30.3",
-    "@uppy/webcam": "0.30.3",
+    "@uppy/core": "0.30.4",
+    "@uppy/dashboard": "0.30.4",
+    "@uppy/dropbox": "0.30.4",
+    "@uppy/form": "0.30.4",
+    "@uppy/google-drive": "0.30.4",
+    "@uppy/instagram": "0.30.4",
+    "@uppy/status-bar": "0.30.4",
+    "@uppy/transloadit": "0.30.4",
+    "@uppy/url": "0.30.4",
+    "@uppy/utils": "0.30.4",
+    "@uppy/webcam": "0.30.4",
     "es6-promise": "4.2.5",
     "whatwg-fetch": "3.0.0"
   }

+ 3 - 3
packages/@uppy/status-bar/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/status-bar",
   "description": "A progress bar for Uppy, with many bells and whistles.",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
@@ -26,14 +26,14 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/utils": "0.30.3",
+    "@uppy/utils": "0.30.4",
     "classnames": "^2.2.6",
     "lodash.throttle": "^4.1.1",
     "preact": "^8.2.9",
     "prettier-bytes": "^1.0.4"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 2 - 2
packages/@uppy/status-bar/src/StatusBar.js

@@ -298,8 +298,8 @@ const ProgressBarError = ({ error, retryAll, hideRetryButton, i18n }) => {
       } */}
       <span class="uppy-StatusBar-details"
         aria-label={error}
-        data-microtip-position="top"
-        data-microtip-size="large"
+        data-microtip-position="top-right"
+        data-microtip-size="medium"
         role="tooltip">?</span>
     </div>
   )

+ 1 - 5
packages/@uppy/status-bar/src/style.scss

@@ -282,7 +282,7 @@
   display: inline-block;
   vertical-align: middle;
   color: $color-white;
-  background-color: rgba($color-black, 0.2);
+  background-color: $color-asphalt-gray;
   border-radius: 50%;
   position: relative;
   top: 0;
@@ -290,10 +290,6 @@
   font-size: 10px;
   text-align: center;
   cursor: help;
-
-  &:hover {
-    cursor: help;
-  }
 }
 
 .uppy-StatusBar-details:after {

+ 3 - 3
packages/@uppy/thumbnail-generator/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/thumbnail-generator",
   "description": "Uppy plugin that generates small previews of images to show on your upload UI.",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
@@ -22,10 +22,10 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/utils": "0.30.3"
+    "@uppy/utils": "0.30.4"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3",
+    "@uppy/core": "0.30.4",
     "namespace-emitter": "^2.0.1"
   },
   "peerDependencies": {

+ 6 - 6
packages/@uppy/transloadit/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/transloadit",
   "description": "The Transloadit plugin can be used to upload files to Transloadit for all kinds of processing, such as transcoding video, resizing images, zipping/unzipping, and more",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
@@ -27,15 +27,15 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/companion-client": "0.28.3",
-    "@uppy/provider-views": "0.30.3",
-    "@uppy/tus": "0.30.3",
-    "@uppy/utils": "0.30.3",
+    "@uppy/companion-client": "0.28.4",
+    "@uppy/provider-views": "0.30.4",
+    "@uppy/tus": "0.30.4",
+    "@uppy/utils": "0.30.4",
     "component-emitter": "^1.2.1",
     "socket.io-client": "^2.1.1"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 14 - 14
packages/@uppy/transloadit/types/transloadit-tests.ts

@@ -1,11 +1,11 @@
-import Uppy, { UppyFile } from '@uppy/core';
-import Transloadit  from '../';
+import Uppy = require('@uppy/core')
+import Transloadit = require('../')
 
 {
-  const uppy = Uppy();
+  const uppy = Uppy()
   uppy.use(Transloadit, {
     getAssemblyOptions(file) {
-      file // $ExpectType UppyFile
+      file // $ExpectType Uppy.UppyFile
     },
     waitForEncoding: false,
     waitForMetadata: true,
@@ -14,40 +14,40 @@ import Transloadit  from '../';
       auth: { key: 'abc' },
       steps: {}
     }
-  });
+  })
 }
 
 {
-  const uppy = Uppy();
+  const uppy = Uppy()
   // $ExpectError
-  uppy.use(Transloadit, { waitForEncoding: null });
+  uppy.use(Transloadit, { waitForEncoding: null })
   // $ExpectError
-  uppy.use(Transloadit, { waitForMetadata: null });
+  uppy.use(Transloadit, { waitForMetadata: null })
 }
 
 {
-  const uppy = Uppy();
+  const uppy = Uppy()
   // $ExpectError
-  uppy.use(Transloadit, { params: {} });
+  uppy.use(Transloadit, { params: {} })
   // $ExpectError
-  uppy.use(Transloadit, { params: { auth: {} } });
+  uppy.use(Transloadit, { params: { auth: {} } })
   // $ExpectError
   uppy.use(Transloadit, {
     params: {
       auth: { key: null }
     }
-  });
+  })
   // $ExpectError
   uppy.use(Transloadit, {
     params: {
       auth: { key: 'abc' },
       steps: 'test'
     }
-  });
+  })
   uppy.use(Transloadit, {
     params: {
       auth: { key: 'abc' },
       steps: { name: {} }
     }
-  });
+  })
 }

+ 5 - 5
packages/@uppy/tus/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/tus",
   "description": "Resumable uploads for Uppy using Tus.io",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
@@ -22,12 +22,12 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/companion-client": "0.28.3",
-    "@uppy/utils": "0.30.3",
-    "tus-js-client": "1.6.0"
+    "@uppy/companion-client": "0.28.4",
+    "@uppy/utils": "0.30.4",
+    "tus-js-client": "github:ifedapoolarewaju/tus-js-client#888bcf73b66698a165f086f7bbe61951597f5c1b"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 4 - 4
packages/@uppy/url/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/url",
   "description": "The Url plugin lets users import files from the Internet. Paste any URL and it’ll be added!",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
@@ -22,12 +22,12 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/companion-client": "0.28.3",
-    "@uppy/utils": "0.30.3",
+    "@uppy/companion-client": "0.28.4",
+    "@uppy/utils": "0.30.4",
     "preact": "^8.2.9"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 1 - 1
packages/@uppy/utils/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/utils",
   "description": "Shared utility functions for Uppy Core and plugins maintained by the Uppy team.",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",

+ 3 - 3
packages/@uppy/webcam/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/webcam",
   "description": "Uppy plugin that takes photos or records videos using the device's camera.",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
@@ -26,11 +26,11 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/utils": "0.30.3",
+    "@uppy/utils": "0.30.4",
     "preact": "^8.2.9"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 4 - 4
packages/@uppy/xhr-upload/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@uppy/xhr-upload",
   "description": "Plain and simple classic HTML multipart form uploads with Uppy, as well as uploads using the HTTP PUT method.",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
@@ -24,12 +24,12 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/companion-client": "0.28.3",
-    "@uppy/utils": "0.30.3",
+    "@uppy/companion-client": "0.28.4",
+    "@uppy/utils": "0.30.4",
     "cuid": "^2.1.1"
   },
   "devDependencies": {
-    "@uppy/core": "0.30.3"
+    "@uppy/core": "0.30.4"
   },
   "peerDependencies": {
     "@uppy/core": "^0.30.0"

+ 10 - 2
packages/@uppy/xhr-upload/src/index.js

@@ -75,6 +75,14 @@ module.exports = class XHRUpload extends Plugin {
        */
       getResponseError (responseText, response) {
         return new Error('Upload error')
+      },
+      /**
+       * @param {number} status the response status code
+       * @param {string} responseText the response body string
+       * @param {XMLHttpRequest | respObj} response the response object (XHR or similar)
+       */
+      validateStatus (status, responseText, response) {
+        return status >= 200 && status < 300
       }
     }
 
@@ -228,7 +236,7 @@ module.exports = class XHRUpload extends Plugin {
         this.uppy.log(`[XHRUpload] ${id} finished`)
         timer.done()
 
-        if (ev.target.status >= 200 && ev.target.status < 300) {
+        if (opts.validateStatus(ev.target.status, xhr.responseText, xhr)) {
           const body = opts.getResponseData(xhr.responseText, xhr)
           const uploadURL = body[opts.responseUrlFieldName]
 
@@ -404,7 +412,7 @@ module.exports = class XHRUpload extends Plugin {
       xhr.addEventListener('load', (ev) => {
         timer.done()
 
-        if (ev.target.status >= 200 && ev.target.status < 300) {
+        if (this.opts.validateStatus(ev.target.status, xhr.responseText, xhr)) {
           const body = this.opts.getResponseData(xhr.responseText, xhr)
           const uploadResp = {
             status: ev.target.status,

+ 42 - 0
packages/@uppy/xhr-upload/src/index.test.js

@@ -34,4 +34,46 @@ describe('XHRUpload', () => {
       })
     })
   })
+
+  describe('validateStatus', () => {
+    it('emit upload error under status code 200', () => {
+      nock('https://fake-endpoint.uppy.io')
+      .defaultReplyHeaders({
+        'access-control-allow-method': 'POST',
+        'access-control-allow-origin': '*'
+      })
+      .options('/').reply(200, {})
+      .post('/').reply(200, {
+        code: 40000,
+        message: 'custom upload error'
+      })
+
+      const core = new Core()
+      const validateStatus = jest.fn(function (status, responseText, response) {
+        return JSON.parse(responseText).code !== 40000
+      })
+
+      core.use(XHRUpload, {
+        id: 'XHRUpload',
+        endpoint: 'https://fake-endpoint.uppy.io',
+        some: 'option',
+        validateStatus,
+        getResponseError (responseText, xhr) {
+          return JSON.parse(responseText).message
+        }
+      })
+      core.addFile({
+        name: 'test.jpg',
+        data: new Blob([Buffer.alloc(8192)])
+      })
+
+      return core.upload().then(result => {
+        expect(validateStatus).toHaveBeenCalled()
+        expect(result.failed.length).toBeGreaterThan(0)
+        result.failed.forEach(file => {
+          expect(file.error).toEqual('custom upload error')
+        })
+      })
+    })
+  })
 })

+ 24 - 24
packages/uppy/package.json

@@ -1,7 +1,7 @@
 {
   "name": "uppy",
   "description": "Extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
-  "version": "0.30.3",
+  "version": "0.30.4",
   "license": "MIT",
   "main": "index.js",
   "module": "index.mjs",
@@ -30,31 +30,31 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
-    "@uppy/aws-s3": "0.30.3",
-    "@uppy/aws-s3-multipart": "0.30.3",
-    "@uppy/companion-client": "0.28.3",
-    "@uppy/core": "0.30.3",
-    "@uppy/dashboard": "0.30.3",
-    "@uppy/drag-drop": "0.30.3",
-    "@uppy/dropbox": "0.30.3",
-    "@uppy/file-input": "0.30.3",
-    "@uppy/form": "0.30.3",
-    "@uppy/golden-retriever": "0.30.3",
-    "@uppy/google-drive": "0.30.3",
-    "@uppy/informer": "0.30.3",
-    "@uppy/instagram": "0.30.3",
-    "@uppy/progress-bar": "0.30.3",
-    "@uppy/provider-views": "0.30.3",
-    "@uppy/redux-dev-tools": "0.30.3",
-    "@uppy/status-bar": "0.30.3",
+    "@uppy/aws-s3": "0.30.4",
+    "@uppy/aws-s3-multipart": "0.30.4",
+    "@uppy/companion-client": "0.28.4",
+    "@uppy/core": "0.30.4",
+    "@uppy/dashboard": "0.30.4",
+    "@uppy/drag-drop": "0.30.4",
+    "@uppy/dropbox": "0.30.4",
+    "@uppy/file-input": "0.30.4",
+    "@uppy/form": "0.30.4",
+    "@uppy/golden-retriever": "0.30.4",
+    "@uppy/google-drive": "0.30.4",
+    "@uppy/informer": "0.30.4",
+    "@uppy/instagram": "0.30.4",
+    "@uppy/progress-bar": "0.30.4",
+    "@uppy/provider-views": "0.30.4",
+    "@uppy/redux-dev-tools": "0.30.4",
+    "@uppy/status-bar": "0.30.4",
     "@uppy/store-default": "0.28.3",
     "@uppy/store-redux": "0.28.3",
-    "@uppy/thumbnail-generator": "0.30.3",
-    "@uppy/transloadit": "0.30.3",
-    "@uppy/tus": "0.30.3",
-    "@uppy/url": "0.30.3",
-    "@uppy/webcam": "0.30.3",
-    "@uppy/xhr-upload": "0.30.3"
+    "@uppy/thumbnail-generator": "0.30.4",
+    "@uppy/transloadit": "0.30.4",
+    "@uppy/tus": "0.30.4",
+    "@uppy/url": "0.30.4",
+    "@uppy/webcam": "0.30.4",
+    "@uppy/xhr-upload": "0.30.4"
   },
   "devDependencies": {
     "es6-promise": "4.2.5",

+ 46 - 25
packages/uppy/types/index.d.ts

@@ -1,40 +1,61 @@
-// Type definitions for uppy 0.25.5
+// Type definitions for uppy
 // Project: https://uppy.io
 // Definitions by: taoqf <https://github.com/taoqf>
 
 // Core
-export { default as Core } from '@uppy/core';
+import Core = require('@uppy/core');
+export { Core };
 
 // Stores
 import DefaultStore = require('@uppy/store-default');
-export { DefaultStore };  // this is weird: exporting a function as something that sounds like a class name!
-// do we really mean what the line above says?
-export { default as ReduxStore } from '@uppy/store-redux';
+export { DefaultStore };
+import ReduxStore = require('@uppy/store-redux');
+export { ReduxStore };
 
 // UI plugins
-export { default as Dashboard } from '@uppy/dashboard';
-export { default as DragDrop } from '@uppy/drag-drop';
-export { default as FileInput } from '@uppy/file-input';
-export { default as Informer } from '@uppy/informer';
-export { default as ProgressBar } from '@uppy/progress-bar';
-export { default as StatusBar } from '@uppy/status-bar';
+import Dashboard = require('@uppy/dashboard');
+export { Dashboard };
+import DragDrop = require('@uppy/drag-drop');
+export { DragDrop };
+import FileInput = require('@uppy/file-input');
+export { FileInput };
+import Informer = require('@uppy/informer');
+export { Informer };
+import ProgressBar = require('@uppy/progress-bar');
+export { ProgressBar };
+import StatusBar = require('@uppy/status-bar');
+export { StatusBar };
 
 // Acquirers
-export { default as Dropbox } from '@uppy/dropbox';
-export { default as GoogleDrive } from '@uppy/google-drive';
-export { default as Instagram } from '@uppy/instagram';
-export { default as Url } from '@uppy/url';
-export { default as Webcam } from '@uppy/webcam';
+import Dropbox = require('@uppy/dropbox');
+export { Dropbox };
+import GoogleDrive = require('@uppy/google-drive');
+export { GoogleDrive };
+import Instagram = require('@uppy/instagram');
+export { Instagram };
+import Url = require('@uppy/url');
+export { Url };
+import Webcam = require('@uppy/webcam');
+export { Webcam };
 
 // Uploaders
-export { default as AwsS3 } from '@uppy/aws-s3';
-export { default as AwsS3Multipart } from '@uppy/aws-s3-multipart';
-export { default as Transloadit } from '@uppy/transloadit';
-export { default as Tus } from '@uppy/tus';
-export { default as XHRUpload } from '@uppy/xhr-upload';
+import AwsS3 = require('@uppy/aws-s3');
+export { AwsS3 };
+import AwsS3Multipart = require('@uppy/aws-s3-multipart');
+export { AwsS3Multipart };
+import Transloadit = require('@uppy/transloadit');
+export { Transloadit };
+import Tus = require('@uppy/tus');
+export { Tus };
+import XHRUpload = require('@uppy/xhr-upload');
+export { XHRUpload };
 
 // Miscellaneous
-export { default as Form } from '@uppy/form';
-export { default as GoldenRetriever } from '@uppy/golden-retriever';
-export { default as ReduxDevTools } from '@uppy/redux-dev-tools';
-export { default as ThumbnailGenerator } from '@uppy/thumbnail-generator';
+import Form = require('@uppy/form');
+export { Form };
+import GoldenRetriever = require('@uppy/golden-retriever');
+export { GoldenRetriever };
+import ReduxDevTools = require('@uppy/redux-dev-tools');
+export { ReduxDevTools };
+import ThumbnailGenerator = require('@uppy/thumbnail-generator');
+export { ThumbnailGenerator };

BIN
rockdog.png


+ 12 - 10
test/endtoend/typescript/main.ts

@@ -1,18 +1,20 @@
 import 'es6-promise/auto'
 import 'whatwg-fetch'
-import Uppy = require('@uppy/core')
-import Dashboard = require('@uppy/dashboard')
-import Instagram = require('@uppy/instagram')
-import Dropbox = require('@uppy/dropbox')
-import GoogleDrive = require('@uppy/google-drive')
-import Url = require('@uppy/url')
-import Webcam = require('@uppy/webcam')
-import Tus = require('@uppy/tus')
-import Form = require('@uppy/form')
+import {
+  Core,
+  Dashboard,
+  Instagram,
+  Dropbox,
+  GoogleDrive,
+  Url,
+  Webcam,
+  Tus,
+  Form
+} from 'uppy'
 
 const TUS_ENDPOINT = 'https://master.tus.io/files/'
 
-const uppy = Uppy({
+const uppy = Core({
   debug: true,
   meta: {
     username: 'John',

+ 1 - 1
tsconfig.json

@@ -12,7 +12,7 @@
     "types": [],
     "noEmit": true,
     "esModuleInterop": true,
-    "allowSyntheticDefaultImports": true,
+    "allowSyntheticDefaultImports": false,
     "strictFunctionTypes": true,
     "forceConsistentCasingInFileNames": true
   },

+ 1 - 1
website/src/_posts/2019-04-liftoff-08.md

@@ -50,6 +50,6 @@ After:<br />
 
 - Ife is writing tests for the implementation of the `tus-js-client` URL storage.
 
-That's all the updates I have for you today. See you tomorrow for Day 9 of our 30 Days to Liftoff!
+That's all the updates I have for you today. See you tomorrow for [Day 9](/blog/2019/04/liftoff-09/) of our 30 Days to Liftoff!
 
 P.S. If you don't want to miss an update, you might like to subscribe to our [RSS feed](https://uppy.io/atom.xml) :)

+ 49 - 0
website/src/_posts/2019-04-liftoff-09.md

@@ -0,0 +1,49 @@
+---
+title: "Day 9"
+date: 2019-04-03
+author: tyler
+image: "https://uppy.io/images/blog/30daystoliftoff/day09.jpg"
+series: 30 Days to Liftoff
+seriesSuffix: 'of 30'
+---
+
+The entire team is starting to get into its groove as the Uppy channel in our Slack workspace is looking livelier than ever. Let's dive straight into Day 9 of our 30 Days to Liftoff!
+
+<center><img width="400" src="/images/blog/30daystoliftoff/day09.jpg"><br /><br /></center>
+
+Everyone is moving at a comfortable pace, working on the various *In Progress* cards sitting neatly on our Uppy board, so here's a quick update on the progress towards **Uppy 1.0, coming April 25**!
+
+<!--more-->
+
+## Done
+
+Tasks completed by the team since yesterday's update include:
+
+- Our React Native team (that's [Artur](https://github.com/arturi), [Ife](https://github.com/ifedapoolarewaju), and [Kevin](https://github.com/kvz)) has been working together most of the day and gave an update on the current completed features for implementing Uppy support. Here's a list of completed tasks directly from the team:
+    - [x] Make the 'Import from URL' example work in RN
+    - [x] Give it its own UI, (re)written for RN
+    - [x] Make websocket progress from Companion work (see: https://facebook.github.io/react-native/docs/network#websocket-support)
+    - [x] Have an example with buttons for resumability and a progress bar
+    - [x] Have 20 min resumability support, proof that fingerprinting works for different local file uploads, fix it if not
+
+- A [feature](https://github.com/transloadit/uppy/pull/1367) was submitted by [Andrew Shini](https://github.com/superandrew213) that involving checking for existing uploads before uploading anything, which has been necessary for his custom dashboard. Artur and [Renée](https://github.com/goto-bus-stop) will be reviewing it. A big thank you to the open source community for making contributions!
+
+## In Progress
+
+Meanwhile, on the progress side of things:
+
+- [Abdel](https://github.com/Kiloreux) has started working on a [Glitch](https://glitch.com/) demo involving Companion, so users can have an even easier time utilizing our services. He's also working on fixing up the Serverless example (running Companion on AWS Lambda) which was once contributed by the community but hasn't been workin for a long time :scream:
+
+- Renée is going to spend the day on getting Transloadit Assemblies to actually cancel when the Cancel button in the UI is pressed. As it stands, the upload cancels, but the Assembly keeps running until it eventually times out by itself.
+
+- [Tim](https://github.com/tim-kos) has started working on fixing an API problem that caused one of our end-to-end tests to intermittently fail and eventually turn off entirely. 
+
+- Finally, the React Native team has already completed an impressive amount of tasks, but they aren't slowing down anytime soon. Development marches on as the team begins tackling the following tasks:
+    - [ ] Add basic file preview to react native example. Can use `file.uri` for images, can use icons for `file.type===video/audio/document` (Artur Paikin)
+    - [ ] Release Uppy (add inline customFingerPrint & AsyncStorage for now, we'll remove customFingerPrint later, once tus-js-client is in good shape) (Artur Paikin)
+    - [ ] Test fingerprinting for Audio, Document (Artur Paikin)
+    - [ ] Add customFingerPrint & hashCode into Ife's tus-js-client branch, add tests, get it into shape for Marius to review (Ifedapo Olarewaju)
+
+Come Thursday, they'll be having another call to discuss their completed work so far and map out the remaining RN tasks from there. I'm looking forward to sharing more details on that call soon!
+
+The first third of our thirty-day challenge ends tomorrow with [Day 10](/blog/2019/04/liftoff-10/). Look forward to more updates (and pictures of dogs in space) tomorrow and, as usual, don't forget about our [RSS feed](https://uppy.io/atom.xml) :dog:

+ 40 - 0
website/src/_posts/2019-04-liftoff-10.md

@@ -0,0 +1,40 @@
+---
+title: "Day 10"
+date: 2019-04-04
+author: tyler
+image: "https://uppy.io/images/blog/30daystoliftoff/day10.jpg"
+series: 30 Days to Liftoff
+seriesSuffix: 'of 30'
+---
+
+Today marks the tenth day in our 30 Days to Liftoff! **April 25** is still in our sights as we work towards the **Uppy 1.0** release. The end of the working week is almost upon us, but there's no sign of slowing down yet.  
+
+<center><img width="400" src="/images/blog/30daystoliftoff/day10.jpg"><br /></center>
+
+Things are looking up(py) on our project board as the team has been working on tasks at a steady pace. A quick status update will give us a little insight into how things are going concerning all things Uppy.
+
+<!--more-->
+
+## In Progress
+
+- [Alex](https://github.com/nqst), [Evgenia](https://github.com/lakesare) and [Artur](https://github.com/arturi) are debugging the [flickering on Drag & Drop](https://github.com/transloadit/uppy/pull/1400) that Chrome and Safari users suffer from. Fingers crossed for a solution!
+
+- Artur, working on the React Native side of things, has impressively added [file previews and tested hashes with different file types](https://github.com/transloadit/uppy/pull/988). After some more testing is complete, he'll move on to releasing a new version of Uppy. In addition, the React Native team has reschedulded their pair programming session to Friday, so expect some pretty significant updates tomorrow!
+
+- [Kevin](https://github.com/kvz) started working on the [new documentation menu structure](https://github.com/transloadit/uppy/pull/1405) for the site and we can already show a [live example](https://5ca5233bfd8bcc00085152a8--uppy.netlify.com/docs/)of it. Take a look at the main differences below:
+
+<center><img width=400 src="/images/blog/30daystoliftoff/2019-04-04-docs.png"><br /><br /></center>
+
+## Done
+
+- If you've been keeping up with our posts, you might notice the navigator at the top of the page looks a little different than usual. That is because Alex has been on a roll and [updated the navigator](https://github.com/transloadit/uppy/pull/1403) for our blogposts.
+
+- [Ife](https://github.com/ifedapoolarewaju)'s fix for [hanging instagram uploads](https://github.com/transloadit/uppy/pull/1274) was merged into Uppy's master branch. One more bug squashed!
+
+- One small chore was done by [Renée](https://github.com/goto-bus-stop), involving [adding git to the Companion dockerfile](https://github.com/transloadit/uppy/pull/1404), which was needed to install tus-js-client and ensure Companion could be deployed successfully on our continuous integration server.
+
+While not strictly related to our Uppy 1.0 release, we have managed to reach over 6000 commits to our master branch and 99 contributors in the [Uppy repo](https://github.com/transloadit/uppy). We would like to give a huge thank you to everyone offering their support!
+
+<center><img src="/images/blog/30daystoliftoff/2019-04-04.png"><br /></center>
+ 
+On that note, Day 10 of our thirty-day challenge comes to an end. Friday looks like it will be another exciting day, so don't hesitate to keep checking up on us via [Twitter](https://twitter.com/uppy_io) or [RSS](https://uppy.io/atom.xml)!

+ 48 - 0
website/src/_posts/2019-04-liftoff-11.md

@@ -0,0 +1,48 @@
+---
+title: Day 11
+date: 2019-04-05
+author: samuel
+image: https://uppy.io/images/blog/30daystoliftoff/day11.jpg
+series: 30 Days to Liftoff
+seriesSuffix: of 30
+---
+
+And another week bites the dust! With the weekend right around the corner, we're busy getting as much as possible done before heading off for some well-deserved rest & relaxation. Here is what we've been up to!   
+
+<center><img width="400" src="/images/blog/30daystoliftoff/day11.jpg"><br /></center>
+
+<!--more-->
+
+## New
+
+- [Alex](https://github.com/nqst) is test driving Uppy('s accessibility) in many different browsers and already uncovered [many issues](https://github.com/transloadit/uppy/issues/created_by/nqst) in some naughty browsers. Hope we'll manage to fix all of them before April 25!
+
+## In Progress
+
+- [Artur](https://github.com/arturi) is working on preparing the changelog for Uppy v. 0.30.4 and releasing it as soon as possible. More news about the 0.30.4 release can be found in the _Done_ section.
+
+- Our React Native team (that's [Artur](https://github.com/arturi), [Ife](https://github.com/ifedapoolarewaju), and [Kevin](https://github.com/kvz)) have been pairing again and determined there are only a few things left to do:
+
+    - Add customFingerPrint & hashCode into Ife's tus-js-client branch, add tests, and get it into shape for [Marius](https://github.com/acconut) to review
+    - UI tweaks (close button for Import, reporting errors)
+    - File preview icon for non-image files
+
+- They also discussed a few non-React Native things:
+
+  - Large file support
+  - Companion XHR Uploads should report download progress towards client-side-reported upload progress
+  - When you upload to api2.transloadit.com with XHR, it seems the API does not block and responds with 200 before the upload is really done (MAYBE :o). This results in a bad Uppy state without a progress bar.
+
+## Done
+
+- We released a new version of Uppy. While it isn’t 1.0 just quite yet, this is another step in the right direction. Uppy `0.30.4` features fixes for `allowSyntheticImports`, Typescript typings, issues when using Companion Client for remote AWS-S3 uploads, and using websockets and console.dir in React Native.
+
+- Artur and [Renée](https://github.com/goto-bus-stop) reviewed and merged a [PR](https://github.com/transloadit/uppy/pull/1360) that allows a user to customize the status of an upload in Uppy. Thanks to [Mactavish](https://github.com/Mactaivsh) for contributing!
+
+- [Abdel](https://github.com/kiloreux) [fixed the Serverless example](https://github.com/transloadit/uppy/pull/1408) for running Companion on AWS Lambda.
+
+- [Kevin](https://github.com/kvz) finished up his work on the [new documentation menu structure](https://github.com/transloadit/uppy/pull/1405) that we talked about [yesterday](/blog/2019/04/liftoff-10/). 
+
+
+That’s it. All the updates I have for you today. Have a great [weekend](/blog/2019/04/liftoff-12/) everyone! And if you don’t want to miss a post, keep tabs on our [Twitter](https://twitter.com/uppy_io) or [RSS](https://uppy.io/atom.xml) feed.
+

+ 7 - 10
website/src/_posts/2019-04-liftoff-12.md

@@ -1,19 +1,16 @@
 ---
 title: "Day 12"
 date: 2019-04-06
-author: kvz
-published: false
-image: "https://uppy.io/images/blog/30daystoliftoff/15.jpg"
+author: aj
+image: "https://uppy.io/images/blog/30daystoliftoff/discodog.jpg"
 series: 30 Days to Liftoff
 seriesSuffix: 'of 30'
 ---
 
-Work hard, play hard! In the weekend it's time to contemplate, stand back, relax, and enjoy some GIFs, handpicked by our crew:
+Hey guys! How is your Saturday treating you so far? We are focusing today on meeting friends and having fun, and taking our minds off all Uppy-related things for just a few days. To give you an idea of what we're up to tonight, check out this groovy GIF:
 
-<center><img width="400" src="https://media.giphy.com/media/RnX4q6yYDoYCI/giphy.gif"><br/><br/></center>
-<center><img width="400" src="https://media.giphy.com/media/3orieRftQRDJLIlpQc/giphy.gif"><br/><br/></center>
-<center><img width="400" src="https://media.giphy.com/media/gUUI0yuLMnFPq/giphy.gif"><br/><br/></center>
-<center><img width="400" src="https://media.giphy.com/media/9MJgNOmdBheJrcOHui/giphy.gif"><br/><br/></center>
-<center><img width="400" src="https://media.giphy.com/media/11syU6ZZ6PsGRO/giphy.gif"><br/><br/></center>
+<!--more-->
 
-Just the thing we need to recharge those :battery::battery: to :100: so we can make a dent on Monday again. Hope you'll have a good one! :kissing_heart: 
+<center><img width="400" src="https://media.giphy.com/media/k2Da0Uzaxo9xe/giphy.gif"><br/><br/></center>
+
+We won't have much in terms of real news to share over the weekend, but we'll be back in full force on [Monday](/blog/2019/04/liftoff-14/)! In the meantime, we hope you have a great day! And see you tomorrow for another weekend update in our 30 Days to Liftoff!

+ 17 - 0
website/src/_posts/2019-04-liftoff-13.md

@@ -0,0 +1,17 @@
+---
+title: "Day 13"
+date: 2019-04-07
+author: aj
+image: "https://uppy.io/images/blog/30daystoliftoff/discodog.jpg"
+series: 30 Days to Liftoff
+seriesSuffix: 'of 30'
+---
+
+Shh, it's Sunday, Uppy is sleeping in today :sleeping: We'll be taking the day off as well, as we gear up to go full throttle again in the morning. There's still a lot to do before the **Uppy 1.0 launch on April 25**. In the meantime, why don't you join us in letting sleeping dogs lie.
+
+<!--more-->
+
+<center><img width="400"  src="https://media.giphy.com/media/26n6UOQke3xCpsbWo/giphy.gif"><br/><br/></center>
+
+We hope this drowsy puppy inspires you to have a great and relaxing remainder of your weekend. We'll have a lot more to share with you tomorrow, so see you on [Day 14](/blog/2019/04/liftoff-14/) of our 30 Days to Liftoff!
+

+ 42 - 0
website/src/_posts/2019-04-liftoff-14.md

@@ -0,0 +1,42 @@
+---
+title: "Day 14"
+date: 2019-04-08
+author: kvz
+image: "https://uppy.io/images/blog/30daystoliftoff/day14.jpg"
+series: 30 Days to Liftoff
+seriesSuffix: 'of 30'
+---
+
+Today marks the fourteenth day in our '30 Days to Liftoff' blog post challenge, working our way towards **launching Uppy 1.0 on April 25**. It's the beginning of a new week and there is much to be done. The Uppy team is already firing on all cylinders and we also have some developments to share from before the weekend.
+
+<!--more-->
+
+Let's jump right in!
+
+<center><br /><img width="400" src="/images/blog/30daystoliftoff/day14.jpg"><br /></center>
+
+## Done
+
+- [Renée](https://github.com/goto-bus-stop) finished work on [canceling Transloadit Assemblies](https://github.com/transloadit/uppy/pull/1431) when you abort uploads.
+
+- [Artur](https://github.com/arturi) published the [CHANGELOG for 0.30.4](https://github.com/transloadit/uppy/commit/845369f0e56b49ab51d4d01909dfdac6f60b1748), which was a bit more work to figure out now that the whole Transloadit team is piling commits onto `master` :scream: We also completed a few smaller tasks, such as fixing an issue with our build scripts and updating the Companion docs.
+
+## Done for React Native
+
+This is a pretty big one! [Ife](https://github.com/ifedapoolarewaju) and Artur delivered on the bare essentials for React Native. In our local tests, this means we can now successfully pick different files, have their uploads resume where they left off, select a remote file via Companion (for now, only picking from URL is supported) and see the progress reported by it. 
+
+There is a basic UI, most of which is encapsulated in the example that we'll publish on our website. We're refraining from building too much UI in our React Native module, as we assume that developers will want to have full control and style everything close to their app. We figured they would care most about seeing a good example and having access to core functionality, such as making mobile uploads more reliable and less draining on batteries and data plans. 
+
+While there are still things to be implemented, such as picking files from Instagram, we now know that we have all the Lego bricks required and that they are doing what they're supposed to. Now, it's just a matter of fleshing out those integrations, but we'll have to see whether we can get to that before 1.0. We're already happy about having a fixed idea about the API and a basic working example as our deliverables!
+
+## In Progress
+
+- [Alex](https://github.com/nqst) is working on improving the Uppy design (in the code) in his [`design-facelift` branch](https://github.com/transloadit/uppy/compare/master...nqst:design-facelift).
+
+- [Evgenia](https://github.com/lakesare) is working on improving accessibility together with Alex, who found many issues that we still need to fix in this area.
+
+- Now that the bare essentials of React Native work, Ife is making sure our local work finds a proper place in the tus-js-client (such as React-Native-compatible fingerprinting) while Artur hopes to find the time to tick off a few design goals to make for a better experience testdriving our example. Things like icons for files that are not images and a _Close_ button for the select file screen.
+
+- The whole team will again do a call this afternoon to reassess our roadmap and see where all we stand. Some new tasks were added on [Friday](/blog/2019/04/liftoff-11/), so we'll also have to see about getting those into gear.
+
+That's it for Day 14. Tomorrow, it will be Samuel's turn again to update you on our board and progress. Subscribe via [Twitter](https://twitter.com/uppy_io) or [RSS](https://uppy.io/atom.xml) and don't miss out on [Day 15](/blog/2019/04/liftoff-15/)! :dog:

+ 45 - 0
website/src/_posts/2019-04-liftoff-15.md

@@ -0,0 +1,45 @@
+---
+title: "Day 15"
+date: 2019-04-09
+author: samuel
+image: "https://uppy.io/images/blog/30daystoliftoff/rockdog.jpg"
+series: 30 Days to Liftoff
+seriesSuffix: 'of 30'
+---
+
+:musical_note: Woah, we're halfway there! Take our paw, we'll make it we swear! :notes: That's fifteen down and fifteen more to go. Great to have you along for the ride as we move ever closer towards **launching Uppy 1.0 on April 25**. 
+
+Let's see what is Uppy today!
+
+<center><br /><img width="400" src="/images/blog/30daystoliftoff/rockdog.jpg"><br /></center>
+
+<!--more-->
+
+## Weekly project board update
+
+We had our weekly call yesterday, during which we re-evaluated the Uppy 1.0 project board. We were able to move a few tasks to the _Done_ lane and got some new tasks _In Progress_. One of the features we're ready to start working on - which I am particularly interested in - is language pack support. Uppy 1.0 might be coming out in your local language! While we only have the capacity within our team to take care of a select few languages (English, French, Russian, Ukrainian, German, and Dutch) we will support as many as possible and hope that the community will add many more. 
+
+Here is a view of what our Asana board looks like this week:
+
+<center><br /><img src="/images/blog/30daystoliftoff/2019-04-09-asana-board.png"><br /></center>
+
+
+## Done
+
+- [Evgenia](https://github.com/lakesare) investigated an [issue](https://github.com/transloadit/uppy/pull/1400#issuecomment-481039845) concerning a bug in Webkit browsers that resulted in the window blinking frequently.
+
+- [Samuel](https://github.com/samuelayo) added another [example](https://github.com/transloadit/uppy/pull/1389) for using Uppy with XHR uploads to a PHP backend.
+
+
+## In Progress
+
+- [Renée](https://github.com/goto-bus-stop) is working on the Robodog Dashboard, so that it can also be depicted right inside the webpage, instead of showing as a modal popup window.
+
+- Evgenia is focused on improving accessibility together with [Alex](https://github.com/nqst), who found many issues that we still need to fix in this area.
+
+- [Artur](https://github.com/arturi) is currently working on a React Native example UI. We will update you once it is ready! 
+
+- Alex is also still actively working on making design improvements for Uppy. You can follow his progress [here](https://github.com/nqst/uppy/commits/design-facelift).
+
+
+And that's Day 15! Tyler will be here again tomorrow to give you more updates on our launch preparations. Subscribe via [Twitter](https://twitter.com/uppy_io) or [RSS](https://uppy.io/atom.xml), so you'll never miss out!

+ 19 - 0
website/src/_posts/2019-04-liftoff-31.md

@@ -0,0 +1,19 @@
+---
+title: "Day 12"
+date: 2019-04-06
+author: kvz
+published: false
+image: "https://uppy.io/images/blog/30daystoliftoff/15.jpg"
+series: 30 Days to Liftoff
+seriesSuffix: 'of 30'
+---
+
+Work hard, play hard! In the weekend it's time to contemplate, stand back, relax, and enjoy some GIFs, handpicked by our crew:
+
+<center><img width="400" src="https://media.giphy.com/media/RnX4q6yYDoYCI/giphy.gif"><br/><br/></center>
+<center><img width="400" src="https://media.giphy.com/media/3orieRftQRDJLIlpQc/giphy.gif"><br/><br/></center>
+<center><img width="400" src="https://media.giphy.com/media/gUUI0yuLMnFPq/giphy.gif"><br/><br/></center>
+<center><img width="400" src="https://media.giphy.com/media/9MJgNOmdBheJrcOHui/giphy.gif"><br/><br/></center>
+<center><img width="400" src="https://media.giphy.com/media/11syU6ZZ6PsGRO/giphy.gif"><br/><br/></center>
+
+Just the thing we need to recharge those :battery::battery: to :100: so we can make a dent on Monday again. Hope you'll have a good one! :kissing_heart: 

+ 1 - 1
website/src/_template/contributing.md

@@ -130,7 +130,7 @@ npm install && cd website && npm install && cd ..
 For local previews on http://localhost:4000, type:
 
 ```bash
-npm start
+npm run web:start # that gets you just the website. if you need companion, etc. you can use `npm start` instead
 ```
 
 This will watch the website, as well as Uppy, as the examples, and rebuild everything and refresh your browser as files change.

+ 2 - 2
website/src/_template/integration_help.md

@@ -1,9 +1,9 @@
 <!-- WARNING! This file was injected. Please edit in ".github/ISSUE_TEMPLATE/integration_help.md" instead and run "inject.js" -->
 
-Transloadit is providing Uppy free of charge. If you want you can self-host all components and never pay us a dime. There's docs and tests and your Bugs and Feature Requests are always welcome on GitHub. 
+Transloadit is providing Uppy free of charge. If you want you can self-host all components and never pay us a dime. There's docs and tests and your Bug Reports and Feature Requests are always welcome on GitHub. 
 
 There is also a different category of support that we'd like to call Integration Help: making things work for you, that are already reported working for the larger community.
 
 As much as we'd like to provide detailed integration help to each non-paying user, Uppy has reached a point where this is no longer sustainable for our small crew. We end up investing our time in a million different projects, but without money flowing back, we can't ramp up our team along with the demand, spreading our team ever thinner, eventually grinding development to a halt.
 
-This is not where we want to be, so in order to offer enthusiasts, businesses, and enterprises assistance in a sustainable way, we're providing community based integration help for free at <https://community.transloadit.com/c/uppy>, and offer business paid support via <https://uppy.io/support>.
+This is not where we want to be, so in order to offer enthusiasts, businesses, and enterprises assistance in a sustainable way, we're providing community based integration help for free at <https://community.transloadit.com/c/uppy>, and offer paid support via <https://uppy.io/support>.

File diff suppressed because it is too large
+ 0 - 0
website/src/disc.html


+ 2 - 1
website/src/docs/aws-s3-multipart.md

@@ -1,9 +1,10 @@
 ---
 type: docs
-order: 53
+order: 3
 title: "AWS S3 Multipart"
 module: "@uppy/aws-s3-multipart"
 permalink: docs/aws-s3-multipart/
+category: 'Destinations'
 ---
 
 The `@uppy/aws-s3-multipart` plugin can be used to upload files directly to an S3 bucket using S3's Multipart upload strategy. With this strategy, files are chopped up in parts of 5MB+ each, so they can be uploaded concurrently. It is also very reliable: if a single part fails to upload, only that 5MB chunk has to be retried.

+ 2 - 1
website/src/docs/aws-s3.md

@@ -1,9 +1,10 @@
 ---
 type: docs
-order: 52
+order: 2
 title: "AWS S3"
 module: "@uppy/aws-s3"
 permalink: docs/aws-s3/
+category: 'Destinations'
 ---
 
 The `@uppy/aws-s3` plugin can be used to upload files directly to an S3 bucket.

+ 3 - 2
website/src/docs/companion.md

@@ -5,6 +5,7 @@ title: "Companion"
 module: "@uppy/companion"
 permalink: docs/companion/
 alias: docs/server/
+category: 'Docs'
 ---
 
 > Companion was previously known as Uppy Server. It was renamed in v0.14.0.
@@ -263,7 +264,7 @@ The default value simply returns `filename`, so all files will be uploaded to th
 
 ### Running in Kubernetes
 
-We have [a detailed guide on running Companion in Kubernetes](https://github.com/transloadit/uppy/blob/master/packages/%40uppy/companion/KUBERNETES.md) for you, that’s how we currently run our example server at http://server.uppy.io.
+We have [a detailed guide on running Companion in Kubernetes](https://github.com/transloadit/uppy/blob/master/packages/%40uppy/companion/KUBERNETES.md) for you, that’s how we currently run our example server at https://companion.uppy.io.
 
 ### Adding custom providers
 
@@ -331,4 +332,4 @@ This would get the Companion instance running on `http://localhost:3020`. It use
 
 ## Live example
 
-An example server is running at http://companion.uppy.io, which is deployed with [Kubernetes](https://github.com/transloadit/uppy/blob/master/packages/%40uppy/companion/KUBERNETES.md)
+An example server is running at https://companion.uppy.io, which is deployed with [Kubernetes](https://github.com/transloadit/uppy/blob/master/packages/%40uppy/companion/KUBERNETES.md)

+ 1 - 0
website/src/docs/contributing.md

@@ -2,6 +2,7 @@
 title: "Contributing"
 type: docs
 order: 5
+category: 'Docs'
 ---
 
 <!-- md contributing.md -->

+ 2 - 1
website/src/docs/dashboard.md

@@ -1,9 +1,10 @@
 ---
 type: docs
-order: 30
+order: 0
 title: "Dashboard"
 module: "@uppy/dashboard"
 permalink: docs/dashboard/
+category: 'UI Elements'
 ---
 
 `@uppy/dashboard` is a universal UI plugin for Uppy, offering several useful features:

+ 2 - 1
website/src/docs/dragdrop.md

@@ -1,10 +1,11 @@
 ---
 type: docs
-order: 31
+order: 1
 title: "Drag & Drop"
 module: "@uppy/drag-drop"
 permalink: docs/drag-drop/
 alias: docs/dragdrop/
+category: 'Sources'
 ---
 
 The `@uppy/drag-drop` plugin renders a simple drag and drop area for file selection. it can be useful when you only want the local device as a file source, don’t need file previews and a UI for metadata editing, and the [Dashboard](/docs/dashboard/) feels like overkill.

+ 3 - 1
website/src/docs/dropbox.md

@@ -1,9 +1,11 @@
 ---
 type: docs
-order: 41
+order: 11
 title: "Dropbox"
+menu_prefix: "<span title='Requires Companion'>ⓒ </span>"
 module: "@uppy/dropbox"
 permalink: docs/dropbox/
+category: 'Sources'
 ---
 
 The `@uppy/dropbox` plugin lets users import files from their Dropbox account.

+ 2 - 2
website/src/docs/fileinput.md

@@ -1,11 +1,11 @@
 ---
 type: docs
-order: 32
+order: 2
 title: "File Input"
 module: "@uppy/file-input"
 permalink: docs/file-input/
 alias: docs/fileinput/
-
+category: 'Sources'
 ---
 
 `@uppy/file-input` is the most barebones UI for selecting files — it shows a single button that, when clicked, opens up the browser's file selector.

+ 2 - 1
website/src/docs/form.md

@@ -1,9 +1,10 @@
 ---
 type: docs
-order: 80
+order: 0
 title: "Form"
 module: "@uppy/form"
 permalink: docs/form/
+category: 'Miscellaneous'
 ---
 
 The `@uppy/form` plugin has several features to integrate with HTML `<form>` elements.

+ 2 - 1
website/src/docs/golden-retriever.md

@@ -3,7 +3,8 @@ title: "Golden Retriever"
 module: "@uppy/golden-retriever"
 type: docs
 permalink: docs/golden-retriever/
-order: 81
+order: 1
+category: 'Miscellaneous'
 ---
 
 The `@uppy/golden-retriever` plugin saves selected files in your browser cache, so that if the browser crashes, Uppy can restore everything and continue uploading as if nothing happened. You can read more about it [on our blog](https://uppy.io/blog/2017/07/golden-retriever/).

+ 3 - 1
website/src/docs/google-drive.md

@@ -1,9 +1,11 @@
 ---
 type: docs
-order: 42
+order: 12
 title: "Google Drive"
+menu_prefix: "<span title='Requires Companion'>ⓒ </span>"
 module: "@uppy/google-drive"
 permalink: docs/google-drive/
+category: 'Sources'
 ---
 
 The `@uppy/google-drive` plugin lets users import files from their Google Drive account.

+ 6 - 5
website/src/docs/index.md

@@ -4,6 +4,7 @@ type: docs
 permalink: docs/
 alias: api/
 order: 0
+category: 'Docs'
 ---
 
 Uppy is a sleek and modular file uploader. It fetches files from local disk, Google Drive, Instagram, remote urls, cameras etc, and then uploads them to the final destination. It’s fast, easy to use and lets you worry about more important problems than building a file uploader.
@@ -18,12 +19,12 @@ Here’s the simplest example html page with Uppy (it uses a CDN bundle, while w
   <head>
     <meta charset="utf-8">
     <title>Uppy</title>
-    <link href="https://transloadit.edgly.net/releases/uppy/v0.30.3/uppy.min.css" rel="stylesheet">
+    <link href="https://transloadit.edgly.net/releases/uppy/v0.30.4/uppy.min.css" rel="stylesheet">
   </head>
   <body>
     <div id="drag-drop-area"></div>
 
-    <script src="https://transloadit.edgly.net/releases/uppy/v0.30.3/uppy.min.js"></script>
+    <script src="https://transloadit.edgly.net/releases/uppy/v0.30.4/uppy.min.js"></script>
     <script>
       var uppy = Uppy.Core()
         .use(Uppy.Dashboard, {
@@ -112,12 +113,12 @@ You can also use a pre-built bundle from Transloadit's CDN: Edgly. `Uppy` will a
 1\. Add a script at the bottom of the closing `</body>` tag:
 
 ``` html
-<script src="https://transloadit.edgly.net/releases/uppy/v0.30.3/uppy.min.js"></script>
+<script src="https://transloadit.edgly.net/releases/uppy/v0.30.4/uppy.min.js"></script>
 ```
 
 2\. Add CSS to `<head>`:
 ``` html
-<link href="https://transloadit.edgly.net/releases/uppy/v0.30.3/uppy.min.css" rel="stylesheet">
+<link href="https://transloadit.edgly.net/releases/uppy/v0.30.4/uppy.min.css" rel="stylesheet">
 ```
 
 3\. Initialize at the bottom of the closing `</body>` tag:
@@ -168,5 +169,5 @@ If you're using Uppy via a script tag, you can load the polyfills from [JSDelivr
 ```html
 <script src="https://cdn.jsdelivr.net/npm/es6-promise@4.2.5/dist/es6-promise.auto.min.js"></script>
 <script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@3.0.0/dist/fetch.umd.min.js"></script>
-<script src="https://transloadit.edgly.net/releases/uppy/v0.30.3/uppy.min.js"></script>
+<script src="https://transloadit.edgly.net/releases/uppy/v0.30.4/uppy.min.js"></script>
 ```

+ 2 - 1
website/src/docs/informer.md

@@ -1,9 +1,10 @@
 ---
 type: docs
-order: 62
+order: 2
 title: "Informer"
 module: "@uppy/informer"
 permalink: docs/informer/
+category: 'UI Elements'
 ---
 
 The `@uppy/informer` plugin is a pop-up bar for showing notifications. When plugins have some exciting news (or error) to share, they can show a notification here.

+ 3 - 1
website/src/docs/instagram.md

@@ -1,9 +1,11 @@
 ---
 type: docs
-order: 43
+order: 13
 title: "Instagram"
+menu_prefix: "<span title='Requires Companion'>ⓒ </span>"
 module: "@uppy/instagram"
 permalink: docs/instagram/
+category: 'Sources'
 ---
 
 The `@uppy/instagram` plugin lets users import files from their Instagram account.

+ 2 - 1
website/src/docs/plugins.md

@@ -2,7 +2,8 @@
 title: "List & Common Options"
 type: docs
 permalink: docs/plugins/
-order: 20
+order: 0
+category: 'Plugins'
 ---
 
 Plugins are what makes Uppy useful: they help select, manipulate and upload files.

+ 2 - 1
website/src/docs/progressbar.md

@@ -1,10 +1,11 @@
 ---
 type: docs
-order: 61
+order: 1
 title: "Progress Bar"
 module: "@uppy/progress-bar"
 permalink: docs/progress-bar/
 alias: docs/progressbar/
+category: 'UI Elements'
 ---
 
 `@uppy/progress-bar` is a minimalist plugin that shows the current upload progress in a thin bar element, similar to the ones used by YouTube and GitHub when navigating between pages.

+ 2 - 1
website/src/docs/providers.md

@@ -2,7 +2,8 @@
 title: "Provider Plugins"
 type: docs
 permalink: docs/providers/
-order: 40
+order: 10
+category: 'Sources'
 ---
 
 The Provider plugins help you connect to your accounts with remote file providers such as [Dropbox](https://dropbox.com), [Google Drive](https://drive.google.com), [Instagram](https://instagram.com) and remote URLs (importing a file by pasting a direct link to it). Because this requires server-to-server communication, they work tightly with [Companion](https://github.com/transloadit/companion) to manage the server-to-server authorization for your account. Almost all of the communication (file download/upload) is done on the server-to-server end, so this saves you the stress and bills of data consumption on the client.

+ 2 - 1
website/src/docs/react-dashboard-modal.md

@@ -2,7 +2,8 @@
 title: "&lt;DashboardModal />"
 type: docs
 permalink: docs/react/dashboard-modal/
-order: 95
+order: 5
+category: 'React'
 ---
 
 The `<DashboardModal />` component wraps the [`@uppy/dashboard`][] plugin, allowing control over the modal `open` state using a prop.

+ 2 - 1
website/src/docs/react-dashboard.md

@@ -2,7 +2,8 @@
 title: "&lt;Dashboard />"
 type: docs
 permalink: docs/react/dashboard/
-order: 94
+order: 4
+category: 'React'
 ---
 
 The `<Dashboard />` component wraps the [`@uppy/dashboard`][] plugin. It only renders the Dashboard inline. To use the Dashboard modal (`inline: false`), use the [`<DashboardModal />`](/docs/react/dashboard-modal) component.

+ 2 - 1
website/src/docs/react-dragdrop.md

@@ -3,7 +3,8 @@ title: "&lt;DragDrop />"
 type: docs
 permalink: docs/react/drag-drop/
 alias: docs/react/dragdrop/
-order: 92
+order: 2
+category: 'React'
 ---
 
 The `<DragDrop />` component wraps the [`@uppy/drag-drop`][] plugin.

Some files were not shown because too many files changed in this diff