Skip to content

Commit

Permalink
fix: drag & drop uploads
Browse files Browse the repository at this point in the history
  • Loading branch information
lidel committed Aug 6, 2018
1 parent 3e8ec34 commit d8fd3b3
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 81 deletions.
181 changes: 100 additions & 81 deletions add-on/src/popup/quick-upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const browser = require('webextension-polyfill')
const choo = require('choo')
const html = require('choo/html')
const logo = require('./logo')
const drop = require('drag-and-drop-files')
const fileReaderPullStream = require('filereader-pull-stream')
const filesize = require('filesize')

Expand All @@ -16,6 +17,98 @@ app.use(quickUploadStore)
app.route('*', quickUploadPage)
app.mount('#root')

function quickUploadStore (state, emitter) {
state.message = ''
state.peerCount = ''
state.ipfsNodeType = 'external'
state.wrapWithDirectory = true
state.pinUpload = true
state.expandOptions = false

function updateState ({ipfsNodeType, peerCount}) {
state.ipfsNodeType = ipfsNodeType
state.peerCount = peerCount
}

let port

emitter.on('DOMContentLoaded', async () => {
// initialize connection to the background script which will trigger UI updates
port = browser.runtime.connect({name: 'browser-action-port'})
port.onMessage.addListener(async (message) => {
if (message.statusUpdate) {
console.log('In browser action, received message from background:', message)
updateState(message.statusUpdate)
emitter.emit('render')
}
})
})

emitter.on('fileInputChange', event => processFiles(state, emitter, event.target.files))

// drag & drop anywhere
drop(document.body, files => processFiles(state, emitter, files))
}

async function processFiles (state, emitter, files) {
console.log('Processing files', files)
try {
if (!files.length) {
// File list may be empty in some rare cases
// eg. when user drags something from proprietary browser context
// We just ignore those UI interactions.
throw new Error('found no valid sources, try selecting a local file instead')
}
const { ipfsCompanion } = await browser.runtime.getBackgroundPage()
const uploadTab = await browser.tabs.getCurrent()
let {streams, totalSize} = files2streams(files)
if (!browser.runtime.id.includes('@')) {
// we are in non-Firefox runtime (we know for a fact that Chrome puts no @ in id)
if (state.ipfsNodeType === 'external' && totalSize >= 134217728) {
// avoid crashing Chrome until the source of issue is fixed in js-ipfs-api
// - https://github.com/ipfs-shipyard/ipfs-companion/issues/464
// - https://github.com/ipfs/js-ipfs-api/issues/654
throw new Error('Unable to process payload bigger than 128MiB in Chrome. See: js-ipfs-api/issues/654')
}
}
progressHandler(0, totalSize, state, emitter)
emitter.emit('render')
const wrapFlag = (state.wrapWithDirectory || streams.length > 1)
const options = {
progress: (len) => progressHandler(len, totalSize, state, emitter),
wrapWithDirectory: wrapFlag,
pin: state.pinUpload
}
let result
try {
result = await ipfsCompanion.ipfs.files.add(streams, options)
// This is just an additional safety check, as in past combination
// of specific go-ipfs/js-ipfs-api versions
// produced silent errors in form of partial responses:
// https://github.com/ipfs-shipyard/ipfs-companion/issues/480
const partialResponse = result.length !== streams.length + (options.wrapWithDirectory ? 1 : 0)
if (partialResponse) {
throw new Error('Result of ipfs.files.add call is missing entries. This may be due to a bug in HTTP API similar to https://github.com/ipfs/go-ipfs/issues/5168')
}
await ipfsCompanion.uploadResultHandler({result, openRootInNewTab: true})
} catch (err) {
console.error('Failed to IPFS add', err)
ipfsCompanion.notify('notify_uploadErrorTitle', 'notify_inlineErrorMsg', `${err.message}`)
throw err
}
emitter.emit('render')
console.log('Upload result', result)
// close upload tab as it will be replaced with a new tab with uploaded content
await browser.tabs.remove(uploadTab.id)
} catch (err) {
console.error('Unable to perform quick upload', err)
// keep upload tab and display error message in it
state.message = `Unable to upload to IPFS API:`
state.progress = `${err}`
emitter.emit('render')
}
}

/* disabled in favor of fileReaderPullStream
function file2buffer (file) {
return new Promise((resolve, reject) => {
Expand All @@ -30,6 +123,12 @@ function files2streams (files) {
const streams = []
let totalSize = 0
for (let file of files) {
if (!file.type && file.size === 0) {
// UX fail-safe:
// at the moment drag&drop of an empty file without an extension
// looks the same as dropping a directory
throw new Error(`unable to add "${file.name}", directories and empty files are not supported`)
}
const fileStream = fileReaderPullStream(file, {chunkSize: 32 * 1024 * 1024})
streams.push({
path: file.name,
Expand Down Expand Up @@ -57,86 +156,6 @@ function progressHandler (doneBytes, totalBytes, state, emitter) {
emitter.emit('render')
}

function quickUploadStore (state, emitter) {
state.message = ''
state.peerCount = ''
state.ipfsNodeType = 'external'
state.wrapWithDirectory = true
state.pinUpload = true
state.expandOptions = false

function updateState ({ipfsNodeType, peerCount}) {
state.ipfsNodeType = ipfsNodeType
state.peerCount = peerCount
}

let port

emitter.on('DOMContentLoaded', async () => {
// initialize connection to the background script which will trigger UI updates
port = browser.runtime.connect({name: 'browser-action-port'})
port.onMessage.addListener(async (message) => {
if (message.statusUpdate) {
console.log('In browser action, received message from background:', message)
updateState(message.statusUpdate)
emitter.emit('render')
}
})
})

emitter.on('fileInputChange', async (event) => {
try {
const { ipfsCompanion } = await browser.runtime.getBackgroundPage()
const uploadTab = await browser.tabs.getCurrent()
let {streams, totalSize} = files2streams(event.target.files)
if (!browser.runtime.id.includes('@')) {
// we are in non-Firefox runtime (we know for a fact that Chrome puts no @ in id)
if (state.ipfsNodeType === 'external' && totalSize >= 134217728) {
// avoid crashing Chrome until the source of issue is fixed in js-ipfs-api
// - https://github.com/ipfs-shipyard/ipfs-companion/issues/464
// - https://github.com/ipfs/js-ipfs-api/issues/654
throw new Error('Unable to process payload bigger than 128MiB in Chrome. See: js-ipfs-api/issues/654')
}
}
progressHandler(0, totalSize, state, emitter)
emitter.emit('render')
const wrapFlag = (state.wrapWithDirectory || streams.length > 1)
const options = {
progress: (len) => progressHandler(len, totalSize, state, emitter),
wrapWithDirectory: wrapFlag,
pin: state.pinUpload
}
let result
try {
result = await ipfsCompanion.ipfs.files.add(streams, options)
// This is just an additional safety check, as in past combination
// of specific go-ipfs/js-ipfs-api versions
// produced silent errors in form of partial responses:
// https://github.com/ipfs-shipyard/ipfs-companion/issues/480
const partialResponse = result.length !== streams.length + (options.wrapWithDirectory ? 1 : 0)
if (partialResponse) {
throw new Error('Result of ipfs.files.add call is missing entries. This may be due to a bug in HTTP API similar to https://github.com/ipfs/go-ipfs/issues/5168')
}
await ipfsCompanion.uploadResultHandler({result, openRootInNewTab: true})
} catch (err) {
console.error('Failed to IPFS add', err)
ipfsCompanion.notify('notify_uploadErrorTitle', 'notify_inlineErrorMsg', `${err.message}`)
throw err
}
emitter.emit('render')
console.log('Upload result', result)
// close upload tab as it will be replaced with a new tab with uploaded content
await browser.tabs.remove(uploadTab.id)
} catch (err) {
console.error('Unable to perform quick upload', err)
// keep upload tab and display error message in it
state.message = `Unable to upload to IPFS API:`
state.progress = `${err}`
emitter.emit('render')
}
})
}

function quickUploadOptions (state, emit) {
const onExpandOptions = (e) => { state.expandOptions = true; emit('render') }
const onWrapWithDirectoryChange = (e) => { state.wrapWithDirectory = e.target.checked }
Expand Down Expand Up @@ -186,7 +205,7 @@ function quickUploadPage (state, emit) {
</p>
</div>
</header>
<label for="quickUploadInput" class='db relative mt5 hover-inner-shadow' style="border:solid 2px #6ACAD1">
<label for="quickUploadInput" class='db relative mt5 hover-inner-shadow pointer' style="border:solid 2px #6ACAD1">
<input class="db pointer w-100 h-100 top-0 o-0" type="file" id="quickUploadInput" multiple onchange=${onFileInputChange} />
<div class='dt dim' style='padding-left: 100px; height: 300px'>
<div class='dtc v-mid'>
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"dependencies": {
"choo": "6.13.0",
"doc-sniff": "1.0.1",
"drag-and-drop-files": "0.0.1",
"file-type": "8.1.0",
"filereader-pull-stream": "1.0.0",
"filesize": "3.6.1",
Expand Down
4 changes: 4 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2778,6 +2778,10 @@ dot-prop@^4.1.0:
dependencies:
is-obj "^1.0.0"

drag-and-drop-files@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/drag-and-drop-files/-/drag-and-drop-files-0.0.1.tgz#adcb1dd0e9d01d510da8131909e47c43e05c3c02"

drbg.js@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b"
Expand Down

0 comments on commit d8fd3b3

Please sign in to comment.