Skip to content

Commit

Permalink
fix: get drop dir working again (#1011)
Browse files Browse the repository at this point in the history
- replace our directory walking code with https://github.com/grabantot/datatransfer-files-promise#readme
- normalise the logic so adding via fileinput and drop both go
through the filesToStreams logic.


License: MIT
Signed-off-by: Oli Evans <oli@tableflip.io>
  • Loading branch information
olizilla authored Apr 24, 2019
1 parent ee3764d commit 50b5535
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 110 deletions.
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"cids": "^0.6.0",
"countly-sdk-web": "^19.2.1",
"d3": "^5.7.0",
"datatransfer-files-promise": "^1.2.0",
"details-polyfill": "^1.1.0",
"file-extension": "^4.0.5",
"filesize": "^3.6.1",
Expand Down
8 changes: 3 additions & 5 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class App extends Component {
this.props.doInitIpfs()
}

addFiles = (files) => {
addFiles = async (files) => {
const { doFilesWrite, doUpdateHash, routeInfo } = this.props

// Add the dropped files to the root
Expand Down Expand Up @@ -84,10 +84,8 @@ const dropTarget = {
if (monitor.didDrop()) {
return
}

const item = monitor.getItem()

App.addFiles(item)
const { filesPromise } = monitor.getItem()
App.addFiles(filesPromise)
}
}

Expand Down
31 changes: 19 additions & 12 deletions src/bundles/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const make = (basename, action) => (...args) => async (args2) => {
await store.doUpdateHash(`/files${path}`)
}
} catch (error) {
console.log(error)
dispatch({ type: `FILES_${basename}_FAILED`, payload: { id, error } })
} finally {
if (basename !== actions.FETCH) {
Expand Down Expand Up @@ -179,28 +180,27 @@ export default (opts = {}) => {
]
}
} else if (status === 'UPDATED') {
const action = state.pending.find(a => a.id === id)
const pendingAction = state.pending.find(a => a.id === id)

return {
...state,
pending: [
...state.pending.filter(a => a.id !== id),
{
...action,
...pendingAction,
data: data
}
]
}
} else if (status === 'FAILED') {
const action = state.pending.find(a => a.id === id)

const pendingAction = state.pending.find(a => a.id === id)
return {
...state,
pending: state.pending.filter(a => a.id !== id),
failed: [
...state.failed,
{
...action,
...pendingAction,
end: Date.now(),
error: data.error
}
Expand Down Expand Up @@ -245,8 +245,9 @@ export default (opts = {}) => {
}
},

doFilesWrite: make(actions.WRITE, async (ipfs, root, rawFiles, id, { dispatch }) => {
const { streams, totalSize } = await filesToStreams(rawFiles)
doFilesWrite: make(actions.WRITE, async (ipfs, root, filesOrPromise, id, { dispatch }) => {
let files = await filesOrPromise
const { streams, totalSize } = await filesToStreams(files)

// Normalise all paths to be relative. Dropped files come as absolute,
// those added by the file input come as relative paths, so normalise them.
Expand All @@ -262,11 +263,17 @@ export default (opts = {}) => {

updateProgress(0)

const res = await ipfs.add(streams, {
pin: false,
wrapWithDirectory: false,
progress: updateProgress
})
let res = null
try {
res = await ipfs.add(streams, {
pin: false,
wrapWithDirectory: false,
progress: updateProgress
})
} catch (error) {
console.error(error)
throw error
}

const numberOfFiles = streams.length
const numberOfDirs = countDirs(streams)
Expand Down
2 changes: 1 addition & 1 deletion src/files/file/File.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ const dropTarget = {
const item = monitor.getItem()

if (item.hasOwnProperty('files')) {
props.onAddFiles(item, props.path)
props.onAddFiles(item.filesPromise, props.path)
} else {
const src = item.path
const dst = join(props.path, basename(item.path))
Expand Down
6 changes: 2 additions & 4 deletions src/files/files-list/FilesList.js
Original file line number Diff line number Diff line change
Expand Up @@ -495,10 +495,8 @@ const dropTarget = {
if (monitor.didDrop()) {
return
}

const item = monitor.getItem()

onAddFiles(item)
const { filesPromise } = monitor.getItem()
onAddFiles(filesPromise)
}
}

Expand Down
109 changes: 28 additions & 81 deletions src/lib/dnd-backend.js
Original file line number Diff line number Diff line change
@@ -1,93 +1,40 @@
import { getFilesFromDataTransferItems } from 'datatransfer-files-promise'
import HTML5Backend from 'react-dnd-html5-backend'
import fileReader from 'pull-file-reader'
import { join } from 'path'

const getFileFromEntry = (entry) => new Promise((resolve, reject) => {
if (entry.file) {
entry.file(resolve, reject)
} else {
resolve(null)
}
}).catch(() => null)

const getAsEntry = (item) => item.getAsEntry
? item.getAsEntry()
: item.webkitGetAsEntry
? item.webkitGetAsEntry()
: null

const readEntries = (reader) => new Promise((resolve, reject) => {
reader.readEntries(resolve, reject)
})

async function scanFiles (item, root = '') {
if (!item.isDirectory) {
const file = await getFileFromEntry(item)
const path = item.fullPath
return [{
path,
content: fileReader(file),
size: file.size
}]
}

let files = []
const reader = item.createReader()
const entries = await readEntries(reader)

for (const entry of entries) {
files = files.concat(await scanFiles(entry, join(root, item.name)))
}

return files
}

async function scan (files) {
let entries = []
let streams = []
let totalSize = 0

for (const file of files) {
if (file.getAsEntry || file.webkitGetAsEntry) {
entries.push({
entry: getAsEntry(file),
path: file.name
})
} else {
totalSize += file.size

streams.push({
path: file.webkitRelativePath || file.name,
content: fileReader(file),
size: file.size
})
}
}

for (const entry of entries) {
const res = await scanFiles(entry.entry, entry.name)

for (const stream of res) {
totalSize += stream.size
}

streams = streams.concat(res)
}

return { streams, totalSize, isDir: false }
}

// If you drop a dir "foo" which contains "cat.jpg" & "dog.png" we receive a
// single item in the `event.dataTransfer.items` for the directory.
//
// We use 'datatransfer-files-promise' to map the dir tree to a flat list of
// FileSystemEntry objects, each with a filepath propety, that captures the
// files relative path within the dir that was dropped.
//
// so the "foo" becomes:
// [
// { filepath: 'foo/cat.jpg' name: 'cat.jpg', size: Number },
// { filepath: 'foo/dog.png' name: 'dog.png', size: Number }
// ]
//
// Which is a useful shape for passing to ipfs.add, with the caveat that each
// FileSystemEntry object must be passed to pull-file-reader or similar to
// convert to a stream style that ipfs.add accepts.
//
// ReactDnD doesn't give the calling code access to the `event.dataTransfer.items`
// so we have to work around it here by plugin a custom drop handler that does
// the work to map from a dir entry to a flat list of files and then stash it on
// a custom `filesPromise` prop on the return object, which we check for in our
// dropTarget drop handler functions.
//
// See: https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/webkitGetAsEntry
// See: https://github.com/grabantot/datatransfer-files-promise/blob/72b6cc763f9b400c59197bcc787268965310c260/index.js
export default (manager) => {
const backend = HTML5Backend(manager)
const handler = backend.handleTopDropCapture

backend.handleTopDropCapture = (event) => {
handler.call(backend, event)

if (backend.currentNativeSource) {
backend.currentNativeSource.item.content = scan(event.dataTransfer.items)
const filesPromise = getFilesFromDataTransferItems(event.dataTransfer.items)
backend.currentNativeSource.item.filesPromise = filesPromise
}
}

return backend
}
8 changes: 1 addition & 7 deletions src/lib/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ const ignore = [
]

export async function filesToStreams (files) {
if (files.hasOwnProperty('content')) {
// this is a promise
return files.content
}

const streams = []
let totalSize = 0
let isDir = false
Expand All @@ -29,14 +24,13 @@ export async function filesToStreams (files) {
}

streams.push({
path: file.webkitRelativePath || file.name,
path: file.filepath || file.webkitRelativePath || file.name,
content: stream,
size: file.size
})

totalSize += file.size
}

return { streams, totalSize, isDir }
}

Expand Down

0 comments on commit 50b5535

Please sign in to comment.