diff --git a/CHANGELOG.md b/CHANGELOG.md index 086086706..2a2fa082c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,43 +72,46 @@ maybe later: ## 0.18.0 +To be released: 2017-07-28 + - [ ] test: add https://github.com/pa11y/pa11y for automated accessibility testing (@arturi) - [ ] webcam: look into simplifying / improving webcam plugin (@arturi, @goto-bus-stop) - [ ] core: add error in file progress state? (@arturi) +- [ ] core: research !important styles to be immune to any environment/page. Maybe use smth like `postcss-safe-important`. Or increase specificity (with .Uppy) (@arturi) - [ ] uppy-server: add uppy-server metrics to Librato (@ifedapoolarewaju) - [ ] dashboard: error UI, question mark button, `core:error` (@arturi) - [ ] uploaders: add direct-to-s3 upload plugin and test it with the flow to then upload to transloadit, stage 2, release (@goto-bus-stop) +- [ ] server: what if smth changed in GDrive while it was open in Uppy? refresh file list? (@ifedapoolarewaju) +- [ ] core: see if we can figure out css-in-js, while keeping non-random classnames (ideally prefixed) and useful preprocessor features. also see simple https://github.com/codemirror/CodeMirror/blob/master/lib/codemirror.css (@arturi, @goto-bus-stop) +- [ ] test: add tests for `npm install uppy` and running in different browsers, the real world use case (@arturi) +- [ ] provider: improve UI: add icons for file types (@arturi) ## 0.17.0 To be released: 2017-06-30 -- [ ] core: add `uppy.reset()` as discussed in #179 (@arturi) -- [ ] core: research !important styles to be immune to any environment/page. Maybe use smth like https://www.npmjs.com/package/postcss-safe-important. Or increase specificity (with .Uppy) (@arturi) - [ ] core: restrictions — by file type, size, number of files (@arturi) -- [ ] core: see if we can figure out css-in-js, while keeping non-random classnames (ideally prefixed) and useful preprocessor features. also see simple https://github.com/codemirror/CodeMirror/blob/master/lib/codemirror.css (@arturi, @goto-bus-stop) -- [ ] fileinput: allow retriving fields/options from form (@arturi #153) -- [ ] provider: improve UI: add icons for file types (@arturi) -- [ ] provider: improve UI: improve overall look, breadcrumbs (@arturi) -- [ ] provider: improve UI: steps towards making it responsive (@arturi) -- [ ] server: what if smth changed in GDrive while it was open in Uppy? refresh file list? (@ifedapoolarewaju) -- [x] statusbar: show status “Upload started...” when the remote upload has begun, but no progress events received yet (@arturi) -- [ ] test: add tests for `npm install uppy` and running in different browsers, the real world use case (@arturi) -- [x] uploaders: add direct-to-s3 upload plugin and test it with the flow to then upload to transloadit, stage 1, WIP (@goto-bus-stop) -- [ ] uppy/uppy-server: Make a barely working Instagram Plugin (@ifedapoolarewaju / #21) -- [x] uppy/uppy-server: some file types cannot be downloaded/uploaded on google drive (e.g google docs). How to handle that? (@ifedapoolarewaju) -- [x] uppy: fix google drive uploads on mobile (double click issue) (@arturi) +- [ ] provider: improve UI: improve overall look, breadcrumbs, more responsive (@arturi) - [ ] website: new demo video / gif (@arturi) +- [x] core: css-in-js demos, try template-css (@arturi @goto-bus-stop #239) +- [x] core: add `uppy.reset()` as discussed in #179 (@arturi) - [x] core: add nanoraf https://github.com/yoshuawuyts/choo/pull/135/files?diff=unified (@goto-bus-stop, @arturi) - [x] core: file type detection: archives, markdown (possible modules: file-type, identify-filetype) example: http://requirebin.com/?gist=f9bea9602030f1320a227cf7f140c45f, http://stackoverflow.com/a/29672957 (@arturi) - [x] dashboard: make file icons prettier: https://uppy.io/images/blog/0.16/service-logos.png (@arturi, @nqst / #215) +- [x] fileinput: allow retriving fields/options from form (@arturi #153) +- [x] server: configurable server port (@ifedapoolarewaju) - [x] server: support for custom providers (@ifedapoolarewaju) - [x] statusbar: also show major errors, add “error” state (@goto-bus-stop) - [x] statusbar: pre/postprocessing status updates in the StatusBar (@goto-bus-stop, #202) +- [x] statusbar: show status “Upload started...” when the remote upload has begun, but no progress events received yet (@arturi) - [x] statusbar: work towards extracting StatusBar to a separate plugin, bundle that with Dashboard? (@goto-bus-stop, @arturi) - [x] tus/uppy-server: Support metadata in remote tus uploads (@ifedapoolarewaju, @goto-bus-stop / #210) +- [x] uploaders: add direct-to-s3 upload plugin and test it with the flow to then upload to transloadit, stage 1, WIP (@goto-bus-stop) +- [x] uppy/uppy-server: Make a barely working Instagram Plugin (@ifedapoolarewaju / #21) +- [x] uppy/uppy-server: Make a barely working Instagram Plugin (@ifedapoolarewaju / #21) - [x] uppy/uppy-server: allow google drive/dropbox non-tus (i.e multipart) remote uploads (@arturi, @ifedapoolarewaju / #205) -- [x] server: configurable server port (@ifedapoolarewaju) +- [x] uppy/uppy-server: some file types cannot be downloaded/uploaded on google drive (e.g google docs). How to handle that? (@ifedapoolarewaju) +- [x] uppy: fix google drive uploads on mobile (double click issue) (@arturi) ## 0.16.2 - 2017-05-31 diff --git a/examples/bundled-example/index.html b/examples/bundled-example/index.html index 50df14205..5255b3e57 100644 --- a/examples/bundled-example/index.html +++ b/examples/bundled-example/index.html @@ -7,12 +7,15 @@

Uppy is here

- +
-
+ + + +
diff --git a/examples/bundled-example/main.js b/examples/bundled-example/main.js index 0a5351862..a135e5247 100644 --- a/examples/bundled-example/main.js +++ b/examples/bundled-example/main.js @@ -2,9 +2,12 @@ const Uppy = require('../../src/core/Core.js') const Dashboard = require('../../src/plugins/Dashboard') const GoogleDrive = require('../../src/plugins/GoogleDrive') const Dropbox = require('../../src/plugins/Dropbox') -const Webcam = require('../../src/plugins/Webcam') +const Instagram = require('../../src/plugins/Instagram') +// const Webcam = require('../../src/plugins/Webcam') const Tus10 = require('../../src/plugins/Tus10') // const Multipart = require('../../src/plugins/Multipart') +// const DragDrop = require('../../src/plugins/FileInput') +// const FileInput = require('../../src/plugins/FileInput') const MetaData = require('../../src/plugins/MetaData') // const Informer = require('../../src/plugins/Informer') // const StatusBar = require('../../src/plugins/StatusBar') @@ -43,10 +46,12 @@ const uppy = Uppy({ trigger: '#uppyModalOpener', // maxWidth: 350, // maxHeight: 400, - // inline: false, + inline: false, // disableStatusBar: true, // disableInformer: true, - target: 'body', + setMetaFromTargetForm: true, + // replaceTargetContent: true, + target: '.MyForm', locale: { strings: {browse: 'wow'} }, @@ -54,6 +59,7 @@ const uppy = Uppy({ }) .use(GoogleDrive, {target: Dashboard, host: 'http://localhost:3020'}) .use(Dropbox, {target: Dashboard, host: 'http://localhost:3020'}) + .use(Instagram, {target: Dashboard, host: 'http://localhost:3020'}) // .use(FileInput, {target: '.Uppy', locale: { // strings: {selectToUpload: 'Выберите файл для загрузки'} // }}) @@ -61,8 +67,7 @@ const uppy = Uppy({ // strings: {chooseFile: 'Выберите файл'} // }}) // .use(ProgressBar, {target: 'body'}) - // .use(dummy) - .use(Webcam, {target: Dashboard}) + // .use(Webcam, {target: Dashboard}) // .use(Multipart, {endpoint: '//api2.transloadit.com'}) .use(Tus10, {endpoint: TUS_ENDPOINT, resume: true}) // .use(Informer, {target: Dashboard}) diff --git a/package-lock.json b/package-lock.json index 6f44cf3c3..84b4b9cac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3449,6 +3449,11 @@ "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", "dev": true }, + "get-form-data": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/get-form-data/-/get-form-data-1.2.5.tgz", + "integrity": "sha1-yQ+bix0tvbRulIDoi6xjKT/msYI=" + }, "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", diff --git a/package.json b/package.json index caec53a23..fe9c8a11f 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "dependencies": { "drag-drop": "2.13.2", "es6-promise": "3.2.1", + "get-form-data": "^1.2.5", "lodash.throttle": "4.1.1", "mime-match": "^1.0.2", "namespace-emitter": "1.0.0", diff --git a/src/core/Core.js b/src/core/Core.js index 9573c66a6..3ee900bde 100644 --- a/src/core/Core.js +++ b/src/core/Core.js @@ -86,7 +86,8 @@ class Uppy { capabilities: { resumableUploads: false }, - totalProgress: 0 + totalProgress: 0, + meta: Object.assign({}, this.opts.meta) } // for debugging and testing @@ -134,6 +135,14 @@ class Uppy { return this.state } + reset () { + this.emit('core:pause-all') + this.emit('core:cancel-all') + this.setState({ + totalProgress: 0 + }) + } + addPreProcessor (fn) { this.preProcessors.push(fn) } @@ -167,6 +176,13 @@ class Uppy { } } + setMeta (data) { + const newMeta = Object.assign({}, this.getState().meta, data) + this.log('Adding metadata:') + this.log(data) + this.setState({meta: newMeta}) + } + updateMeta (data, fileID) { const updatedFiles = Object.assign({}, this.getState().files) const newMeta = Object.assign({}, updatedFiles[fileID].meta, data) @@ -372,10 +388,9 @@ class Uppy { }) this.on('core:cancel-all', () => { - const files = this.getState().files - Object.keys(files).forEach((file) => { - this.removeFile(files[file].id) - }) + let updatedFiles = this.getState().files + updatedFiles = {} + this.setState({files: updatedFiles}) }) this.on('core:upload-started', (fileID, upload) => { @@ -588,6 +603,8 @@ class Uppy { * Uninstall all plugins and close down this Uppy instance. */ close () { + this.reset() + this.iteratePlugins((plugin) => { plugin.uninstall() }) @@ -606,16 +623,18 @@ class Uppy { if (!this.opts.debug) { return } + + if (type === 'error') { + console.error(`LOG: ${msg}`) + return + } + if (msg === `${msg}`) { console.log(`LOG: ${msg}`) } else { console.dir(msg) } - if (type === 'error') { - console.error(`LOG: ${msg}`) - } - global.uppyLog = global.uppyLog + '\n' + 'DEBUG LOG: ' + msg } diff --git a/src/generic-provider-views/Browser.js b/src/generic-provider-views/Browser.js index 13f883812..457b49d47 100644 --- a/src/generic-provider-views/Browser.js +++ b/src/generic-provider-views/Browser.js @@ -40,11 +40,11 @@ module.exports = (props) => { activeRow: props.isActiveRow, sortByTitle: props.sortByTitle, sortByDate: props.sortByDate, - // handleRowClick: props.handleRowClick, handleFileClick: props.addFile, handleFolderClick: props.getNextFolder, getItemName: props.getItemName, - getItemIcon: props.getItemIcon + getItemIcon: props.getItemIcon, + handleScroll: props.handleScroll })} diff --git a/src/generic-provider-views/Table.js b/src/generic-provider-views/Table.js index dc6b1aa7d..04ac8f08e 100644 --- a/src/generic-provider-views/Table.js +++ b/src/generic-provider-views/Table.js @@ -17,13 +17,12 @@ module.exports = (props) => { ${headers} - + ${props.folders.map((folder) => { return Row({ title: props.getItemName(folder), active: props.activeRow(folder), getItemIcon: () => props.getItemIcon(folder), - // handleClick: () => props.handleRowClick(folder), handleClick: () => props.handleFolderClick(folder), columns: props.columns }) @@ -33,7 +32,6 @@ module.exports = (props) => { title: props.getItemName(file), active: props.activeRow(file), getItemIcon: () => props.getItemIcon(file), - // handleClick: () => props.handleRowClick(file), handleClick: () => props.handleFileClick(file), columns: props.columns }) diff --git a/src/generic-provider-views/index.js b/src/generic-provider-views/index.js index 11801518e..24536f0eb 100644 --- a/src/generic-provider-views/index.js +++ b/src/generic-provider-views/index.js @@ -34,6 +34,8 @@ const Utils = require('../core/Utils') * @return {String} unique request path of the item when making calls to uppy server * getItemModifiedDate * @return {object} or {String} date of when last the item was modified + * getItemThumbnailUrl + * @return {String} */ module.exports = class View { /** @@ -49,7 +51,6 @@ module.exports = class View { this.filterQuery = this.filterQuery.bind(this) this.getFolder = this.getFolder.bind(this) this.getNextFolder = this.getNextFolder.bind(this) - // this.handleRowClick = this.handleRowClick.bind(this) this.logout = this.logout.bind(this) this.handleAuth = this.handleAuth.bind(this) this.handleDemoAuth = this.handleDemoAuth.bind(this) @@ -57,6 +58,7 @@ module.exports = class View { this.sortByDate = this.sortByDate.bind(this) this.isActiveRow = this.isActiveRow.bind(this) this.handleError = this.handleError.bind(this) + this.handleScroll = this.handleScroll.bind(this) // Visual this.render = this.render.bind(this) @@ -72,6 +74,18 @@ module.exports = class View { this.plugin.core.setState({[stateId]: Object.assign({}, state[stateId], newState)}) } + _updateFilesAndFolders (res, files, folders) { + this.plugin.getItemSubList(res).forEach((item) => { + if (this.plugin.isFolder(item)) { + folders.push(item) + } else { + files.push(item) + } + }) + + this.updateState({ folders, files }) + } + /** * Based on folder ID, fetch a new folder and update it to state * @param {String} id Folder id @@ -94,18 +108,8 @@ module.exports = class View { updatedDirectories = state.directories.concat([{id, title: name || this.plugin.getItemName(res)}]) } - this.plugin.getItemSubList(res).forEach((item) => { - if (this.plugin.isFolder(item)) { - folders.push(item) - } else { - files.push(item) - } - }) - - let data = {folders, files, directories: updatedDirectories} - this.updateState(data) - - return data + this._updateFilesAndFolders(res, files, folders) + this.updateState({ directories: updatedDirectories }) }, this.handleError) } @@ -124,7 +128,7 @@ module.exports = class View { const tagFile = { source: this.plugin.id, data: this.plugin.getItemData(file), - name: this.plugin.getItemName(file), + name: this.plugin.getItemName(file) || this.plugin.getItemId(file), type: this.plugin.getMimeType(file), isRemote: true, body: { @@ -141,7 +145,7 @@ module.exports = class View { Utils.getFileType(tagFile).then(fileType => { if (Utils.isPreviewSupported(fileType[1])) { - tagFile.preview = `${this.plugin.opts.host}/${this.Provider.id}/thumbnail/${this.plugin.getItemRequestPath(file)}` + tagFile.preview = this.plugin.getItemThumbnailUrl(file) } this.plugin.core.log('Adding remote file') this.plugin.core.emitter.emit('core:file-add', tagFile) @@ -167,19 +171,6 @@ module.exports = class View { }).catch(this.handleError) } - /** - * Used to set active file/folder. - * @param {Object} file Active file/folder - */ - // handleRowClick (file) { - // const state = this.plugin.core.getState()[this.plugin.stateId] - // const newState = Object.assign({}, state, { - // activeRow: this.plugin.getItemId(file) - // }) - - // this.updateState(newState) - // } - filterQuery (e) { const state = this.plugin.core.getState()[this.plugin.stateId] this.updateState(Object.assign({}, state, { @@ -297,17 +288,27 @@ module.exports = class View { this.updateState({ error }) } + handleScroll (e) { + const scrollPos = e.target.scrollHeight - (e.target.scrollTop + e.target.offsetHeight) + const path = this.plugin.getNextPagePath ? this.plugin.getNextPagePath() : null + + if (scrollPos < 50 && path && !this._isHandlingScroll) { + this.Provider.list(path) + .then((res) => { + const { files, folders } = this.plugin.core.getState()[this.plugin.stateId] + this._updateFilesAndFolders(res, files, folders) + }).catch(this.handleError) + .then(() => { this._isHandlingScroll = false }) // always called + + this._isHandlingScroll = true + } + } + // displays loader view while asynchronous request is being made. _loaderWrapper (promise, then, catch_) { promise - .then((result) => { - this.updateState({ loading: false }) - then(result) - }) - .catch((err) => { - this.updateState({ loading: false }) - catch_(err) - }) + .then(then).catch(catch_) + .then(() => this.updateState({ loading: false })) // always called. this.updateState({ loading: true }) } @@ -338,14 +339,14 @@ module.exports = class View { addFile: this.addFile, filterItems: this.filterItems, filterQuery: this.filterQuery, - handleRowClick: this.handleRowClick, sortByTitle: this.sortByTitle, sortByDate: this.sortByDate, logout: this.logout, demo: this.plugin.opts.demo, isActiveRow: this.isActiveRow, getItemName: this.plugin.getItemName, - getItemIcon: this.plugin.getItemIcon + getItemIcon: this.plugin.getItemIcon, + handleScroll: this.handleScroll }) return Browser(browserProps) diff --git a/src/index.js b/src/index.js index 202c62d99..8d1503bb8 100644 --- a/src/index.js +++ b/src/index.js @@ -12,6 +12,7 @@ const DragDrop = require('./plugins/DragDrop/index.js') const FileInput = require('./plugins/FileInput.js') const GoogleDrive = require('./plugins/GoogleDrive/index.js') const Dropbox = require('./plugins/Dropbox/index.js') +const Instagram = require('./plugins/Instagram/index.js') const Webcam = require('./plugins/Webcam/index.js') // Progressindicators @@ -35,6 +36,7 @@ module.exports = { DragDrop, GoogleDrive, Dropbox, + Instagram, FileInput, Tus10, Multipart, diff --git a/src/plugins/Dashboard/Dashboard.js b/src/plugins/Dashboard/Dashboard.js index f9be2f824..d20333625 100644 --- a/src/plugins/Dashboard/Dashboard.js +++ b/src/plugins/Dashboard/Dashboard.js @@ -58,6 +58,7 @@ module.exports = function Dashboard (props) { onload=${() => props.updateDashboardElWidth()}> @@ -135,6 +136,7 @@ module.exports = function Dashboard (props) { ${props.i18n('importFrom')} ${props.activePanel ? props.activePanel.name : null} ${props.activePanel ? props.activePanel.render(props.state) : ''} diff --git a/src/plugins/Dashboard/FileCard.js b/src/plugins/Dashboard/FileCard.js index 2d6f0e751..5e7fd3775 100644 --- a/src/plugins/Dashboard/FileCard.js +++ b/src/plugins/Dashboard/FileCard.js @@ -29,7 +29,7 @@ module.exports = function fileCard (props) { return html`

Editing ${file.meta ? file.meta.name : file.name}

-
${props.fileCardFor diff --git a/src/plugins/Dashboard/FileItem.js b/src/plugins/Dashboard/FileItem.js index f0d6ef8d5..ee53249de 100644 --- a/src/plugins/Dashboard/FileItem.js +++ b/src/plugins/Dashboard/FileItem.js @@ -49,6 +49,7 @@ module.exports = function fileItem (props) {
@@ -24,6 +25,7 @@ Webcam: document.querySelector('#opts-Webcam'), GoogleDrive: document.querySelector('#opts-GoogleDrive'), Dropbox: document.querySelector('#opts-Dropbox'), + Instagram: document.querySelector('#opts-Instagram'), autoProceed: document.querySelector('#opts-autoProceed') } @@ -31,6 +33,7 @@ DashboardInline: false, Webcam: true, GoogleDrive: true, + Instagram: true, Dropbox: false, autoProceed: false }