diff --git a/examples/custom-provider/client/MyCustomProvider.js b/examples/custom-provider/client/MyCustomProvider.js index f90cc2053..5127278d3 100644 --- a/examples/custom-provider/client/MyCustomProvider.js +++ b/examples/custom-provider/client/MyCustomProvider.js @@ -10,7 +10,6 @@ module.exports = class MyCustomProvider extends UIPlugin { this.id = this.opts.id || 'MyCustomProvider' Provider.initPlugin(this, opts) - this.title = 'MyUnsplash' this.icon = () => ( @@ -24,6 +23,14 @@ module.exports = class MyCustomProvider extends UIPlugin { pluginId: this.id, }) + this.defaultLocale = { + strings: { + pluginNameMyUnsplash: 'MyUnsplash', + }, + } + this.i18nInit() + this.title = this.i18n('MyUnsplash') + this.files = [] this.onFirstRender = this.onFirstRender.bind(this) this.render = this.render.bind(this) diff --git a/package-lock.json b/package-lock.json index 271bbde6c..02a857794 100644 --- a/package-lock.json +++ b/package-lock.json @@ -89718,7 +89718,7 @@ "@uppy/google-drive": "*", "@uppy/informer": "file:../informer", "@uppy/provider-views": "file:../provider-views", - "@uppy/status-bar": "*", + "@uppy/status-bar": "file:../status-bar", "@uppy/thumbnail-generator": "file:../thumbnail-generator", "@uppy/utils": "file:../utils", "classnames": "^2.2.6", diff --git a/packages/@uppy/aws-s3/src/index.js b/packages/@uppy/aws-s3/src/index.js index 7d2415f1f..0a2824f1c 100644 --- a/packages/@uppy/aws-s3/src/index.js +++ b/packages/@uppy/aws-s3/src/index.js @@ -90,24 +90,11 @@ module.exports = class AwsS3 extends BasePlugin { this.opts = { ...defaultOptions, ...opts } - this.i18nInit() - this.client = new RequestClient(uppy, opts) this.handleUpload = this.handleUpload.bind(this) this.requests = new RateLimitedQueue(this.opts.limit) } - setOptions (newOpts) { - super.setOptions(newOpts) - this.i18nInit() - } - - i18nInit () { - this.translator = new Translator([this.defaultLocale, this.uppy.locale, this.opts.locale]) - this.i18n = this.translator.translate.bind(this.translator) - this.setPluginState() // so that UI re-renders and we see the updated locale - } - getUploadParameters (file) { if (!this.opts.companionUrl) { throw new Error('Expected a `companionUrl` option containing a Companion address.') diff --git a/packages/@uppy/box/src/index.js b/packages/@uppy/box/src/index.js index 59c6aa3cf..b0a6a4a7e 100644 --- a/packages/@uppy/box/src/index.js +++ b/packages/@uppy/box/src/index.js @@ -32,6 +32,14 @@ module.exports = class Box extends UIPlugin { pluginId: this.id, }) + this.defaultLocale = { + strings: { + pluginNameBox: 'Box', + }, + } + this.i18nInit() + this.title = this.i18n('pluginNameBox') + this.onFirstRender = this.onFirstRender.bind(this) this.render = this.render.bind(this) } diff --git a/packages/@uppy/companion-client/src/RequestClient.js b/packages/@uppy/companion-client/src/RequestClient.js index 0b71a72fb..d6dd7673d 100644 --- a/packages/@uppy/companion-client/src/RequestClient.js +++ b/packages/@uppy/companion-client/src/RequestClient.js @@ -8,9 +8,33 @@ function stripSlash (url) { return url.replace(/\/$/, '') } +async function handleJSONResponse (res) { + if (res.status === 401) { + throw new AuthError() + } + + const jsonPromise = res.json() + + if (res.status < 200 || res.status > 300) { + let errMsg = `Failed request with status: ${res.status}. ${res.statusText}` + try { + const errData = await jsonPromise + errMsg = errData.message ? `${errMsg} message: ${errData.message}` : errMsg + errMsg = errData.requestId ? `${errMsg} request-Id: ${errData.requestId}` : errMsg + } finally { + // eslint-disable-next-line no-unsafe-finally + throw new Error(errMsg) + } + } + return jsonPromise +} + module.exports = class RequestClient { + // eslint-disable-next-line global-require static VERSION = require('../package.json').version + #getPostResponseFunc = skip => response => (skip ? response : this.onReceiveResponse(response)) + constructor (uppy, opts) { this.uppy = uppy this.opts = opts @@ -25,32 +49,20 @@ module.exports = class RequestClient { return stripSlash(companion && companion[host] ? companion[host] : host) } - get defaultHeaders () { - return { - Accept: 'application/json', - 'Content-Type': 'application/json', - 'Uppy-Versions': `@uppy/companion-client=${RequestClient.VERSION}`, - } + static defaultHeaders ={ + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Uppy-Versions': `@uppy/companion-client=${RequestClient.VERSION}`, } headers () { const userHeaders = this.opts.companionHeaders || {} return Promise.resolve({ - ...this.defaultHeaders, + ...RequestClient.defaultHeaders, ...userHeaders, }) } - _getPostResponseFunc (skip) { - return (response) => { - if (!skip) { - return this.onReceiveResponse(response) - } - - return response - } - } - onReceiveResponse (response) { const state = this.uppy.getState() const companion = state.companion || {} @@ -65,28 +77,22 @@ module.exports = class RequestClient { return response } - _getUrl (url) { + #getUrl (url) { if (/^(https?:|)\/\//.test(url)) { return url } return `${this.hostname}/${url}` } - _json (res) { - if (res.status === 401) { - throw new AuthError() + #errorHandler (method, path) { + return (err) => { + if (!err?.isAuthError) { + const error = new Error(`Could not ${method} ${this.#getUrl(path)}`) + error.cause = err + err = error // eslint-disable-line no-param-reassign + } + return Promise.reject(err) } - - if (res.status < 200 || res.status > 300) { - let errMsg = `Failed request with status: ${res.status}. ${res.statusText}` - return res.json() - .then((errData) => { - errMsg = errData.message ? `${errMsg} message: ${errData.message}` : errMsg - errMsg = errData.requestId ? `${errMsg} request-Id: ${errData.requestId}` : errMsg - throw new Error(errMsg) - }).catch(() => { throw new Error(errMsg) }) - } - return res.json() } preflight (path) { @@ -94,7 +100,7 @@ module.exports = class RequestClient { return Promise.resolve(this.allowedHeaders.slice()) } - return fetch(this._getUrl(path), { + return fetch(this.#getUrl(path), { method: 'OPTIONS', }) .then((response) => { @@ -117,9 +123,9 @@ module.exports = class RequestClient { .then(([allowedHeaders, headers]) => { // filter to keep only allowed Headers Object.keys(headers).forEach((header) => { - if (allowedHeaders.indexOf(header.toLowerCase()) === -1) { - this.uppy.log(`[CompanionClient] excluding unallowed header ${header}`) - delete headers[header] + if (!allowedHeaders.includes(header.toLowerCase())) { + this.uppy.log(`[CompanionClient] excluding disallowed header ${header}`) + delete headers[header] // eslint-disable-line no-param-reassign } }) @@ -128,55 +134,43 @@ module.exports = class RequestClient { } get (path, skipPostResponse) { + const method = 'get' return this.preflightAndHeaders(path) - .then((headers) => fetchWithNetworkError(this._getUrl(path), { - method: 'get', + .then((headers) => fetchWithNetworkError(this.#getUrl(path), { + method, headers, credentials: this.opts.companionCookiesRule || 'same-origin', })) - .then(this._getPostResponseFunc(skipPostResponse)) - .then((res) => this._json(res)) - .catch((err) => { - if (!err.isAuthError) { - err.message = `Could not get ${this._getUrl(path)}. ${err.message}` - } - return Promise.reject(err) - }) + .then(this.#getPostResponseFunc(skipPostResponse)) + .then(handleJSONResponse) + .catch(this.#errorHandler(method, path)) } post (path, data, skipPostResponse) { + const method = 'post' return this.preflightAndHeaders(path) - .then((headers) => fetchWithNetworkError(this._getUrl(path), { - method: 'post', + .then((headers) => fetchWithNetworkError(this.#getUrl(path), { + method, headers, credentials: this.opts.companionCookiesRule || 'same-origin', body: JSON.stringify(data), })) - .then(this._getPostResponseFunc(skipPostResponse)) - .then((res) => this._json(res)) - .catch((err) => { - if (!err.isAuthError) { - err.message = `Could not post ${this._getUrl(path)}. ${err.message}` - } - return Promise.reject(err) - }) + .then(this.#getPostResponseFunc(skipPostResponse)) + .then(handleJSONResponse) + .catch(this.#errorHandler(method, path)) } delete (path, data, skipPostResponse) { + const method = 'delete' return this.preflightAndHeaders(path) .then((headers) => fetchWithNetworkError(`${this.hostname}/${path}`, { - method: 'delete', + method, headers, credentials: this.opts.companionCookiesRule || 'same-origin', body: data ? JSON.stringify(data) : null, })) - .then(this._getPostResponseFunc(skipPostResponse)) - .then((res) => this._json(res)) - .catch((err) => { - if (!err.isAuthError) { - err.message = `Could not delete ${this._getUrl(path)}. ${err.message}` - } - return Promise.reject(err) - }) + .then(this.#getPostResponseFunc(skipPostResponse)) + .then(handleJSONResponse) + .catch(this.#errorHandler(method, path)) } } diff --git a/packages/@uppy/companion-client/src/Socket.js b/packages/@uppy/companion-client/src/Socket.js index a289eb794..f2b8245ba 100644 --- a/packages/@uppy/companion-client/src/Socket.js +++ b/packages/@uppy/companion-client/src/Socket.js @@ -1,83 +1,84 @@ const ee = require('namespace-emitter') module.exports = class UppySocket { + #queued = [] + + #emitter = ee() + + #isOpen = false + + #socket + constructor (opts) { this.opts = opts - this._queued = [] - this.isOpen = false - this.emitter = ee() - - this._handleMessage = this._handleMessage.bind(this) - - this.close = this.close.bind(this) - this.emit = this.emit.bind(this) - this.on = this.on.bind(this) - this.once = this.once.bind(this) - this.send = this.send.bind(this) if (!opts || opts.autoOpen !== false) { this.open() } } + get isOpen () { return this.#isOpen } + + [Symbol.for('uppy test: getSocket')] () { return this.#socket } + + [Symbol.for('uppy test: getQueued')] () { return this.#queued } + open () { - this.socket = new WebSocket(this.opts.target) + this.#socket = new WebSocket(this.opts.target) - this.socket.onopen = (e) => { - this.isOpen = true + this.#socket.onopen = () => { + this.#isOpen = true - while (this._queued.length > 0 && this.isOpen) { - const first = this._queued[0] + while (this.#queued.length > 0 && this.#isOpen) { + const first = this.#queued.shift() this.send(first.action, first.payload) - this._queued = this._queued.slice(1) } } - this.socket.onclose = (e) => { - this.isOpen = false + this.#socket.onclose = () => { + this.#isOpen = false } - this.socket.onmessage = this._handleMessage + this.#socket.onmessage = this.#handleMessage } close () { - if (this.socket) { - this.socket.close() - } + this.#socket?.close() } send (action, payload) { // attach uuid - if (!this.isOpen) { - this._queued.push({ action, payload }) + if (!this.#isOpen) { + this.#queued.push({ action, payload }) return } - this.socket.send(JSON.stringify({ + this.#socket.send(JSON.stringify({ action, payload, })) } on (action, handler) { - this.emitter.on(action, handler) + this.#emitter.on(action, handler) } emit (action, payload) { - this.emitter.emit(action, payload) + this.#emitter.emit(action, payload) } once (action, handler) { - this.emitter.once(action, handler) + this.#emitter.once(action, handler) } - _handleMessage (e) { + #handleMessage= (e) => { try { const message = JSON.parse(e.data) this.emit(message.action, message.payload) } catch (err) { - console.log(err) + // TODO: use a more robust error handler. + console.log(err) // eslint-disable-line no-console } } } diff --git a/packages/@uppy/companion-client/src/Socket.test.js b/packages/@uppy/companion-client/src/Socket.test.js index bae8af4c7..255cb14a0 100644 --- a/packages/@uppy/companion-client/src/Socket.test.js +++ b/packages/@uppy/companion-client/src/Socket.test.js @@ -15,10 +15,12 @@ describe('Socket', () => { webSocketConstructorSpy(target) } + // eslint-disable-next-line class-methods-use-this close (args) { webSocketCloseSpy(args) } + // eslint-disable-next-line class-methods-use-this send (json) { webSocketSendSpy(json) } @@ -52,7 +54,7 @@ describe('Socket', () => { it('should send a message via the websocket if the connection is open', () => { const uppySocket = new UppySocket({ target: 'foo' }) - const webSocketInstance = uppySocket.socket + const webSocketInstance = uppySocket[Symbol.for('uppy test: getSocket')]() webSocketInstance.triggerOpen() uppySocket.send('bar', 'boo') @@ -66,17 +68,17 @@ describe('Socket', () => { const uppySocket = new UppySocket({ target: 'foo' }) uppySocket.send('bar', 'boo') - expect(uppySocket._queued).toEqual([{ action: 'bar', payload: 'boo' }]) + expect(uppySocket[Symbol.for('uppy test: getQueued')]()).toEqual([{ action: 'bar', payload: 'boo' }]) expect(webSocketSendSpy.mock.calls.length).toEqual(0) }) it('should queue any messages for the websocket if the connection is not open, then send them when the connection is open', () => { const uppySocket = new UppySocket({ target: 'foo' }) - const webSocketInstance = uppySocket.socket + const webSocketInstance = uppySocket[Symbol.for('uppy test: getSocket')]() uppySocket.send('bar', 'boo') uppySocket.send('moo', 'baa') - expect(uppySocket._queued).toEqual([ + expect(uppySocket[Symbol.for('uppy test: getQueued')]()).toEqual([ { action: 'bar', payload: 'boo' }, { action: 'moo', payload: 'baa' }, ]) @@ -84,7 +86,7 @@ describe('Socket', () => { webSocketInstance.triggerOpen() - expect(uppySocket._queued).toEqual([]) + expect(uppySocket[Symbol.for('uppy test: getQueued')]()).toEqual([]) expect(webSocketSendSpy.mock.calls.length).toEqual(2) expect(webSocketSendSpy.mock.calls[0]).toEqual([ JSON.stringify({ action: 'bar', payload: 'boo' }), @@ -96,19 +98,19 @@ describe('Socket', () => { it('should start queuing any messages when the websocket connection is closed', () => { const uppySocket = new UppySocket({ target: 'foo' }) - const webSocketInstance = uppySocket.socket + const webSocketInstance = uppySocket[Symbol.for('uppy test: getSocket')]() webSocketInstance.triggerOpen() uppySocket.send('bar', 'boo') - expect(uppySocket._queued).toEqual([]) + expect(uppySocket[Symbol.for('uppy test: getQueued')]()).toEqual([]) webSocketInstance.triggerClose() uppySocket.send('bar', 'boo') - expect(uppySocket._queued).toEqual([{ action: 'bar', payload: 'boo' }]) + expect(uppySocket[Symbol.for('uppy test: getQueued')]()).toEqual([{ action: 'bar', payload: 'boo' }]) }) it('should close the websocket when it is force closed', () => { const uppySocket = new UppySocket({ target: 'foo' }) - const webSocketInstance = uppySocket.socket + const webSocketInstance = uppySocket[Symbol.for('uppy test: getSocket')]() webSocketInstance.triggerOpen() uppySocket.close() @@ -117,7 +119,7 @@ describe('Socket', () => { it('should be able to subscribe to messages received on the websocket', () => { const uppySocket = new UppySocket({ target: 'foo' }) - const webSocketInstance = uppySocket.socket + const webSocketInstance = uppySocket[Symbol.for('uppy test: getSocket')]() const emitterListenerMock = jest.fn() uppySocket.on('hi', emitterListenerMock) diff --git a/packages/@uppy/companion-client/types/index.d.ts b/packages/@uppy/companion-client/types/index.d.ts index cce383f56..4bd886e9f 100644 --- a/packages/@uppy/companion-client/types/index.d.ts +++ b/packages/@uppy/companion-client/types/index.d.ts @@ -65,7 +65,7 @@ export interface SocketOptions { } export class Socket { - isOpen: boolean + readonly isOpen: boolean constructor (opts: SocketOptions) diff --git a/packages/@uppy/core/src/BasePlugin.js b/packages/@uppy/core/src/BasePlugin.js index 8cc2b0edd..534efefa7 100644 --- a/packages/@uppy/core/src/BasePlugin.js +++ b/packages/@uppy/core/src/BasePlugin.js @@ -6,6 +6,9 @@ * * See `Plugin` for the extended version with Preact rendering for interfaces. */ + +const Translator = require('@uppy/utils/lib/Translator') + module.exports = class BasePlugin { constructor (uppy, opts = {}) { this.uppy = uppy @@ -34,6 +37,14 @@ module.exports = class BasePlugin { setOptions (newOpts) { this.opts = { ...this.opts, ...newOpts } this.setPluginState() // so that UI re-renders with new options + this.i18nInit() + } + + i18nInit () { + const translator = new Translator([this.defaultLocale, this.uppy.locale, this.opts.locale]) + this.i18n = translator.translate.bind(translator) + this.i18nArray = translator.translateArray.bind(translator) + this.setPluginState() // so that UI re-renders and we see the updated locale } /** diff --git a/packages/@uppy/core/src/index.js b/packages/@uppy/core/src/index.js index eb6fd31ae..fb4fc125f 100644 --- a/packages/@uppy/core/src/index.js +++ b/packages/@uppy/core/src/index.js @@ -54,6 +54,14 @@ class Uppy { #emitter = ee() + #translator + + #preProcessors = new Set() + + #uploaders = new Set() + + #postProcessors = new Set() + /** * Instantiate Uppy * @@ -173,10 +181,6 @@ class Uppy { // [Practical Check] Firefox, try to upload a big file for a prolonged period of time. Laptop will start to heat up. this.calculateProgress = throttle(this.calculateProgress.bind(this), 500, { leading: true, trailing: true }) - this.preProcessors = [] - this.uploaders = [] - this.postProcessors = [] - this.store = this.opts.store this.setState({ plugins: {}, @@ -279,10 +283,16 @@ class Uppy { } i18nInit () { - this.translator = new Translator([this.defaultLocale, this.opts.locale]) - this.locale = this.translator.locale - this.i18n = this.translator.translate.bind(this.translator) - this.i18nArray = this.translator.translateArray.bind(this.translator) + this.#translator = new Translator([this.defaultLocale, this.opts.locale]) + this.locale = this.#translator.locale + } + + i18n (...args) { + return this.#translator.translate(...args) + } + + i18nArray (...args) { + return this.#translator.translateArray(...args) } setOptions (newOpts) { @@ -335,36 +345,27 @@ class Uppy { } addPreProcessor (fn) { - this.preProcessors.push(fn) + this.#preProcessors.add(fn) } removePreProcessor (fn) { - const i = this.preProcessors.indexOf(fn) - if (i !== -1) { - this.preProcessors.splice(i, 1) - } + return this.#preProcessors.delete(fn) } addPostProcessor (fn) { - this.postProcessors.push(fn) + this.#postProcessors.add(fn) } removePostProcessor (fn) { - const i = this.postProcessors.indexOf(fn) - if (i !== -1) { - this.postProcessors.splice(i, 1) - } + return this.#postProcessors.delete(fn) } addUploader (fn) { - this.uploaders.push(fn) + this.#uploaders.add(fn) } removeUploader (fn) { - const i = this.uploaders.indexOf(fn) - if (i !== -1) { - this.uploaders.splice(i, 1) - } + return this.#uploaders.delete(fn) } setMeta (data) { @@ -1199,7 +1200,7 @@ class Uppy { this.setFileState(file.id, { progress: { ...currentProgress, - postprocess: this.postProcessors.length > 0 ? { + postprocess: this.#postProcessors.size > 0 ? { mode: 'indeterminate', } : null, uploadComplete: true, @@ -1592,9 +1593,9 @@ class Uppy { const restoreStep = uploadData.step const steps = [ - ...this.preProcessors, - ...this.uploaders, - ...this.postProcessors, + ...this.#preProcessors, + ...this.#uploaders, + ...this.#postProcessors, ] let lastStep = Promise.resolve() steps.forEach((fn, step) => { diff --git a/packages/@uppy/core/src/index.test.js b/packages/@uppy/core/src/index.test.js index 137648fb8..5e4ac599c 100644 --- a/packages/@uppy/core/src/index.test.js +++ b/packages/@uppy/core/src/index.test.js @@ -327,24 +327,13 @@ describe('src/Core', () => { }) describe('preprocessors', () => { - it('should add a preprocessor', () => { + it('should add and remove preprocessor', () => { const core = new Core() const preprocessor = () => { } + expect(core.removePreProcessor(preprocessor)).toBe(false) core.addPreProcessor(preprocessor) - expect(core.preProcessors[0]).toEqual(preprocessor) - }) - - it('should remove a preprocessor', () => { - const core = new Core() - const preprocessor1 = () => { } - const preprocessor2 = () => { } - const preprocessor3 = () => { } - core.addPreProcessor(preprocessor1) - core.addPreProcessor(preprocessor2) - core.addPreProcessor(preprocessor3) - expect(core.preProcessors.length).toEqual(3) - core.removePreProcessor(preprocessor2) - expect(core.preProcessors.length).toEqual(2) + expect(core.removePreProcessor(preprocessor)).toBe(true) + expect(core.removePreProcessor(preprocessor)).toBe(false) }) it('should execute all the preprocessors when uploading a file', () => { @@ -456,24 +445,13 @@ describe('src/Core', () => { }) describe('postprocessors', () => { - it('should add a postprocessor', () => { + it('should add and remove postprocessor', () => { const core = new Core() const postprocessor = () => { } + expect(core.removePostProcessor(postprocessor)).toBe(false) core.addPostProcessor(postprocessor) - expect(core.postProcessors[0]).toEqual(postprocessor) - }) - - it('should remove a postprocessor', () => { - const core = new Core() - const postprocessor1 = () => { } - const postprocessor2 = () => { } - const postprocessor3 = () => { } - core.addPostProcessor(postprocessor1) - core.addPostProcessor(postprocessor2) - core.addPostProcessor(postprocessor3) - expect(core.postProcessors.length).toEqual(3) - core.removePostProcessor(postprocessor2) - expect(core.postProcessors.length).toEqual(2) + expect(core.removePostProcessor(postprocessor)).toBe(true) + expect(core.removePostProcessor(postprocessor)).toBe(false) }) it('should execute all the postprocessors when uploading a file', () => { @@ -586,24 +564,13 @@ describe('src/Core', () => { }) describe('uploaders', () => { - it('should add an uploader', () => { + it('should add and remove uploader', () => { const core = new Core() const uploader = () => { } + expect(core.removeUploader(uploader)).toBe(false) core.addUploader(uploader) - expect(core.uploaders[0]).toEqual(uploader) - }) - - it('should remove an uploader', () => { - const core = new Core() - const uploader1 = () => { } - const uploader2 = () => { } - const uploader3 = () => { } - core.addUploader(uploader1) - core.addUploader(uploader2) - core.addUploader(uploader3) - expect(core.uploaders.length).toEqual(3) - core.removeUploader(uploader2) - expect(core.uploaders.length).toEqual(2) + expect(core.removeUploader(uploader)).toBe(true) + expect(core.removeUploader(uploader)).toBe(false) }) }) diff --git a/packages/@uppy/dashboard/src/index.js b/packages/@uppy/dashboard/src/index.js index 5990298b1..37def194f 100644 --- a/packages/@uppy/dashboard/src/index.js +++ b/packages/@uppy/dashboard/src/index.js @@ -165,18 +165,6 @@ module.exports = class Dashboard extends UIPlugin { this.removeDragOverClassTimeout = null } - setOptions = (newOpts) => { - super.setOptions(newOpts) - this.i18nInit() - } - - i18nInit = () => { - this.translator = new Translator([this.defaultLocale, this.uppy.locale, this.opts.locale]) - this.i18n = this.translator.translate.bind(this.translator) - this.i18nArray = this.translator.translateArray.bind(this.translator) - this.setPluginState() // so that UI re-renders and we see the updated locale - } - removeTarget = (plugin) => { const pluginState = this.getPluginState() // filter out the one we want to remove diff --git a/packages/@uppy/drag-drop/src/index.js b/packages/@uppy/drag-drop/src/index.js index 81036504f..d10e3c40b 100644 --- a/packages/@uppy/drag-drop/src/index.js +++ b/packages/@uppy/drag-drop/src/index.js @@ -38,12 +38,12 @@ module.exports = class DragDrop extends UIPlugin { // Merge default options with the ones set by user this.opts = { ...defaultOpts, ...opts } + this.i18nInit() + // Check for browser dragDrop support this.isDragDropSupported = isDragDropSupported() this.removeDragOverClassTimeout = null - this.i18nInit() - // Bind `this` to class methods this.onInputChange = this.onInputChange.bind(this) this.handleDragOver = this.handleDragOver.bind(this) @@ -53,18 +53,6 @@ module.exports = class DragDrop extends UIPlugin { this.render = this.render.bind(this) } - setOptions (newOpts) { - super.setOptions(newOpts) - this.i18nInit() - } - - i18nInit () { - this.translator = new Translator([this.defaultLocale, this.uppy.locale, this.opts.locale]) - this.i18n = this.translator.translate.bind(this.translator) - this.i18nArray = this.translator.translateArray.bind(this.translator) - this.setPluginState() // so that UI re-renders and we see the updated locale - } - addFiles (files) { const descriptors = files.map((file) => ({ source: this.id, diff --git a/packages/@uppy/dropbox/src/index.js b/packages/@uppy/dropbox/src/index.js index 502300e69..ed0b9dd8e 100644 --- a/packages/@uppy/dropbox/src/index.js +++ b/packages/@uppy/dropbox/src/index.js @@ -29,6 +29,14 @@ module.exports = class Dropbox extends UIPlugin { pluginId: this.id, }) + this.defaultLocale = { + strings: { + pluginNameDropbox: 'Dropbox', + }, + } + this.i18nInit() + this.title = this.i18n('pluginNameDropbox') + this.onFirstRender = this.onFirstRender.bind(this) this.render = this.render.bind(this) } diff --git a/packages/@uppy/facebook/src/index.js b/packages/@uppy/facebook/src/index.js index 6ef0723c1..40e11be4d 100644 --- a/packages/@uppy/facebook/src/index.js +++ b/packages/@uppy/facebook/src/index.js @@ -29,6 +29,14 @@ module.exports = class Facebook extends UIPlugin { pluginId: this.id, }) + this.defaultLocale = { + strings: { + pluginNameFacebook: 'Facebook', + }, + } + this.i18nInit() + this.title = this.i18n('pluginNameFacebook') + this.onFirstRender = this.onFirstRender.bind(this) this.render = this.render.bind(this) } diff --git a/packages/@uppy/file-input/src/index.js b/packages/@uppy/file-input/src/index.js index c9ce2641e..4442507d4 100644 --- a/packages/@uppy/file-input/src/index.js +++ b/packages/@uppy/file-input/src/index.js @@ -38,18 +38,6 @@ module.exports = class FileInput extends UIPlugin { this.handleClick = this.handleClick.bind(this) } - setOptions (newOpts) { - super.setOptions(newOpts) - this.i18nInit() - } - - i18nInit () { - this.translator = new Translator([this.defaultLocale, this.uppy.locale, this.opts.locale]) - this.i18n = this.translator.translate.bind(this.translator) - this.i18nArray = this.translator.translateArray.bind(this.translator) - this.setPluginState() // so that UI re-renders and we see the updated locale - } - addFiles (files) { const descriptors = files.map((file) => ({ source: this.id, diff --git a/packages/@uppy/google-drive/src/index.js b/packages/@uppy/google-drive/src/index.js index 3082d28c1..0b9859814 100644 --- a/packages/@uppy/google-drive/src/index.js +++ b/packages/@uppy/google-drive/src/index.js @@ -30,6 +30,14 @@ module.exports = class GoogleDrive extends UIPlugin { pluginId: this.id, }) + this.defaultLocale = { + strings: { + pluginNameGoogleDrive: 'Google Drive', + }, + } + this.i18nInit() + this.title = this.i18n('pluginNameGoogleDrive') + this.onFirstRender = this.onFirstRender.bind(this) this.render = this.render.bind(this) } diff --git a/packages/@uppy/image-editor/src/Editor.js b/packages/@uppy/image-editor/src/Editor.js index eddf6d8ea..77f0f8309 100644 --- a/packages/@uppy/image-editor/src/Editor.js +++ b/packages/@uppy/image-editor/src/Editor.js @@ -31,43 +31,15 @@ module.exports = class Editor extends Component { this.cropper.destroy() } - renderRevert () { - return ( - - ) - } + save = () => { + const { opts, save, currentImage } = this.props - renderRotate () { - return ( - - ) + this.cropper.getCroppedCanvas(opts.cropperOptions.croppedCanvasOptions) + .toBlob( + (blob) => save(blob), + currentImage.type, + opts.quality + ) } granularRotateOnChange = (ev) => { @@ -83,11 +55,15 @@ module.exports = class Editor extends Component { } renderGranularRotate () { + const { i18n } = this.props + const { rotationDelta, rotationAngle } = this.state + return ( + // eslint-disable-next-line jsx-a11y/label-has-associated-control ) } - renderFlip () { + renderRevert () { + const { i18n } = this.props + return ( + ) + } + + renderRotate () { + const { i18n } = this.props + + return ( + + ) + } + + renderFlip () { + const { i18n } = this.props + + return ( +