Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 126 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ import {
listView
} from '@osjs/gui';


/**
* flag indicating whether uploading folders is supported
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you capitalize this comment? Other than that, looks great!

*/
const supportsUploadingFolders = core =>
core.config('filemanager.disableMultiUpload', false) !== true
&& !!(window.DataTransferItem && DataTransferItem.prototype.webkitGetAsEntry);

/**
* Creates default settings
*/
Expand All @@ -66,7 +74,8 @@ const createWindowOptions = (core, proc, title) => ({
attributes: {
mediaQueries: {
small: 'screen and (max-width: 400px)'
}
},
droppable: {dataTransferProperty: supportsUploadingFolders(core) ? 'items' : 'files'},
},
dimension: Object.assign({
width: 400,
Expand Down Expand Up @@ -284,7 +293,7 @@ const vfsActionFactory = (core, proc, win, dialog, state) => {
};

const writeRelative = f => {
const d = dialog('progress', f);
const d = dialog('progress', f.name);

return vfs.writefile({
path: pathJoin(state.currentPath.path, f.name)
Expand All @@ -300,12 +309,122 @@ const vfsActionFactory = (core, proc, win, dialog, state) => {
});
};

const uploadBrowserFiles = (files) => {
const legacyUploadBrowserFiles = (files) => {
Promise.all(files.map(writeRelative))
.then(() => refresh(files[0].name)) // FIXME: Select all ?
.catch(error => dialog('error', error, __('MSG_UPLOAD_ERROR')));
};

const getUploadList = async (items) => {
/*
type: [{dirPath: string, file?: any}]
Directories do not have the `file` property.
They only have their relative path stored in `dirPath`.
Files store their containing folders path as their `dirPath`.
*/
const uploadList = [];

const getDirectoryEntries = (directory) => {
const reader = directory.createReader();
return new Promise(resolve => {
reader.readEntries(async (entries) => {
resolve(entries);
});
});
};

const getFileFromEntry = (entry) => {
return new Promise((resolve, reject) => {
entry.file((file) => {
resolve(file);
}, (error) => {
reject(error);
});
});
};

const checkDirectory = async (directory, dirPath) => {
uploadList.push({dirPath});
const entries = await getDirectoryEntries(directory);
for (let entry of entries) {
if (entry.isFile) {
const file = await getFileFromEntry(entry);
uploadList.push({dirPath, file});
} else if (entry.isDirectory) {
const subDirPath = dirPath + '/' + entry.name;
await checkDirectory(entry, subDirPath);
}
}
};

try {
for (let item of items) {
const entry = item.webkitGetAsEntry();
if (entry.isFile) {
const file = item.getAsFile();
const dirPath = '';
uploadList.push({dirPath, file});
} else if (entry.isDirectory) {
await checkDirectory(entry, entry.name);
}
}
} catch (error) {
console.warn(error);
}

return uploadList;
};

const uploadFileAndFolderList = async (list) => {
const files = list.map(({file}) => file).filter((file) => file);
const totalSize = files.reduce((sum, {size}) => sum + size, 0);
const dialogTitle = files.length === 1 ? files[0].name : 'multiple files';
const abortController = new AbortController();
let isCancelled = false;
const onCancel = () => {
isCancelled = false;
abortController.abort();
};
const d = dialog('progress', dialogTitle, onCancel);
try {
let uploaded = 0;
for (let {dirPath, file} of list) {
if (isCancelled) {
return;
}
if (file) {
// upload file
await vfs.writefile({
path: pathJoin(state.currentPath.path, dirPath, file.name)
}, file, {
signal: abortController.signal,
pid: proc.pid,
onProgress: (ev, progress) => {
d.setProgress(Math.round((uploaded + progress * file.size / 100) * 100 / totalSize));
}
});
uploaded += file.size;
} else {
// create folder
await vfs.mkdir({path: pathJoin(state.currentPath.path, dirPath)}, {pid: proc.pid});
}
}
} catch (error) {
dialog('error', error, __('MSG_UPLOAD_ERROR'));
}
d.destroy();
};

const uploadBrowserFiles = async (items) => {
if (!supportsUploadingFolders(core)) {
return legacyUploadBrowserFiles(items);
}

const uploadList = await getUploadList(items);
await uploadFileAndFolderList(uploadList);
refresh(items[0].name); // FIXME: Select all ?
};

const uploadVirtualFile = (data) => {
const dest = {path: pathJoin(state.currentPath.path, data.filename)};
if (dest.path !== data.path) {
Expand Down Expand Up @@ -449,10 +568,10 @@ const dialogFactory = (core, proc, win) => {
action(() => vfs.unlink(file, {pid: proc.pid}), true, __('MSG_DELETE_ERROR'));
}));

const progressDialog = (file) => dialog('progress', {
message: __('DIALOG_PROGRESS_MESSAGE', file.name),
buttons: []
}, () => {}, false);
const progressDialog = (name, cb = (() => {})) => dialog('progress', {
message: __('DIALOG_PROGRESS_MESSAGE', name),
buttons: ['Cancel']
}, cb, false);

const errorDialog = (error, message) => dialog('alert', {
type: 'error',
Expand Down