Преглед изворни кода

Merge remote-tracking branch 'upstream/master' into design-facelift-2

Alexander Zaytsev пре 6 година
родитељ
комит
47d951c78c
76 измењених фајлова са 364 додато и 198 уклоњено
  1. 2 2
      README.md
  2. 2 2
      examples/aws-companion/main.js
  3. 3 3
      examples/bundled/index.js
  4. 1 1
      examples/custom-provider/client/MyCustomProvider.js
  5. 2 2
      examples/custom-provider/client/main.js
  6. 4 4
      examples/dev/main.js
  7. 1 1
      examples/digitalocean-spaces/main.js
  8. 1 1
      examples/react-example/App.js
  9. 4 0
      examples/transloadit-textarea/main.js
  10. 2 2
      examples/uppy-with-companion/client/index.html
  11. 1 1
      packages/@uppy/aws-s3-multipart/README.md
  12. 3 3
      packages/@uppy/aws-s3-multipart/src/index.js
  13. 1 1
      packages/@uppy/aws-s3-multipart/types/index.d.ts
  14. 1 1
      packages/@uppy/aws-s3/README.md
  15. 2 2
      packages/@uppy/aws-s3/src/index.js
  16. 1 1
      packages/@uppy/aws-s3/types/index.d.ts
  17. 2 2
      packages/@uppy/companion-client/README.md
  18. 8 8
      packages/@uppy/companion-client/src/Provider.js
  19. 2 2
      packages/@uppy/companion-client/src/RequestClient.js
  20. 2 2
      packages/@uppy/companion-client/src/RequestClient.test.js
  21. 1 1
      packages/@uppy/companion-client/types/index.d.ts
  22. 6 2
      packages/@uppy/core/src/index.js
  23. 6 17
      packages/@uppy/core/src/index.test.js
  24. 13 7
      packages/@uppy/dashboard/src/components/FileItem.js
  25. 1 1
      packages/@uppy/dashboard/src/components/FileItemProgress.js
  26. 1 1
      packages/@uppy/dashboard/src/index.js
  27. 2 2
      packages/@uppy/dashboard/src/index.test.js
  28. 10 0
      packages/@uppy/dashboard/src/style.scss
  29. 1 1
      packages/@uppy/dropbox/src/index.js
  30. 2 2
      packages/@uppy/dropbox/types/index.d.ts
  31. 1 1
      packages/@uppy/google-drive/src/index.js
  32. 2 2
      packages/@uppy/google-drive/types/index.d.ts
  33. 1 1
      packages/@uppy/instagram/src/index.js
  34. 2 2
      packages/@uppy/instagram/types/index.d.ts
  35. 4 4
      packages/@uppy/provider-views/src/index.js
  36. 4 4
      packages/@uppy/robodog/src/addProviders.js
  37. 53 22
      packages/@uppy/transloadit/src/AssemblyOptions.test.js
  38. 11 0
      packages/@uppy/transloadit/src/AssemblyWatcher.js
  39. 22 0
      packages/@uppy/transloadit/src/Client.js
  40. 56 12
      packages/@uppy/transloadit/src/index.js
  41. 1 1
      packages/@uppy/tus/src/index.js
  42. 3 3
      packages/@uppy/url/src/index.js
  43. 1 1
      packages/@uppy/url/types/index.d.ts
  44. 11 7
      packages/@uppy/xhr-upload/src/index.js
  45. 2 2
      packages/uppy/types/uppy-tests.ts
  46. 1 1
      test/endtoend/create-react-app/src/App.js
  47. 3 3
      test/endtoend/providers/main.js
  48. 4 4
      test/endtoend/typescript/main.ts
  49. 1 1
      test/endtoend/url-plugin/main.js
  50. 1 1
      website/src/_posts/2019-04-liftoff-18.md
  51. 1 1
      website/src/_posts/2019-04-liftoff-19.md
  52. 1 1
      website/src/_posts/2019-04-liftoff-20.md
  53. 1 1
      website/src/_posts/2019-04-liftoff-21.md
  54. 41 0
      website/src/_posts/2019-04-liftoff-22.md
  55. 2 2
      website/src/docs/aws-s3-multipart.md
  56. 3 3
      website/src/docs/aws-s3.md
  57. 3 3
      website/src/docs/dropbox.md
  58. 3 3
      website/src/docs/google-drive.md
  59. 3 3
      website/src/docs/instagram.md
  60. 5 5
      website/src/docs/providers.md
  61. 2 2
      website/src/docs/robodog-picker.md
  62. 9 9
      website/src/docs/transloadit.md
  63. 2 2
      website/src/docs/url.md
  64. 4 4
      website/src/examples/dashboard/app.es6
  65. 3 3
      website/src/examples/dashboard/index.ejs
  66. 2 2
      website/src/examples/transloadit/app.es6
  67. 1 1
      website/src/examples/transloadit/index.ejs
  68. 1 1
      website/src/frontpage-code-sample.ejs
  69. BIN
      website/src/images/blog/30daystoliftoff/29.jpg
  70. 0 0
      website/src/images/blog/30daystoliftoff/day22.png
  71. BIN
      website/src/images/blog/30daystoliftoff/localetodos.png
  72. BIN
      website/src/images/blog/30daystoliftoff/sheetstranslate.png
  73. BIN
      website/src/images/blog/30daystoliftoff/webdesign2.png
  74. 3 3
      website/themes/uppy/layout/index.ejs
  75. 1 1
      website/themes/uppy/layout/partials/frontpage-code-sample.html
  76. 1 1
      website/themes/uppy/layout/post.ejs

+ 2 - 2
README.md

@@ -33,8 +33,8 @@ const Tus = require('@uppy/tus')
 
 const uppy = Uppy({ autoProceed: false })
   .use(Dashboard, { trigger: '#select-files' })
-  .use(GoogleDrive, { target: Dashboard, serverUrl: 'https://companion.uppy.io' })
-  .use(Instagram, { target: Dashboard, serverUrl: 'https://companion.uppy.io' })
+  .use(GoogleDrive, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
+  .use(Instagram, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
   .use(Webcam, { target: Dashboard })
   .use(Tus, { endpoint: 'https://master.tus.io/files/' })
   .on('complete', (result) => {

+ 2 - 2
examples/aws-companion/main.js

@@ -10,7 +10,7 @@ const uppy = Uppy({
 })
 
 uppy.use(GoogleDrive, {
-  serverUrl: 'http://localhost:3020'
+  companionUrl: 'http://localhost:3020'
 })
 uppy.use(Webcam)
 uppy.use(Dashboard, {
@@ -19,5 +19,5 @@ uppy.use(Dashboard, {
   plugins: ['GoogleDrive', 'Webcam']
 })
 uppy.use(AwsS3, {
-  serverUrl: 'http://localhost:3020'
+  companionUrl: 'http://localhost:3020'
 })

+ 3 - 3
examples/bundled/index.js

@@ -33,9 +33,9 @@ const uppy = Uppy({
     proudlyDisplayPoweredByUppy: true,
     note: '2 files, images and video only'
   })
-  .use(GoogleDrive, { target: Dashboard, serverUrl: 'http://localhost:3020' })
-  .use(Instagram, { target: Dashboard, serverUrl: 'http://localhost:3020' })
-  .use(Url, { target: Dashboard, serverUrl: 'http://localhost:3020' })
+  .use(GoogleDrive, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+  .use(Instagram, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+  .use(Url, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(Webcam, { target: Dashboard })
   .use(Tus, { endpoint: TUS_ENDPOINT })
 

+ 1 - 1
examples/custom-provider/client/MyCustomProvider.js

@@ -18,7 +18,7 @@ module.exports = class MyCustomProvider extends Plugin {
     // writing out the key explicitly for readability the key used to store
     // the provider instance must be equal to this.id.
     this[this.id] = new Provider(uppy, {
-      serverUrl: this.opts.serverUrl,
+      companionUrl: this.opts.companionUrl,
       provider: 'mycustomprovider'
     })
 

+ 2 - 2
examples/custom-provider/client/main.js

@@ -9,11 +9,11 @@ const uppy = Uppy({
 })
 
 uppy.use(GoogleDrive, {
-  serverUrl: 'http://localhost:3020'
+  companionUrl: 'http://localhost:3020'
 })
 
 uppy.use(MyCustomProvider, {
-  serverUrl: 'http://localhost:3020'
+  companionUrl: 'http://localhost:3020'
 })
 
 uppy.use(Dashboard, {

+ 4 - 4
examples/dev/main.js

@@ -31,10 +31,10 @@ const uppy = Uppy({
     proudlyDisplayPoweredByUppy: true,
     note: '2 files, images and video only'
   })
-  .use(GoogleDrive, { target: Dashboard, serverUrl: 'http://localhost:3020' })
-  .use(Instagram, { target: Dashboard, serverUrl: 'http://localhost:3020' })
-  .use(Dropbox, { target: Dashboard, serverUrl: 'http://localhost:3020' })
-  .use(Url, { target: Dashboard, serverUrl: 'http://localhost:3020' })
+  .use(GoogleDrive, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+  .use(Instagram, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+  .use(Dropbox, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+  .use(Url, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(Webcam, { target: Dashboard })
   .use(Tus, { endpoint: TUS_ENDPOINT })
   // .use(XHRUpload, { endpoint: XHR_ENDPOINT })

+ 1 - 1
examples/digitalocean-spaces/main.js

@@ -12,4 +12,4 @@ uppy.use(Dashboard, {
 })
 
 // No client side changes needed!
-uppy.use(AwsS3, { serverUrl: '/companion' })
+uppy.use(AwsS3, { companionUrl: '/companion' })

+ 1 - 1
examples/react-example/App.js

@@ -16,7 +16,7 @@ module.exports = class App extends React.Component {
 
     this.uppy = new Uppy({ id: 'uppy1', autoProceed: true, debug: true })
       .use(Tus, { endpoint: 'https://master.tus.io/files/' })
-      .use(GoogleDrive, { serverUrl: 'https://companion.uppy.io' })
+      .use(GoogleDrive, { companionUrl: 'https://companion.uppy.io' })
 
     this.uppy2 = new Uppy({ id: 'uppy2', autoProceed: false, debug: true })
       .use(Tus, { endpoint: 'https://master.tus.io/files/' })

+ 4 - 0
examples/transloadit-textarea/main.js

@@ -109,6 +109,8 @@ class MarkdownTextarea {
         template_id: TRANSLOADIT_EXAMPLE_TEMPLATE
       }
     }).then((result) => {
+      // Was cancelled
+      if (result == null) return
       this.insertAttachments(
         this.matchFilesAndThumbs(result.results)
       )
@@ -126,6 +128,8 @@ class MarkdownTextarea {
         template_id: TRANSLOADIT_EXAMPLE_TEMPLATE
       }
     }).then((result) => {
+      // Was cancelled
+      if (result == null) return
       this.insertAttachments(
         this.matchFilesAndThumbs(result.results)
       )

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

@@ -12,8 +12,8 @@
     <script>
       const uppy = Uppy.Core({debug: true, autoProceed: false})
         .use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })
-        .use(Uppy.Instagram, { target: Uppy.Dashboard, serverUrl: 'http://localhost:3020' })
-        .use(Uppy.GoogleDrive, { target: Uppy.Dashboard, serverUrl: 'http://localhost:3020' })
+        .use(Uppy.Instagram, { target: Uppy.Dashboard, companionUrl: 'http://localhost:3020' })
+        .use(Uppy.GoogleDrive, { target: Uppy.Dashboard, companionUrl: 'http://localhost:3020' })
         .use(Uppy.Tus, { endpoint: 'https://master.tus.io/files/' })
 
       uppy.on('success', (fileCount) => {

+ 1 - 1
packages/@uppy/aws-s3-multipart/README.md

@@ -18,7 +18,7 @@ const AwsS3Multipart = require('@uppy/aws-s3-multipart')
 const uppy = Uppy()
 uppy.use(AwsS3Multipart, {
   limit: 2,
-  serverUrl: 'https://companion.myapp.com/'
+  companionUrl: 'https://companion.myapp.com/'
 })
 ```
 

+ 3 - 3
packages/@uppy/aws-s3-multipart/src/index.js

@@ -86,8 +86,8 @@ module.exports = class AwsS3Multipart extends Plugin {
   }
 
   assertHost () {
-    if (!this.opts.serverUrl) {
-      throw new Error('Expected a `serverUrl` option containing a Companion address.')
+    if (!this.opts.companionUrl) {
+      throw new Error('Expected a `companionUrl` option containing a Companion address.')
     }
   }
 
@@ -278,7 +278,7 @@ module.exports = class AwsS3Multipart extends Plugin {
   connectToServerSocket (file) {
     return new Promise((resolve, reject) => {
       const token = file.serverToken
-      const host = getSocketHost(file.remote.serverUrl)
+      const host = getSocketHost(file.remote.companionUrl)
       const socket = new Socket({ target: `${host}/api/${token}` })
       this.uploaderSockets[socket] = socket
       this.uploaderEvents[file.id] = createEventTracker(this.uppy)

+ 1 - 1
packages/@uppy/aws-s3-multipart/types/index.d.ts

@@ -8,7 +8,7 @@ declare module AwsS3Multipart {
   }
 
   interface AwsS3MultipartOptions extends Uppy.PluginOptions {
-    serverUrl: string;
+    companionUrl: string;
     createMultipartUpload(file: Uppy.UppyFile): Promise<{ uploadId: string, key: string }>;
     listParts(file: Uppy.UppyFile, opts: { uploadId: string, key: string }): Promise<AwsS3Part[]>;
     prepareUploadPart(file: Uppy.UppyFile, partData: { uploadId: string, key: string, body: Blob, number: number }): Promise<{ url: string }>;

+ 1 - 1
packages/@uppy/aws-s3/README.md

@@ -19,7 +19,7 @@ const uppy = Uppy()
 uppy.use(AwsS3, {
   limit: 2,
   timeout: ms('1 minute'),
-  serverUrl: 'https://companion.myapp.com/'
+  companionUrl: 'https://companion.myapp.com/'
 })
 ```
 

+ 2 - 2
packages/@uppy/aws-s3/src/index.js

@@ -66,8 +66,8 @@ module.exports = class AwsS3 extends Plugin {
   }
 
   getUploadParameters (file) {
-    if (!this.opts.serverUrl) {
-      throw new Error('Expected a `serverUrl` option containing a Companion address.')
+    if (!this.opts.companionUrl) {
+      throw new Error('Expected a `companionUrl` option containing a Companion address.')
     }
 
     const filename = encodeURIComponent(file.meta.name)

+ 1 - 1
packages/@uppy/aws-s3/types/index.d.ts

@@ -9,7 +9,7 @@ declare module AwsS3 {
   }
 
   interface AwsS3Options extends Uppy.PluginOptions {
-    serverUrl: string;
+    companionUrl: string;
     getUploadParameters(file: Uppy.UppyFile): Promise<AwsS3UploadParameters>;
     timeout: number;
     limit: number;

+ 2 - 2
packages/@uppy/companion-client/README.md

@@ -17,11 +17,11 @@ const { Provider, RequestClient, Socket } = require('@uppy/companion-client')
 
 const uppy = Uppy()
 
-const client = new RequestClient(uppy, { serverUrl: 'https://uppy.mywebsite.com/' })
+const client = new RequestClient(uppy, { companionUrl: 'https://uppy.mywebsite.com/' })
 client.get('/drive/list').then(() => {})
 
 const provider = new Provider(uppy, {
-  serverUrl: 'https://uppy.mywebsite.com/',
+  companionUrl: 'https://uppy.mywebsite.com/',
   provider: providerPluginInstance
 })
 provider.checkAuth().then(() => {})

+ 8 - 8
packages/@uppy/companion-client/src/Provider.js

@@ -74,19 +74,19 @@ module.exports = class Provider extends RequestClient {
       plugin.opts = Object.assign({}, defaultOpts, opts)
     }
 
-    if (opts.serverPattern) {
-      const pattern = opts.serverPattern
-      // validate serverPattern param
+    if (opts.companionAllowedHosts) {
+      const pattern = opts.companionAllowedHosts
+      // validate companionAllowedHosts param
       if (typeof pattern !== 'string' && !Array.isArray(pattern) && !(pattern instanceof RegExp)) {
-        throw new TypeError(`${plugin.id}: the option "serverPattern" must be one of string, Array, RegExp`)
+        throw new TypeError(`${plugin.id}: the option "companionAllowedHosts" must be one of string, Array, RegExp`)
       }
-      plugin.opts.serverPattern = pattern
+      plugin.opts.companionAllowedHosts = pattern
     } else {
       // does not start with https://
-      if (/^(?!https?:\/\/).*$/i.test(opts.serverUrl)) {
-        plugin.opts.serverPattern = `https://${opts.serverUrl.replace(/^\/\//, '')}`
+      if (/^(?!https?:\/\/).*$/i.test(opts.companionUrl)) {
+        plugin.opts.companionAllowedHosts = `https://${opts.companionUrl.replace(/^\/\//, '')}`
       } else {
-        plugin.opts.serverPattern = opts.serverUrl
+        plugin.opts.companionAllowedHosts = opts.companionUrl
       }
     }
 

+ 2 - 2
packages/@uppy/companion-client/src/RequestClient.js

@@ -16,7 +16,7 @@ module.exports = class RequestClient {
 
   get hostname () {
     const { companion } = this.uppy.getState()
-    const host = this.opts.serverUrl
+    const host = this.opts.companionUrl
     return stripSlash(companion && companion[host] ? companion[host] : host)
   }
 
@@ -44,7 +44,7 @@ module.exports = class RequestClient {
   onReceiveResponse (response) {
     const state = this.uppy.getState()
     const companion = state.companion || {}
-    const host = this.opts.serverUrl
+    const host = this.opts.companionUrl
     const headers = response.headers
     // Store the self-identified domain name for the Companion instance we just hit.
     if (headers.has('i-am') && headers.get('i-am') !== companion[host]) {

+ 2 - 2
packages/@uppy/companion-client/src/RequestClient.test.js

@@ -3,8 +3,8 @@ const RequestClient = require('./RequestClient')
 describe('RequestClient', () => {
   it('has a hostname without trailing slash', () => {
     const mockCore = { getState: () => ({}) }
-    const a = new RequestClient(mockCore, { serverUrl: 'http://companion.uppy.io' })
-    const b = new RequestClient(mockCore, { serverUrl: 'http://companion.uppy.io/' })
+    const a = new RequestClient(mockCore, { companionUrl: 'http://companion.uppy.io' })
+    const b = new RequestClient(mockCore, { companionUrl: 'http://companion.uppy.io/' })
 
     expect(a.hostname).toBe('http://companion.uppy.io')
     expect(b.hostname).toBe('http://companion.uppy.io')

+ 1 - 1
packages/@uppy/companion-client/types/index.d.ts

@@ -1,7 +1,7 @@
 import Uppy = require('@uppy/core');
 
 export interface RequestClientOptions {
-  serverUrl: string;
+  companionUrl: string;
   serverHeaders?: object;
 }
 

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

@@ -123,6 +123,7 @@ class Uppy {
       allowNewUpload: true,
       capabilities: {
         uploadProgress: supportsUploadProgress(),
+        individualCancellation: true,
         resumableUploads: false
       },
       totalProgress: 0,
@@ -1188,7 +1189,6 @@ class Uppy {
       const { currentUploads } = this.getState()
       const currentUpload = currentUploads[uploadID]
       if (!currentUpload) {
-        this.log(`Not setting result for an upload that has been removed: ${uploadID}`)
         return
       }
 
@@ -1204,7 +1204,6 @@ class Uppy {
       // to an outdated object without the `.result` property.
       const { currentUploads } = this.getState()
       if (!currentUploads[uploadID]) {
-        this.log(`Not setting result for an upload that has been canceled: ${uploadID}`)
         return
       }
       const currentUpload = currentUploads[uploadID]
@@ -1213,6 +1212,11 @@ class Uppy {
 
       this._removeUpload(uploadID)
 
+      return result
+    }).then((result) => {
+      if (result == null) {
+        this.log(`Not setting result for an upload that has been removed: ${uploadID}`)
+      }
       return result
     })
   }

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

@@ -150,7 +150,7 @@ describe('src/Core', () => {
 
       const newState = {
         bee: 'boo',
-        capabilities: { uploadProgress: true, resumableUploads: false },
+        capabilities: { individualCancellation: true, uploadProgress: true, resumableUploads: false },
         files: {},
         currentUploads: {},
         allowNewUpload: true,
@@ -174,7 +174,7 @@ describe('src/Core', () => {
       // current state
       expect(stateUpdateEventMock.mock.calls[1][0]).toEqual({
         bee: 'boo',
-        capabilities: { uploadProgress: true, resumableUploads: false },
+        capabilities: { individualCancellation: true, uploadProgress: true, resumableUploads: false },
         files: {},
         currentUploads: {},
         allowNewUpload: true,
@@ -187,7 +187,7 @@ describe('src/Core', () => {
       // new state
       expect(stateUpdateEventMock.mock.calls[1][1]).toEqual({
         bee: 'boo',
-        capabilities: { uploadProgress: true, resumableUploads: false },
+        capabilities: { individualCancellation: true, uploadProgress: true, resumableUploads: false },
         files: {},
         currentUploads: {},
         allowNewUpload: true,
@@ -204,17 +204,7 @@ describe('src/Core', () => {
 
       core.setState({ foo: 'bar' })
 
-      expect(core.getState()).toEqual({
-        capabilities: { uploadProgress: true, resumableUploads: false },
-        files: {},
-        currentUploads: {},
-        allowNewUpload: true,
-        foo: 'bar',
-        info: { isHidden: true, message: '', type: 'info' },
-        meta: {},
-        plugins: {},
-        totalProgress: 0
-      })
+      expect(core.getState()).toMatchObject({ foo: 'bar' })
     })
   })
 
@@ -229,11 +219,10 @@ describe('src/Core', () => {
 
     core.reset()
 
-    // expect(corePauseEventMock.mock.calls.length).toEqual(1)
     expect(coreCancelEventMock.mock.calls.length).toEqual(1)
     expect(coreStateUpdateEventMock.mock.calls.length).toEqual(2)
     expect(coreStateUpdateEventMock.mock.calls[1][1]).toEqual({
-      capabilities: { uploadProgress: true, resumableUploads: false },
+      capabilities: { individualCancellation: true, uploadProgress: true, resumableUploads: false },
       files: {},
       currentUploads: {},
       allowNewUpload: true,
@@ -292,7 +281,7 @@ describe('src/Core', () => {
     expect(coreCancelEventMock.mock.calls.length).toEqual(1)
     expect(coreStateUpdateEventMock.mock.calls.length).toEqual(1)
     expect(coreStateUpdateEventMock.mock.calls[0][1]).toEqual({
-      capabilities: { uploadProgress: true, resumableUploads: false },
+      capabilities: { individualCancellation: true, uploadProgress: true, resumableUploads: false },
       files: {},
       currentUploads: {},
       allowNewUpload: true,

+ 13 - 7
packages/@uppy/dashboard/src/components/FileItem.js

@@ -15,14 +15,13 @@ function FileItemProgressWrapper (props) {
   }
 
   if (props.isUploaded ||
-      props.bundled ||
       (props.hidePauseResumeCancelButtons && !props.error)) {
     return <div class="uppy-DashboardItem-progressIndicator">
       <FileItemProgress
         progress={props.file.progress.percentage}
         fileID={props.file.id}
         hidePauseResumeCancelButtons={props.hidePauseResumeCancelButtons}
-        bundled={props.bundled}
+        individualCancellation={props.individualCancellation}
       />
     </div>
   }
@@ -38,13 +37,14 @@ function FileItemProgressWrapper (props) {
       : <FileItemProgress
         progress={props.file.progress.percentage}
         fileID={props.file.id}
+        individualCancellation={props.individualCancellation}
         hidePauseResumeCancelButtons={props.hidePauseResumeCancelButtons}
       />
     }
   </button>
 }
 
-module.exports = function fileItem (props) {
+module.exports = function FileItem (props) {
   const file = props.file
   const acquirers = props.acquirers
 
@@ -72,7 +72,7 @@ module.exports = function fileItem (props) {
 
     if (props.resumableUploads) {
       props.pauseUpload(file.id)
-    } else {
+    } else if (props.individualCancellation) {
       props.cancelUpload(file.id)
     }
   }
@@ -91,9 +91,11 @@ module.exports = function fileItem (props) {
         return props.i18n('resumeUpload')
       }
       return props.i18n('pauseUpload')
-    } else {
+    } else if (props.individualCancellation) {
       return props.i18n('cancelUpload')
     }
+
+    return ''
   }
 
   const dashboardItemClass = classNames(
@@ -104,9 +106,13 @@ module.exports = function fileItem (props) {
     { 'is-paused': isPaused },
     { 'is-error': error },
     { 'is-resumable': props.resumableUploads },
-    { 'is-bundled': props.bundledUpload }
+    { 'is-noIndividualCancellation': !props.individualCancellation }
   )
 
+  const showRemoveButton = props.individualCancellation
+    ? !isUploaded
+    : !uploadInProgress && !isUploaded
+
   return <li class={dashboardItemClass} id={`uppy_${file.id}`} title={file.meta.name}>
     <div class="uppy-DashboardItem-preview">
       <div class="uppy-DashboardItem-previewInnerWrap" style={{ backgroundColor: getFileTypeIcon(file.type).color }}>
@@ -174,7 +180,7 @@ module.exports = function fileItem (props) {
       </div>
     </div>
     <div class="uppy-DashboardItem-action">
-      {!isUploaded &&
+      {showRemoveButton &&
         <button class="uppy-DashboardItem-remove"
           type="button"
           aria-label={props.i18n('removeFile')}

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

@@ -19,7 +19,7 @@ module.exports = (props) => {
           stroke-dashoffset={circleLength - (circleLength / 100 * props.progress)}
         />
       </g>
-      {!props.hidePauseResumeCancelButtons && !props.bundled ? (
+      {!props.hidePauseResumeCancelButtons ? (
         <g>
           <polygon class="play" transform="translate(3, 3)" points="12 20 12 10 20 15" />
           <g class="pause" transform="translate(14.5, 13)">

+ 1 - 1
packages/@uppy/dashboard/src/index.js

@@ -729,7 +729,7 @@ module.exports = class Dashboard extends Plugin {
       note: this.opts.note,
       metaFields: pluginState.metaFields,
       resumableUploads: capabilities.resumableUploads || false,
-      bundled: capabilities.bundled || false,
+      individualCancellation: capabilities.individualCancellation,
       startUpload,
       pauseUpload: this.uppy.pauseResume,
       retryUpload: this.uppy.retryUpload,

+ 2 - 2
packages/@uppy/dashboard/src/index.test.js

@@ -35,7 +35,7 @@ describe('Dashboard', () => {
         inline: true,
         target: 'body'
       })
-      core.use(GoogleDrivePlugin, { target: DashboardPlugin, serverUrl: 'https://fake.uppy.io/' })
+      core.use(GoogleDrivePlugin, { target: DashboardPlugin, companionUrl: 'https://fake.uppy.io/' })
     }).not.toThrow()
 
     core.close()
@@ -43,7 +43,7 @@ describe('Dashboard', () => {
 
   it('works when passing plugins in `plugins` array', () => {
     const core = new Core()
-    core.use(GoogleDrivePlugin, { serverUrl: 'https://fake.uppy.io/' })
+    core.use(GoogleDrivePlugin, { companionUrl: 'https://fake.uppy.io/' })
 
     expect(() => {
       core.use(DashboardPlugin, {

+ 10 - 0
packages/@uppy/dashboard/src/style.scss

@@ -1073,6 +1073,16 @@ a.uppy-Dashboard-poweredBy {
   }
 }
 
+.uppy-DashboardItem.is-noIndividualCancellation {
+  .uppy-DashboardItem-progressIndicator {
+    cursor: default;
+  }
+
+  .cancel {
+    display: none;
+  }
+}
+
 .uppy-DashboardItem.is-processing .uppy-DashboardItem-progress {
   opacity: 0;
 }

+ 1 - 1
packages/@uppy/dropbox/src/index.js

@@ -16,7 +16,7 @@ module.exports = class Dropbox extends Plugin {
     )
 
     this.provider = new Provider(uppy, {
-      serverUrl: this.opts.serverUrl,
+      companionUrl: this.opts.companionUrl,
       serverHeaders: this.opts.serverHeaders,
       storage: this.opts.storage,
       provider: 'dropbox',

+ 2 - 2
packages/@uppy/dropbox/types/index.d.ts

@@ -3,8 +3,8 @@ import CompanionClient = require('@uppy/companion-client');
 
 declare module Dropbox {
   interface DropboxOptions extends Uppy.PluginOptions, CompanionClient.ProviderOptions {
-    serverUrl: string;
-    serverPattern: string | RegExp | Array<string | RegExp>;
+    companionUrl: string;
+    companionAllowedHosts: string | RegExp | Array<string | RegExp>;
   }
 }
 

+ 1 - 1
packages/@uppy/google-drive/src/index.js

@@ -21,7 +21,7 @@ module.exports = class GoogleDrive extends Plugin {
     )
 
     this.provider = new Provider(uppy, {
-      serverUrl: this.opts.serverUrl,
+      companionUrl: this.opts.companionUrl,
       serverHeaders: this.opts.serverHeaders,
       storage: this.opts.storage,
       provider: 'drive',

+ 2 - 2
packages/@uppy/google-drive/types/index.d.ts

@@ -3,8 +3,8 @@ import CompanionClient = require('@uppy/companion-client');
 
 declare module GoogleDrive {
   interface GoogleDriveOptions extends Uppy.PluginOptions, CompanionClient.ProviderOptions {
-    serverUrl: string;
-    serverPattern: string | RegExp | Array<string | RegExp>;
+    companionUrl: string;
+    companionAllowedHosts: string | RegExp | Array<string | RegExp>;
   }
 }
 

+ 1 - 1
packages/@uppy/instagram/src/index.js

@@ -18,7 +18,7 @@ module.exports = class Instagram extends Plugin {
     )
 
     this.provider = new Provider(uppy, {
-      serverUrl: this.opts.serverUrl,
+      companionUrl: this.opts.companionUrl,
       serverHeaders: this.opts.serverHeaders,
       storage: this.opts.storage,
       provider: 'instagram',

+ 2 - 2
packages/@uppy/instagram/types/index.d.ts

@@ -3,8 +3,8 @@ import CompanionClient = require('@uppy/companion-client');
 
 declare module Instagram {
   interface InstagramOptions extends Uppy.PluginOptions, CompanionClient.ProviderOptions {
-    serverUrl: string;
-    serverPattern: string | RegExp | Array<string | RegExp>;
+    companionUrl: string;
+    companionAllowedHosts: string | RegExp | Array<string | RegExp>;
   }
 }
 

+ 4 - 4
packages/@uppy/provider-views/src/index.js

@@ -153,7 +153,7 @@ module.exports = class ProviderView {
         fileId: file.id
       },
       remote: {
-        serverUrl: this.plugin.opts.serverUrl,
+        companionUrl: this.plugin.opts.companionUrl,
         url: `${this.provider.fileUrl(file.requestPath)}`,
         body: {
           fileId: file.id
@@ -218,7 +218,7 @@ module.exports = class ProviderView {
 
   filterItems (items) {
     const state = this.plugin.getPluginState()
-    if (state.filterInput === '') {
+    if (!state.filterInput || state.filterInput === '') {
       return items
     }
     return items.filter((folder) => {
@@ -423,8 +423,8 @@ module.exports = class ProviderView {
 
     const authWindow = window.open(link, '_blank')
     const handleToken = (e) => {
-      if (!this._isOriginAllowed(e.origin, this.plugin.opts.serverPattern) || e.source !== authWindow) {
-        this.plugin.uppy.log(`rejecting event from ${e.origin} vs allowed pattern ${this.plugin.opts.serverPattern}`)
+      if (!this._isOriginAllowed(e.origin, this.plugin.opts.companionAllowedHosts) || e.source !== authWindow) {
+        this.plugin.uppy.log(`rejecting event from ${e.origin} vs allowed pattern ${this.plugin.opts.companionAllowedHosts}`)
         return
       }
       authWindow.close()

+ 4 - 4
packages/@uppy/robodog/src/addProviders.js

@@ -12,8 +12,8 @@ const localProviders = {
 }
 
 const remoteProviderOptionNames = [
-  'serverUrl',
-  'serverPattern',
+  'companionUrl',
+  'companionAllowedHosts',
   'serverHeaders',
   'target'
 ]
@@ -27,8 +27,8 @@ function addRemoteProvider (uppy, name, opts) {
   const Provider = remoteProviders[name]
   const providerOptions = {
     // Default to the :tl: Companion servers.
-    serverUrl: Transloadit.COMPANION,
-    serverPattern: Transloadit.COMPANION_PATTERN
+    companionUrl: Transloadit.COMPANION,
+    companionAllowedHosts: Transloadit.COMPANION_PATTERN
   }
 
   remoteProviderOptionNames.forEach((name) => {

+ 53 - 22
packages/@uppy/transloadit/src/AssemblyOptions.test.js

@@ -1,7 +1,7 @@
 const AssemblyOptions = require('./AssemblyOptions')
 
 describe('Transloadit/AssemblyOptions', () => {
-  it('Validates response from getAssemblyOptions()', () => {
+  it('Validates response from getAssemblyOptions()', async () => {
     const options = new AssemblyOptions([
       { name: 'testfile' }
     ], {
@@ -13,12 +13,12 @@ describe('Transloadit/AssemblyOptions', () => {
       }
     })
 
-    return expect(options.build()).rejects.toThrow(
+    await expect(options.build()).rejects.toThrow(
       /The `params\.auth\.key` option is required/
     )
   })
 
-  it('Uses different assemblies for different params', () => {
+  it('Uses different assemblies for different params', async () => {
     const data = Buffer.alloc(10)
     data.size = data.byteLength
 
@@ -38,16 +38,15 @@ describe('Transloadit/AssemblyOptions', () => {
       })
     })
 
-    return options.build().then((assemblies) => {
-      expect(assemblies).toHaveLength(4)
-      expect(assemblies[0].options.params.steps.fake_step.data).toBe('a.png')
-      expect(assemblies[1].options.params.steps.fake_step.data).toBe('b.png')
-      expect(assemblies[2].options.params.steps.fake_step.data).toBe('c.png')
-      expect(assemblies[3].options.params.steps.fake_step.data).toBe('d.png')
-    })
+    const assemblies = await options.build()
+    expect(assemblies).toHaveLength(4)
+    expect(assemblies[0].options.params.steps.fake_step.data).toBe('a.png')
+    expect(assemblies[1].options.params.steps.fake_step.data).toBe('b.png')
+    expect(assemblies[2].options.params.steps.fake_step.data).toBe('c.png')
+    expect(assemblies[3].options.params.steps.fake_step.data).toBe('d.png')
   })
 
-  it('Should merge files with same parameters into one Assembly', () => {
+  it('Should merge files with same parameters into one Assembly', async () => {
     const data = Buffer.alloc(10)
     const data2 = Buffer.alloc(20)
 
@@ -67,26 +66,25 @@ describe('Transloadit/AssemblyOptions', () => {
       })
     })
 
-    return options.build().then((assemblies) => {
-      expect(assemblies).toHaveLength(2)
-      expect(assemblies[0].fileIDs).toHaveLength(3)
-      expect(assemblies[1].fileIDs).toHaveLength(1)
-      expect(assemblies[0].options.params.steps.fake_step.data).toBe(10)
-      expect(assemblies[1].options.params.steps.fake_step.data).toBe(20)
-    })
+    const assemblies = await options.build()
+    expect(assemblies).toHaveLength(2)
+    expect(assemblies[0].fileIDs).toHaveLength(3)
+    expect(assemblies[1].fileIDs).toHaveLength(1)
+    expect(assemblies[0].options.params.steps.fake_step.data).toBe(10)
+    expect(assemblies[1].options.params.steps.fake_step.data).toBe(20)
   })
 
-  it('Does not create an Assembly if no files are being uploaded', () => {
+  it('Does not create an Assembly if no files are being uploaded', async () => {
     const options = new AssemblyOptions([], {
       getAssemblyOptions () {
         throw new Error('should not create Assembly')
       }
     })
 
-    return expect(options.build()).resolves.toEqual([])
+    await expect(options.build()).resolves.toEqual([])
   })
 
-  it('Creates an Assembly if no files are being uploaded but `alwaysRunAssembly` is enabled', () => {
+  it('Creates an Assembly if no files are being uploaded but `alwaysRunAssembly` is enabled', async () => {
     const options = new AssemblyOptions([], {
       alwaysRunAssembly: true,
       getAssemblyOptions (file) {
@@ -100,6 +98,39 @@ describe('Transloadit/AssemblyOptions', () => {
       }
     })
 
-    return expect(options.build()).resolves.toHaveLength(1)
+    await expect(options.build()).resolves.toHaveLength(1)
+  })
+
+  it('Collects metadata if `fields` is an array', async () => {
+    function defaultGetAssemblyOptions (file, options) {
+      return {
+        params: options.params,
+        signature: options.signature,
+        fields: options.fields
+      }
+    }
+
+    const options = new AssemblyOptions([{
+      id: 1,
+      meta: { watermark: 'Some text' }
+    }, {
+      id: 2,
+      meta: { watermark: 'ⓒ Transloadit GmbH' }
+    }], {
+      fields: ['watermark'],
+      params: {
+        auth: { key: 'fake key' }
+      },
+      getAssemblyOptions: defaultGetAssemblyOptions
+    })
+
+    const assemblies = await options.build()
+    expect(assemblies).toHaveLength(2)
+    expect(assemblies[0].options.fields).toMatchObject({
+      watermark: 'Some text'
+    })
+    expect(assemblies[1].options.fields).toMatchObject({
+      watermark: 'ⓒ Transloadit GmbH'
+    })
   })
 })

+ 11 - 0
packages/@uppy/transloadit/src/AssemblyWatcher.js

@@ -22,6 +22,7 @@ class TransloaditAssemblyWatcher extends Emitter {
     })
 
     this._onAssemblyComplete = this._onAssemblyComplete.bind(this)
+    this._onAssemblyCancel = this._onAssemblyCancel.bind(this)
     this._onAssemblyError = this._onAssemblyError.bind(this)
     this._onImportError = this._onImportError.bind(this)
 
@@ -47,6 +48,14 @@ class TransloaditAssemblyWatcher extends Emitter {
     this._checkAllComplete()
   }
 
+  _onAssemblyCancel (assembly) {
+    if (!this._watching(assembly.assembly_id)) {
+      return
+    }
+
+    this._checkAllComplete()
+  }
+
   _onAssemblyError (assembly, error) {
     if (!this._watching(assembly.assembly_id)) {
       return
@@ -84,12 +93,14 @@ class TransloaditAssemblyWatcher extends Emitter {
 
   _removeListeners () {
     this._uppy.off('transloadit:complete', this._onAssemblyComplete)
+    this._uppy.off('transloadit:assembly-cancel', this._onAssemblyCancel)
     this._uppy.off('transloadit:assembly-error', this._onAssemblyError)
     this._uppy.off('transloadit:import-error', this._onImportError)
   }
 
   _addListeners () {
     this._uppy.on('transloadit:complete', this._onAssemblyComplete)
+    this._uppy.on('transloadit:assembly-cancel', this._onAssemblyCancel)
     this._uppy.on('transloadit:assembly-error', this._onAssemblyError)
     this._uppy.on('transloadit:import-error', this._onImportError)
   }

+ 22 - 0
packages/@uppy/transloadit/src/Client.js

@@ -46,12 +46,24 @@ module.exports = class Client {
     })
   }
 
+  /**
+   * Reserve resources for a file in an Assembly. Then addFile can be used later.
+   *
+   * @param {object} assembly
+   * @param {UppyFile} file
+   */
   reserveFile (assembly, file) {
     const size = encodeURIComponent(file.size)
     return fetch(`${assembly.assembly_ssl_url}/reserve_file?size=${size}`, { method: 'post' })
       .then((response) => response.json())
   }
 
+  /**
+   * Import a remote file to an Assembly.
+   *
+   * @param {object} assembly
+   * @param {UppyFile} file
+   */
   addFile (assembly, file) {
     if (!file.uploadURL) {
       return Promise.reject(new Error('File does not have an `uploadURL`.'))
@@ -66,6 +78,16 @@ module.exports = class Client {
       .then((response) => response.json())
   }
 
+  /**
+   * Cancel a running Assembly.
+   *
+   * @param {object} assembly
+   */
+  cancelAssembly (assembly) {
+    return fetch(assembly.assembly_ssl_url, { method: 'delete' })
+      .then((response) => response.json())
+  }
+
   /**
    * Get the current status for an assembly.
    *

+ 56 - 12
packages/@uppy/transloadit/src/index.js

@@ -64,7 +64,8 @@ module.exports = class Transloadit extends Plugin {
 
     this._prepareUpload = this._prepareUpload.bind(this)
     this._afterUpload = this._afterUpload.bind(this)
-    this._handleError = this._handleError.bind(this)
+    this._onError = this._onError.bind(this)
+    this._onCancelAll = this._onCancelAll.bind(this)
     this._onFileUploadURLAvailable = this._onFileUploadURLAvailable.bind(this)
     this._onRestored = this._onRestored.bind(this)
     this._getPersistentData = this._getPersistentData.bind(this)
@@ -113,10 +114,10 @@ module.exports = class Transloadit extends Plugin {
     // We only replace the hostname for Transloadit's companions, so that
     // people can also self-host them while still using Transloadit for encoding.
     let remote = file.remote
-    if (file.remote && TL_UPPY_SERVER.test(file.remote.serverUrl)) {
+    if (file.remote && TL_UPPY_SERVER.test(file.remote.companionUrl)) {
       const err = new Error(
         'The https://api2.transloadit.com/uppy-server endpoint was renamed to ' +
-        'https://api2.transloadit.com/companion, please update your `serverUrl` ' +
+        'https://api2.transloadit.com/companion, please update your `companionUrl` ' +
         'options accordingly.')
       // Explicitly log this error here because it is caught by the `createAssembly`
       // Promise further along.
@@ -126,16 +127,16 @@ module.exports = class Transloadit extends Plugin {
       throw err
     }
 
-    if (file.remote && TL_COMPANION.test(file.remote.serverUrl)) {
+    if (file.remote && TL_COMPANION.test(file.remote.companionUrl)) {
       const newHost = status.companion_url
         .replace(/\/$/, '')
       const path = file.remote.url
-        .replace(file.remote.serverUrl, '')
+        .replace(file.remote.companionUrl, '')
         .replace(/^\//, '')
 
       remote = {
         ...file.remote,
-        serverUrl: newHost,
+        companionUrl: newHost,
         url: `${newHost}/${path}`
       }
     }
@@ -329,6 +330,29 @@ module.exports = class Transloadit extends Plugin {
     })
   }
 
+  _cancelAssembly (assembly) {
+    return this.client.cancelAssembly(assembly).then(() => {
+      // TODO bubble this through AssemblyWatcher so its event handlers can clean up correctly
+      this.uppy.emit('transloadit:assembly-cancelled', assembly)
+    })
+  }
+
+  /**
+   * When all files are removed, cancel in-progress Assemblies.
+   */
+  _onCancelAll () {
+    const { assemblies } = this.getPluginState()
+
+    const cancelPromises = Object.keys(assemblies).map((assemblyID) => {
+      const assembly = this.getAssembly(assemblyID)
+      return this._cancelAssembly(assembly)
+    })
+
+    Promise.all(cancelPromises).catch((err) => {
+      this.uppy.log(err)
+    })
+  }
+
   /**
    * Custom state serialization for the Golden Retriever plugin.
    * It will pass this back to the `_onRestored` function.
@@ -632,8 +656,8 @@ module.exports = class Transloadit extends Plugin {
     })
   }
 
-  _handleError (err, uploadID) {
-    this.uppy.log(`[Transloadit] _handleError in upload ${uploadID}`)
+  _onError (err, uploadID) {
+    this.uppy.log(`[Transloadit] _onError in upload ${uploadID}`)
     this.uppy.log(err)
     const state = this.getPluginState()
     const assemblyIDs = state.uploadsAssemblies[uploadID]
@@ -650,7 +674,10 @@ module.exports = class Transloadit extends Plugin {
     this.uppy.addPostProcessor(this._afterUpload)
 
     // We may need to close socket.io connections on error.
-    this.uppy.on('error', this._handleError)
+    this.uppy.on('error', this._onError)
+
+    // Handle cancellation.
+    this.uppy.on('cancel-all', this._onCancelAll)
 
     if (this.opts.importFromUploadURLs) {
       // No uploader needed when importing; instead we take the upload URL from an existing uploader.
@@ -681,21 +708,38 @@ module.exports = class Transloadit extends Plugin {
       // Contains result data from Transloadit.
       results: []
     })
+
+    // We cannot cancel individual files because Assemblies tend to contain many files.
+    const { capabilities } = this.uppy.getState()
+    this.uppy.setState({
+      capabilities: {
+        ...capabilities,
+        individualCancellation: false
+      }
+    })
   }
 
   uninstall () {
     this.uppy.removePreProcessor(this._prepareUpload)
     this.uppy.removePostProcessor(this._afterUpload)
-    this.uppy.off('error', this._handleError)
+    this.uppy.off('error', this._onError)
 
     if (this.opts.importFromUploadURLs) {
       this.uppy.off('upload-success', this._onFileUploadURLAvailable)
     }
+
+    const { capabilities } = this.uppy.getState()
+    this.uppy.setState({
+      capabilities: {
+        ...capabilities,
+        individualCancellation: true
+      }
+    })
   }
 
   getAssembly (id) {
-    const state = this.getPluginState()
-    return state.assemblies[id]
+    const { assemblies } = this.getPluginState()
+    return assemblies[id]
   }
 
   getAssemblyFiles (assemblyID) {

+ 1 - 1
packages/@uppy/tus/src/index.js

@@ -271,7 +271,7 @@ module.exports = class Tus extends Plugin {
   connectToServerSocket (file) {
     return new Promise((resolve, reject) => {
       const token = file.serverToken
-      const host = getSocketHost(file.remote.serverUrl)
+      const host = getSocketHost(file.remote.companionUrl)
       const socket = new Socket({ target: `${host}/api/${token}` })
       this.uploaderSockets[file.id] = socket
       this.uploaderEvents[file.id] = createEventTracker(this.uppy)

+ 3 - 3
packages/@uppy/url/src/index.js

@@ -40,7 +40,7 @@ module.exports = class Url extends Plugin {
     this.i18n = this.translator.translate.bind(this.translator)
     this.i18nArray = this.translator.translateArray.bind(this.translator)
 
-    this.hostname = this.opts.serverUrl
+    this.hostname = this.opts.companionUrl
 
     if (!this.hostname) {
       throw new Error('Companion hostname is required, please consult https://uppy.io/docs/companion')
@@ -56,7 +56,7 @@ module.exports = class Url extends Plugin {
     this.handlePaste = this.handlePaste.bind(this)
 
     this.client = new RequestClient(uppy, {
-      serverUrl: this.opts.serverUrl,
+      companionUrl: this.opts.companionUrl,
       serverHeaders: this.opts.serverHeaders
     })
   }
@@ -120,7 +120,7 @@ module.exports = class Url extends Plugin {
             url: url
           },
           remote: {
-            serverUrl: this.opts.serverUrl,
+            companionUrl: this.opts.companionUrl,
             url: `${this.hostname}/url/get`,
             body: {
               fileId: url,

+ 1 - 1
packages/@uppy/url/types/index.d.ts

@@ -2,7 +2,7 @@ import Uppy = require('@uppy/core');
 
 declare module Url {
   export interface UrlOptions extends Uppy.PluginOptions {
-    serverUrl: string;
+    companionUrl: string;
     // TODO inherit from ProviderOptions
   }
 }

+ 11 - 7
packages/@uppy/xhr-upload/src/index.js

@@ -333,7 +333,7 @@ module.exports = class XHRUpload extends Plugin {
       )
       .then((res) => {
         const token = res.token
-        const host = getSocketHost(file.remote.serverUrl)
+        const host = getSocketHost(file.remote.companionUrl)
         const socket = new Socket({ target: `${host}/api/${token}` })
 
         socket.on('progress', (progressData) => emitSocketProgress(this, progressData, file))
@@ -507,10 +507,12 @@ module.exports = class XHRUpload extends Plugin {
 
   install () {
     if (this.opts.bundle) {
+      const { capabilities } = this.uppy.getState()
       this.uppy.setState({
-        capabilities: Object.assign({}, this.uppy.getState().capabilities, {
-          bundled: true
-        })
+        capabilities: {
+          ...capabilities,
+          individualCancellation: false
+        }
       })
     }
 
@@ -519,10 +521,12 @@ module.exports = class XHRUpload extends Plugin {
 
   uninstall () {
     if (this.opts.bundle) {
+      const { capabilities } = this.uppy.getState()
       this.uppy.setState({
-        capabilities: Object.assign({}, this.uppy.getState().capabilities, {
-          bundled: true
-        })
+        capabilities: {
+          ...capabilities,
+          individualCancellation: true
+        }
       })
     }
 

+ 2 - 2
packages/uppy/types/uppy-tests.ts

@@ -17,8 +17,8 @@ import * as Uppy from '../';
 (() => {
   const uppy = Uppy.Core({ autoProceed: false })
     .use(Uppy.Dashboard, { trigger: '#select-files' })
-    .use(Uppy.GoogleDrive, { target: Uppy.Dashboard, serverUrl: 'https://companion.uppy.io' })
-    .use(Uppy.Instagram, { target: Uppy.Dashboard, serverUrl: 'https://companion.uppy.io' })
+    .use(Uppy.GoogleDrive, { target: Uppy.Dashboard, companionUrl: 'https://companion.uppy.io' })
+    .use(Uppy.Instagram, { target: Uppy.Dashboard, companionUrl: 'https://companion.uppy.io' })
     .use(Uppy.Webcam, { target: Uppy.Dashboard })
     .use(Uppy.Tus, { endpoint: 'https://master.tus.io/files/' })
     .on('complete', (result) => {

+ 1 - 1
test/endtoend/create-react-app/src/App.js

@@ -15,7 +15,7 @@ class App extends Component {
 
     this.uppy = new Uppy({ id: 'uppy1', autoProceed: true, debug: true })
       .use(Tus, { endpoint: 'https://master.tus.io/files/' })
-      .use(GoogleDrive, { serverUrl: 'https://companion.uppy.io' })
+      .use(GoogleDrive, { companionUrl: 'https://companion.uppy.io' })
 
     this.uppy2 = new Uppy({ id: 'uppy2', autoProceed: false, debug: true })
       .use(Tus, { endpoint: 'https://master.tus.io/files/' })

+ 3 - 3
test/endtoend/providers/main.js

@@ -16,7 +16,7 @@ Uppy({
     target: '#uppyDashboard',
     inline: true
   })
-  .use(GoogleDrive, { target: Dashboard, serverUrl: 'http://localhost:3020' })
-  .use(Instagram, { target: Dashboard, serverUrl: 'http://localhost:3020' })
-  .use(Dropbox, { target: Dashboard, serverUrl: 'http://localhost:3020' })
+  .use(GoogleDrive, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+  .use(Instagram, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+  .use(Dropbox, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(Tus, { endpoint: 'https://master.tus.io/files/' })

+ 4 - 4
test/endtoend/typescript/main.ts

@@ -32,10 +32,10 @@ const uppy = Core({
     proudlyDisplayPoweredByUppy: true,
     note: '2 files, images and video only'
   })
-  .use(GoogleDrive, { target: Dashboard, serverUrl: 'http://localhost:3020' })
-  .use(Instagram, { target: Dashboard, serverUrl: 'http://localhost:3020' })
-  .use(Dropbox, { target: Dashboard, serverUrl: 'http://localhost:3020' })
-  .use(Url, { target: Dashboard, serverUrl: 'http://localhost:3020' })
+  .use(GoogleDrive, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+  .use(Instagram, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+  .use(Dropbox, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+  .use(Url, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(Webcam, { target: Dashboard })
   .use(Tus, { endpoint: TUS_ENDPOINT })
   .use(Form, { target: '#upload-form' })

+ 1 - 1
test/endtoend/url-plugin/main.js

@@ -16,7 +16,7 @@ function initUrlPlugin (companionUrl) {
     })
     .use(Url, {
       target: Dashboard,
-      serverUrl: companionUrl
+      companionUrl: companionUrl
     })
     .use(Tus, { endpoint: 'https://master.tus.io/files/' })
 }

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

@@ -25,4 +25,4 @@ Hi! You're reading our '30 Days to Liftoff' blog post challenge about the **laun
 
 - [Evgenia](https://github.com/lakesare) continues to charge at fixing Drag & Drop issues, and [Renée](https://github.com/goto-bus-stop) is working on making the Robodog Dashboard work inline (as well as a popup).
 
-Hope you'll have a great weekend and see you on Monday for another real update on the 1.0 launch preparations! We'll of course also have a few more light-hearted posts in the meantime for you, so be sure to check those out as well :slightly_smiling_face: Keep tabs on [@uppy_io](https://twitter.com/uppy_io) or [RSS](https://uppy.io/atom.xml) for more Uppy updates!
+Hope you'll have a great weekend and see you on [Monday](/blog/2019/04/liftoff-21/) for another real update on the 1.0 launch preparations! We'll of course also have a few more light-hearted posts in the meantime for you, so be sure to check those out as well :slightly_smiling_face: Keep tabs on [@uppy_io](https://twitter.com/uppy_io) or [RSS](https://uppy.io/atom.xml) for more Uppy updates!

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

@@ -13,4 +13,4 @@ Hey DJ, turn the music Uppy! We're taking a few well-deserved days off from chas
 
 <center><img width="400" src="https://media.giphy.com/media/rdAeOA3mfXomQ/giphy.gif"><br/><br/></center>
 
-That means no news today or tomorrow, but you can expect us to be back at the 1.0 grindstone on Monday! Have an awesome Saturday! And see you tomorrow for another little tidbit in our 30 Days to Liftoff!
+That means no news today or tomorrow, but you can expect us to be back at the 1.0 grindstone on [Monday](/blog/2019/04/liftoff-21/)! Have an awesome Saturday! And see you tomorrow for another little tidbit in our 30 Days to Liftoff!

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

@@ -13,5 +13,5 @@ And on the seventh day, Uppy rested :angel: We, too, will be spending this day i
 
 <center><img width="400"  src="https://media.giphy.com/media/xC5LOq3LMQmqs/giphy.gif"><br/><br/></center>
 
-Feel free to hit that snooze button a few more times yourself, before enjoying the remainder of your weekend. We'll see you back tomorrow on Day 21, when we'll have some more real news to share as we head into the final stretch of our 30 Days to Liftoff!
+Feel free to hit that snooze button a few more times yourself, before enjoying the remainder of your weekend. We'll see you back tomorrow on [Day 21](/blog/2019/04/liftoff-21/), when we'll have some more real news to share as we head into the final stretch of our 30 Days to Liftoff!
 

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

@@ -31,4 +31,4 @@ There are still a few unique, and possibly daunting, tasks left for the remainde
 
 - [Alex](https://github.com/nqst) [discovered an issue](https://github.com/transloadit/uppy/issues/1447) with the filter field when using Google Drive on the Dashboard.
 
-Our goal is in sight, so we're starting to get pretty excited! Keep us in your thoughts by giving us a follow on [Twitter](https://twitter.com/uppy_io) or subscribing to regular updates on [RSS](https://uppy.io/atom.xml). Day 22 is up next, so you can look forward to another lovely post from Kevin!
+Our goal is in sight, so we're starting to get pretty excited! Keep us in your thoughts by giving us a follow on [Twitter](https://twitter.com/uppy_io) or subscribing to regular updates on [RSS](https://uppy.io/atom.xml). [Day 22](/blog/2019/04/liftoff-22/) is up next, so you can look forward to another lovely post from Kevin!

+ 41 - 0
website/src/_posts/2019-04-liftoff-22.md

@@ -0,0 +1,41 @@
+---
+title: "Day 22"
+date: 2019-04-16
+author: kvz
+image: "https://uppy.io/images/blog/30daystoliftoff/day22.png"
+series: 30 Days to Liftoff
+seriesSuffix: 'of 30'
+---
+
+We will launch **Uppy 1.0 on April 25** and this is our '30 Days to Liftoff' blog post challenge where we share updates on our progress each day! Let's see what happened on Day 22.
+
+<center><br /><img width="400" src="/images/blog/30daystoliftoff/day22.png"><br /><br /></center>
+
+<!--more-->
+
+- [Artur](https://github.com/arturi) and [Kevin](https://github.com/kvz) have been pairing on [language packs](https://github.com/transloadit/uppy/pull/1443) and are nearly done with it. Très bien, magnifique!
+
+<center><img src="/images/blog/30daystoliftoff/localetodos.png"><br/><br/></center>
+
+After this, it will be just as easy to maintain language/locale packs as it will be to switch Uppy to your favorite language. Fun fact: we experimented a bit with Google Sheets and Google Translate to automatically create a first swing for a few of the languages we want to launch with.
+
+<center><a rel="noreferrer noopener" target="_blank" href="/images/blog/30daystoliftoff/sheetstranslate.png"><img src="/images/blog/30daystoliftoff/sheetstranslate.png"></a></center>
+
+It kinda worked! Google automatically translates to target languages in each column we add, for each change we make in the original! In the end, though, we decided this was not the way to go, as it really didn't save us any time. As it turns out, starting from a good English version of the text is _way_ easier than making sense of a broken Google translation. It was a fun experiment nonetheless and it's cool to see where this is all heading!
+
+Artur will also be reviewing the many PRs that have been submitted by both Transloadians and outside contributors.
+
+- [Alex](https://github.com/nqst) finished his redesign for the new website:
+
+<center><a rel="noreferrer noopener" target="_blank" href="/images/blog/30daystoliftoff/webdesign2.png"><img src="/images/blog/30daystoliftoff/webdesign2.png"></a></center>
+
+..and will start implementing it today! He also worked on more [design improvements for Uppy itself](https://github.com/transloadit/uppy/pull/1452).
+
+- [Ife](https://github.com/ifedapoolarewaju) is investigating incorrect progress reporting / uploads with XHR and Companion.
+
+- [Renée](https://github.com/goto-bus-stop) finished work on making Robodog work with an [inline dashboard](https://github.com/transloadit/uppy/pull/1450), as well as fix a bug in how we log to the console. Up next, Renée will work on error reporting for the Transloadit Plugin.
+
+- [Evgenia](https://github.com/lakesare) finished her work on [drag and drop improvements](https://github.com/transloadit/uppy/pull/1440) and will continue working on accessibility improvements.
+
+
+Stay current by following us on [Twitter](https://twitter.com/uppy_io) or subscribing to regular updates on [RSS](https://uppy.io/atom.xml). See you tomorrow for Day 23!

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

@@ -13,7 +13,7 @@ The `@uppy/aws-s3-multipart` plugin can be used to upload files directly to an S
 const AwsS3Multipart = require('@uppy/aws-s3-multipart')
 uppy.use(AwsS3Multipart, {
   limit: 4,
-  serverUrl: 'https://uppy-companion.myapp.net/'
+  companionUrl: 'https://uppy-companion.myapp.net/'
 })
 ```
 
@@ -41,7 +41,7 @@ The `@uppy/aws-s3-multipart` plugin has the following configurable options:
 
 The maximum amount of chunks to upload simultaneously. Set to `0` to disable limiting.
 
-### serverUrl: null
+### companionUrl: null
 
 The Companion URL to use for proxying calls to the S3 Multipart API.
 

+ 3 - 3
website/src/docs/aws-s3.md

@@ -17,7 +17,7 @@ const ms = require('ms')
 uppy.use(AwsS3, {
   limit: 2,
   timeout: ms('1 minute'),
-  serverUrl: 'https://uppy-companion.myapp.com/'
+  companionUrl: 'https://uppy-companion.myapp.com/'
 })
 ```
 
@@ -49,13 +49,13 @@ The `@uppy/aws-s3` plugin has the following configurable options:
 
 A unique identifier for this plugin. Defaults to `'AwsS3'`.
 
-### `serverUrl`
+### `companionUrl`
 
 When using [Companion][companion docs] to sign S3 uploads, set this option to the root URL of the Companion instance.
 
 ```js
 uppy.use(AwsS3, {
-  serverUrl: 'https://uppy-companion.my-app.com/'
+  companionUrl: 'https://uppy-companion.my-app.com/'
 })
 ```
 

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

@@ -52,7 +52,7 @@ The `@uppy/dropbox` plugin has the following configurable options:
 ```js
 uppy.use(Dropbox, {
   target: Dashboard,
-  serverUrl: 'https://companion.uppy.io/',
+  companionUrl: 'https://companion.uppy.io/',
 })
 ```
 
@@ -68,7 +68,7 @@ Title / name shown in the UI, such as Dashboard tabs. It defaults to `'Dropbox'`
 
 DOM element, CSS selector, or plugin to mount the Dropbox provider into. This should normally be the Dashboard.
 
-### `serverUrl: null`
+### `companionUrl: null`
 
 URL to a [Companion](/docs/companion) instance.
 
@@ -76,7 +76,7 @@ URL to a [Companion](/docs/companion) instance.
 
 Custom headers that should be sent along to [Companion](/docs/companion) on every request.
 
-### `serverPattern: serverUrl`
+### `companionAllowedHosts: companionUrl`
 
 The valid and authorised URL(s) from which OAuth responses should be accepted.
 

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

@@ -51,7 +51,7 @@ The `@uppy/google-drive` plugin has the following configurable options:
 ```js
 uppy.use(GoogleDrive, {
   target: Dashboard,
-  serverUrl: 'https://companion.uppy.io/',
+  companionUrl: 'https://companion.uppy.io/',
 })
 ```
 
@@ -67,7 +67,7 @@ Configures the title / name shown in the UI, for instance, on Dashboard tabs. It
 
 DOM element, CSS selector, or plugin to mount the Google Drive provider into. This should normally be the the [`@uppy/dashboard`](/docs/dashboard) plugin.
 
-### `serverUrl: null`
+### `companionUrl: null`
 
 URL to a [Companion](/docs/companion) instance.
 
@@ -75,7 +75,7 @@ URL to a [Companion](/docs/companion) instance.
 
 Custom headers that should be sent along to [Companion](/docs/companion) on every request.
 
-### `serverPattern: serverUrl`
+### `companionAllowedHosts: companionUrl`
 
 The valid and authorised URL(s) from which OAuth responses should be accepted.
 

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

@@ -53,7 +53,7 @@ The `@uppy/instagram` plugin has the following configurable options:
 ```js
 uppy.use(Instagram, {
   target: Dashboard,
-  serverUrl: 'https://companion.uppy.io/',
+  companionUrl: 'https://companion.uppy.io/',
 })
 ```
 
@@ -69,7 +69,7 @@ Configures the title / name shown in the UI, for instance, on Dashboard tabs. It
 
 DOM element, CSS selector, or plugin to mount the Instagram provider into. This should normally be the Dashboard.
 
-### `serverUrl: null`
+### `companionUrl: null`
 
 URL to a [Companion](/docs/companion) instance.
 
@@ -77,7 +77,7 @@ URL to a [Companion](/docs/companion) instance.
 
 Custom headers that should be sent along to [Companion](/docs/companion) on every request.
 
-### `serverPattern: serverUrl`
+### `companionAllowedHosts: companionUrl`
 
 The valid and authorised URL(s) from which OAuth responses should be accepted.
 

+ 5 - 5
website/src/docs/providers.md

@@ -10,7 +10,7 @@ The Provider plugins help you connect to your accounts with remote file provider
 
 As of now, the supported providers are [**Dropbox**](/docs/dropbox), [**GoogleDrive**](/docs/google-drive), [**Instagram**](/docs/instagram), and [**URL**](/docs/url).
 
-Usage of the Provider plugins is not that different from any other *acquirer* plugin, except that it takes an extra option `serverUrl`, which specifies the URL to the Companion that you are running. This allows Uppy to know what server to connect to when datacenter operations are required by the provider plugin.
+Usage of the Provider plugins is not that different from any other *acquirer* plugin, except that it takes an extra option `companionUrl`, which specifies the URL to the Companion that you are running. This allows Uppy to know what server to connect to when datacenter operations are required by the provider plugin.
 
 Here's a quick example:
 
@@ -24,19 +24,19 @@ uppy.use(Dashboard, {
 
 // for Google Drive
 const GoogleDrive = require('@uppy/google-drive')
-uppy.use(GoogleDrive, {target: Dashboard, serverUrl: 'http://localhost:3020'})
+uppy.use(GoogleDrive, {target: Dashboard, companionUrl: 'http://localhost:3020'})
 
 // for Dropbox
 const Dropbox = require('@uppy/dropbox')
-uppy.use(Dropbox, {target: Dashboard, serverUrl: 'http://localhost:3020'})
+uppy.use(Dropbox, {target: Dashboard, companionUrl: 'http://localhost:3020'})
 
 // for Instagram
 const Instagram = require('@uppy/instagram')
-uppy.use(Instagram, {target: Dashboard, serverUrl: 'http://localhost:3020'})
+uppy.use(Instagram, {target: Dashboard, companionUrl: 'http://localhost:3020'})
 
 // for URL
 const Url = require('@uppy/url')
-uppy.use(Url, {target: Dashboard, serverUrl: 'http://localhost:3020'})
+uppy.use(Url, {target: Dashboard, companionUrl: 'http://localhost:3020'})
 ```
 
 ⚠️ The [Dashboard](/docs/dashboard) plugin is recommended as a universal container to all Provider plugins. It also comes with file previews, progress reporting and more. If you are using the Dashboard, it already [comes with all the nessesary styles](/docs/dashboard/#CSS) and functionality for Providers to work well.

+ 2 - 2
website/src/docs/robodog-picker.md

@@ -77,11 +77,11 @@ Array of providers to use. Each entry is the name of a provider. The available o
 - `'url'` – Import files from public Web URLs using [Uppy Companion][companion].
 - `'webcam'` – Take photos and record videos using thee user's device camera.
 
-### `serverUrl: Transloadit.COMPANION`
+### `companionUrl: Transloadit.COMPANION`
 
 The URL to a [Uppy Companion][companion] server to use.
 
-### `serverPattern: Transloadit.COMPANION_PATTERN`
+### `companionAllowedHosts: Transloadit.COMPANION_PATTERN`
 
 The valid and authorised URL(s) from which OAuth responses should be accepted.
 

+ 9 - 9
website/src/docs/transloadit.md

@@ -59,38 +59,38 @@ const Dropbox = require('@uppy/dropbox')
 const Transloadit = require('@uppy/transloadit')
 
 uppy.use(Dropbox, {
-  serverUrl: Transloadit.COMPANION
-  serverPattern: Transloadit.COMPANION_PATTERN
+  companionUrl: Transloadit.COMPANION
+  companionAllowedHosts: Transloadit.COMPANION_PATTERN
 })
 ```
 
-When using `Transloadit.COMPANION`, you should also configure [`serverPattern: Transloadit.COMPANION_PATTERN`](#Transloadit-COMPANION-PATTERN).
+When using `Transloadit.COMPANION`, you should also configure [`companionAllowedHosts: Transloadit.COMPANION_PATTERN`](#Transloadit-COMPANION-PATTERN).
 
 The value of this constant is `https://api2.transloadit.com/companion`. If you are using a custom [`service`](#service) option, you should also set a custom host option in your provider plugins, by taking a Transloadit API url and appending `/companion`:
 
 ```js
 uppy.use(Dropbox, {
-  serverUrl: 'https://api2-us-east-1.transloadit.com/companion'
+  companionUrl: 'https://api2-us-east-1.transloadit.com/companion'
 })
 ```
 
 ### `Transloadit.COMPANION_PATTERN`
 
-A RegExp pattern matching Transloadit's hosted companion endpoints. The pattern is used in remote provider `serverPattern` options, to ensure that third party authentication messages cannot be faked by an attacker's page, but can only originate from Transloadit's servers.
+A RegExp pattern matching Transloadit's hosted companion endpoints. The pattern is used in remote provider `companionAllowedHosts` options, to ensure that third party authentication messages cannot be faked by an attacker's page, but can only originate from Transloadit's servers.
 
-Use it whenever you use `serverUrl: Transloadit.COMPANION`, like so:
+Use it whenever you use `companionUrl: Transloadit.COMPANION`, like so:
 
 ```js
 const Dropbox = require('@uppy/dropbox')
 const Transloadit = require('@uppy/transloadit')
 
 uppy.use(Dropbox, {
-  serverUrl: Transloadit.COMPANION
-  serverPattern: Transloadit.COMPANION_PATTERN
+  companionUrl: Transloadit.COMPANION
+  companionAllowedHosts: Transloadit.COMPANION_PATTERN
 })
 ```
 
-The value of this constant covers _all_ Transloadit's Companion servers, so it does not need to be changed if you are using a custom [`service`](#service) option. However, if you are not using the Transloadit Companion servers at `*.transloadit.com`, make sure to set the `serverPattern` option to something that matches what you do use.
+The value of this constant covers _all_ Transloadit's Companion servers, so it does not need to be changed if you are using a custom [`service`](#service) option. However, if you are not using the Transloadit Companion servers at `*.transloadit.com`, make sure to set the `companionAllowedHosts` option to something that matches what you do use.
 
 ## Options
 

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

@@ -56,7 +56,7 @@ The `@uppy/url` plugin has the following configurable options:
 ```js
 uppy.use(Url, {
   target: Dashboard,
-  serverUrl: 'https://companion.uppy.io/',
+  companionUrl: 'https://companion.uppy.io/',
   locale: {}
 })
 ```
@@ -73,7 +73,7 @@ Configures the title / name shown in the UI, for instance, on Dashboard tabs. It
 
 DOM element, CSS selector, or plugin to mount the URL provider into. This should normally be the Dashboard.
 
-### `serverUrl: null`
+### `companionUrl: null`
 
 URL to an Companion instance.
 

+ 4 - 4
website/src/examples/dashboard/app.es6

@@ -52,19 +52,19 @@ function uppyInit () {
   })
 
   if (opts.GoogleDrive) {
-    uppy.use(GoogleDrive, { target: Dashboard, serverUrl: COMPANION })
+    uppy.use(GoogleDrive, { target: Dashboard, companionUrl: COMPANION })
   }
 
   if (opts.Dropbox) {
-    uppy.use(Dropbox, { target: Dashboard, serverUrl: COMPANION })
+    uppy.use(Dropbox, { target: Dashboard, companionUrl: COMPANION })
   }
 
   if (opts.Instagram) {
-    uppy.use(Instagram, { target: Dashboard, serverUrl: COMPANION })
+    uppy.use(Instagram, { target: Dashboard, companionUrl: COMPANION })
   }
 
   if (opts.Url) {
-    uppy.use(Url, { target: Dashboard, serverUrl: COMPANION })
+    uppy.use(Url, { target: Dashboard, companionUrl: COMPANION })
   }
 
   if (opts.Webcam) {

+ 3 - 3
website/src/examples/dashboard/index.ejs

@@ -52,9 +52,9 @@ const uppy = Uppy({
   ],
   browserBackButtonClose: true
 })
-.use(GoogleDrive, { target: Dashboard, serverUrl: 'https://companion.uppy.io' })
-.use(Dropbox, { target: Dashboard, serverUrl: 'https://companion.uppy.io' })
-.use(Instagram, { target: Dashboard, serverUrl: 'https://companion.uppy.io' })
+.use(GoogleDrive, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
+.use(Dropbox, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
+.use(Instagram, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
 .use(Webcam, { target: Dashboard })
 .use(Tus, { endpoint: 'https://master.tus.io/files/' })
 

+ 2 - 2
website/src/examples/transloadit/app.es6

@@ -61,8 +61,8 @@ function initUppy () {
     })
     .use(Instagram, {
       target: Dashboard,
-      serverUrl: 'https://api2.transloadit.com/companion',
-      serverPattern: Transloadit.COMPANION_PATTERN
+      companionUrl: 'https://api2.transloadit.com/companion',
+      companionAllowedHosts: Transloadit.COMPANION_PATTERN
     })
     .use(Webcam, { target: Dashboard })
 

+ 1 - 1
website/src/examples/transloadit/index.ejs

@@ -126,7 +126,7 @@ uppy
     },
     waitForEncoding: true
   })
-  .use(Instagram, { target: Dashboard, serverUrl: 'https://api2.transloadit.com/companion', serverPattern: /\.transloadit\.com$/ })
+  .use(Instagram, { target: Dashboard, companionUrl: 'https://api2.transloadit.com/companion', companionAllowedHosts: /\.transloadit\.com$/ })
   .use(Dashboard, {
     inline: true,
     maxHeight: 400,

+ 1 - 1
website/src/frontpage-code-sample.ejs

@@ -22,7 +22,7 @@ Uppy()
   })
   .use(Instagram, {
     target: Dashboard,
-    serverUrl: 'https://companion.uppy.io'
+    companionUrl: 'https://companion.uppy.io'
   })
   .use(Tus, { endpoint: 'https://master.tus.io/files/' })
   .on('complete', (result) => {

BIN
website/src/images/blog/30daystoliftoff/29.jpg


+ 0 - 0
website/src/images/blog/30daystoliftoff/27.png → website/src/images/blog/30daystoliftoff/day22.png


BIN
website/src/images/blog/30daystoliftoff/localetodos.png


BIN
website/src/images/blog/30daystoliftoff/sheetstranslate.png


BIN
website/src/images/blog/30daystoliftoff/webdesign2.png


+ 3 - 3
website/themes/uppy/layout/index.ejs

@@ -102,10 +102,10 @@
         { id: 'caption', name: 'Caption', placeholder: 'describe what the image is about' }
       ]
     })
-    .use(Uppy.GoogleDrive, { target: Uppy.Dashboard, serverUrl: COMPANION_ENDPOINT })
-    .use(Uppy.Instagram, { target: Uppy.Dashboard, serverUrl: COMPANION_ENDPOINT })
+    .use(Uppy.GoogleDrive, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
+    .use(Uppy.Instagram, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
     .use(Uppy.Webcam, { target: Uppy.Dashboard })
-    .use(Uppy.Url, { target: Uppy.Dashboard, serverUrl: COMPANION_ENDPOINT })
+    .use(Uppy.Url, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
     .use(Uppy.Tus, { endpoint: TUS_ENDPOINT})
 
   uppy.on('success', (files) => {

+ 1 - 1
website/themes/uppy/layout/partials/frontpage-code-sample.html

@@ -12,7 +12,7 @@
   <span class="token punctuation">}</span><span class="token punctuation">)</span>
   <span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>Instagram<span class="token punctuation">,</span> <span class="token punctuation">{</span>
     target<span class="token punctuation">:</span> Dashboard<span class="token punctuation">,</span>
-    serverUrl<span class="token punctuation">:</span> <span class="token string">&apos;https://companion.uppy.io&apos;</span>
+    companionUrl<span class="token punctuation">:</span> <span class="token string">&apos;https://companion.uppy.io&apos;</span>
   <span class="token punctuation">}</span><span class="token punctuation">)</span>
   <span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>Tus<span class="token punctuation">,</span> <span class="token punctuation">{</span> endpoint<span class="token punctuation">:</span> <span class="token string">&apos;https://master.tus.io/files/&apos;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
   <span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">&apos;complete&apos;</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">result</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>

+ 1 - 1
website/themes/uppy/layout/post.ejs

@@ -28,7 +28,7 @@
     <%
         if (page.series) {
             var seriesList = []
-            site.posts.sort('date', +1).each(function (post) {
+            site.posts.sort('date', +1).reverse().each(function (post) {
                 if (post.series === page.series) {
                     seriesList.push('<option value="/' + post.path + '"' + (page.title === post.title ? ' selected' : '') + '>' + post.title + '</option>')
                 }