From 3fdf5d7d8a7f747ab783ec3654c16b35196358f4 Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Wed, 6 Nov 2019 20:50:50 -0500 Subject: [PATCH 01/28] upload files to MFS --- add-on/_locales/en/messages.json | 6 +-- add-on/src/lib/ipfs-companion.js | 10 +++++ add-on/src/popup/quick-upload.js | 70 ++++++++------------------------ 3 files changed, 31 insertions(+), 55 deletions(-) diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index 841ed34f6..70f7eb8c7 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -475,9 +475,9 @@ "message": "upload options", "description": "Button on the share files page (quickUpload_options_show)" }, - "quickUpload_options_wrapWithDirectory": { - "message": "Wrap single files in a directory to preserve their filenames.", - "description": "Checkbox label on the share files page (quickUpload_options_wrapWithDirectory)" + "quickUpload_options_uploadDir": { + "message": "MFS directory to store uploaded files", + "description": "Textbox label on the share files page (quickUpload_options_wrapWithDirectory)" }, "quickUpload_options_pinUpload": { "message": "Pin files so they are retained when performing garbage collection on your IPFS repo.", diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 8ec0c36e2..be6b1bd48 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -367,6 +367,12 @@ module.exports = async function init () { return result } + async function openWebUiAtDirectory (dir) { + await browser.tabs.create({ + url: `${state.webuiRootUrl}#/files${dir}` + }) + } + // Page-specific Actions // ------------------------------------------------------------------- @@ -787,6 +793,10 @@ module.exports = async function init () { return uploadResultHandler }, + get openWebUiAtDirectory () { + return openWebUiAtDirectory + }, + destroy () { const destroyTasks = [] clearInterval(apiStatusUpdateInterval) diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index 098f71ec4..2ef83ff1a 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -7,7 +7,6 @@ const html = require('choo/html') const logo = require('./logo') const drop = require('drag-and-drop-files') const fileReaderPullStream = require('pull-file-reader') -const filesize = require('filesize') document.title = browser.i18n.getMessage('panel_quickUpload') @@ -21,9 +20,8 @@ function quickUploadStore (state, emitter) { state.message = '' state.peerCount = '' state.ipfsNodeType = 'external' - state.wrapWithDirectory = true - state.pinUpload = true state.expandOptions = false + state.uploadDir = '/ipfs-companion-uploads/' function updateState ({ ipfsNodeType, peerCount }) { state.ipfsNodeType = ipfsNodeType @@ -61,34 +59,27 @@ async function processFiles (state, emitter, files) { } const { ipfsCompanion } = await browser.runtime.getBackgroundPage() const uploadTab = await browser.tabs.getCurrent() - const { streams, totalSize } = files2streams(files) - progressHandler(0, totalSize, state, emitter) + const streams = files2streams(files) emitter.emit('render') - const wrapFlag = (state.wrapWithDirectory || streams.length > 1) const options = { - progress: (len) => progressHandler(len, totalSize, state, emitter), - wrapWithDirectory: wrapFlag, - pin: state.pinUpload + create: true, + parents: true } - let result + state.progress = `Uploading ${streams.length} files...` try { - result = await ipfsCompanion.ipfs.add(streams, options) - // This is just an additional safety check, as in past combination - // of specific go-ipfs/js-ipfs-http-client 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.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 }) + const files = streams.map(stream => (ipfsCompanion.ipfs.files.write(`${state.uploadDir}${stream.path}`, stream.content, options))) + await Promise.all(files) + await ipfsCompanion.openWebUiAtDirectory(state.uploadDir) } catch (err) { - console.error('Failed to IPFS add', err) + console.error('Failed to upload files to IPFS', err) ipfsCompanion.notify('notify_uploadErrorTitle', 'notify_inlineErrorMsg', `${err.message}`) throw err } + state.progress = 'Completed' emitter.emit('render') - console.log('Upload result', result) + console.log(`Successfully uploaded ${streams.length} files`) + // function to open web UI at state.uploadDir + // await ipfsCompanion.uploadHandler(state.uploadDir) // close upload tab as it will be replaced with a new tab with uploaded content await browser.tabs.remove(uploadTab.id) } catch (err) { @@ -112,7 +103,6 @@ function file2buffer (file) { function files2streams (files) { const streams = [] - let totalSize = 0 for (const file of files) { if (!file.type && file.size === 0) { // UX fail-safe: @@ -125,44 +115,20 @@ function files2streams (files) { path: file.name, content: fileStream }) - totalSize += file.size - } - return { streams, totalSize } -} - -function progressHandler (doneBytes, totalBytes, state, emitter) { - state.message = browser.i18n.getMessage('quickUpload_state_uploading') - // console.log('Upload progress:', doneBytes) - if (doneBytes && doneBytes > 0) { - const format = { standard: 'iec', round: 0, output: 'object' } - const done = filesize(doneBytes, format) - const total = filesize(totalBytes, format) - const percent = ((doneBytes / totalBytes) * 100).toFixed(0) - state.progress = `${done.value} ${done.symbol} / ${total.value} ${total.symbol} (${percent}%)` - } else { - // This is a gracefull fallback for environments in which progress reporting is delayed - // until entire file/chunk is bufferend into memory (eg. js-ipfs-http-client) - state.progress = browser.i18n.getMessage('quickUpload_state_buffering') } - emitter.emit('render') + return streams } function quickUploadOptions (state, emit) { const onExpandOptions = (e) => { state.expandOptions = true; emit('render') } - const onWrapWithDirectoryChange = (e) => { state.wrapWithDirectory = e.target.checked } - const onPinUploadChange = (e) => { state.pinUpload = e.target.checked } + const onDirectoryChange = (e) => { state.uploadDir = e.target.value } if (state.expandOptions) { return html`
- -
` From 6925e14377178b989f7add71a9ab51ef29d3c090 Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Wed, 6 Nov 2019 22:08:57 -0500 Subject: [PATCH 02/28] format directory path properly on upload --- add-on/src/popup/quick-upload.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index 2ef83ff1a..6781ebe5f 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -66,10 +66,11 @@ async function processFiles (state, emitter, files) { parents: true } state.progress = `Uploading ${streams.length} files...` + const uploadDir = state.uploadDir.replace(/\/$|$/, '/') try { - const files = streams.map(stream => (ipfsCompanion.ipfs.files.write(`${state.uploadDir}${stream.path}`, stream.content, options))) + const files = streams.map(stream => (ipfsCompanion.ipfs.files.write(`${uploadDir}${stream.path}`, stream.content, options))) await Promise.all(files) - await ipfsCompanion.openWebUiAtDirectory(state.uploadDir) + await ipfsCompanion.openWebUiAtDirectory(uploadDir) } catch (err) { console.error('Failed to upload files to IPFS', err) ipfsCompanion.notify('notify_uploadErrorTitle', 'notify_inlineErrorMsg', `${err.message}`) From 12ebff86765996176dea1fdb6103787efc436e35 Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Thu, 7 Nov 2019 08:05:45 -0500 Subject: [PATCH 03/28] remove pinUpload description text --- add-on/_locales/en/messages.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index 70f7eb8c7..5bbfd65ea 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -479,10 +479,6 @@ "message": "MFS directory to store uploaded files", "description": "Textbox label on the share files page (quickUpload_options_wrapWithDirectory)" }, - "quickUpload_options_pinUpload": { - "message": "Pin files so they are retained when performing garbage collection on your IPFS repo.", - "description": "Checkbox label on the share files page (quickUpload_options_pinUpload)" - }, "page_proxyAcl_title": { "message": "Manage Permissions", "description": "Page title for the IPFS proxy ACL page (page_proxyAcl_title)" From 7d07ef384bdff6044292515973bc73f3f599b32b Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Sun, 10 Nov 2019 09:57:35 -0500 Subject: [PATCH 04/28] remove unnecessary comment --- add-on/src/popup/quick-upload.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index 6781ebe5f..c56e60a30 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -79,8 +79,6 @@ async function processFiles (state, emitter, files) { state.progress = 'Completed' emitter.emit('render') console.log(`Successfully uploaded ${streams.length} files`) - // function to open web UI at state.uploadDir - // await ipfsCompanion.uploadHandler(state.uploadDir) // close upload tab as it will be replaced with a new tab with uploaded content await browser.tabs.remove(uploadTab.id) } catch (err) { From 790421ef72701acd592bbf286d6c866c41eb55d2 Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Sun, 10 Nov 2019 10:03:53 -0500 Subject: [PATCH 05/28] move open web UI call out of file upload block --- add-on/src/popup/quick-upload.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index c56e60a30..6b59efce3 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -70,7 +70,6 @@ async function processFiles (state, emitter, files) { try { const files = streams.map(stream => (ipfsCompanion.ipfs.files.write(`${uploadDir}${stream.path}`, stream.content, options))) await Promise.all(files) - await ipfsCompanion.openWebUiAtDirectory(uploadDir) } catch (err) { console.error('Failed to upload files to IPFS', err) ipfsCompanion.notify('notify_uploadErrorTitle', 'notify_inlineErrorMsg', `${err.message}`) @@ -79,6 +78,9 @@ async function processFiles (state, emitter, files) { state.progress = 'Completed' emitter.emit('render') console.log(`Successfully uploaded ${streams.length} files`) + + // open web UI at proper directory + await ipfsCompanion.openWebUiAtDirectory(uploadDir) // close upload tab as it will be replaced with a new tab with uploaded content await browser.tabs.remove(uploadTab.id) } catch (err) { From ae4db71bdbab6b85339bda944c9961faf22bbd57 Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Tue, 12 Nov 2019 21:33:32 -0500 Subject: [PATCH 06/28] reword upload to import in UI messages --- add-on/_locales/en/messages.json | 2 +- add-on/src/popup/quick-upload.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index 5bbfd65ea..3566554b6 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -476,7 +476,7 @@ "description": "Button on the share files page (quickUpload_options_show)" }, "quickUpload_options_uploadDir": { - "message": "MFS directory to store uploaded files", + "message": "MFS directory to store imported files", "description": "Textbox label on the share files page (quickUpload_options_wrapWithDirectory)" }, "page_proxyAcl_title": { diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index 6b59efce3..1caf47124 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -21,7 +21,7 @@ function quickUploadStore (state, emitter) { state.peerCount = '' state.ipfsNodeType = 'external' state.expandOptions = false - state.uploadDir = '/ipfs-companion-uploads/' + state.uploadDir = '/ipfs-companion-imports/' function updateState ({ ipfsNodeType, peerCount }) { state.ipfsNodeType = ipfsNodeType @@ -65,28 +65,28 @@ async function processFiles (state, emitter, files) { create: true, parents: true } - state.progress = `Uploading ${streams.length} files...` + state.progress = `Importing ${streams.length} files...` const uploadDir = state.uploadDir.replace(/\/$|$/, '/') try { const files = streams.map(stream => (ipfsCompanion.ipfs.files.write(`${uploadDir}${stream.path}`, stream.content, options))) await Promise.all(files) } catch (err) { - console.error('Failed to upload files to IPFS', err) + console.error('Failed to import files to IPFS', err) ipfsCompanion.notify('notify_uploadErrorTitle', 'notify_inlineErrorMsg', `${err.message}`) throw err } state.progress = 'Completed' emitter.emit('render') - console.log(`Successfully uploaded ${streams.length} files`) + console.log(`Successfully imported ${streams.length} files`) // open web UI at proper directory await ipfsCompanion.openWebUiAtDirectory(uploadDir) // 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) + console.error('Unable to perform import', err) // keep upload tab and display error message in it - state.message = 'Unable to upload to IPFS API:' + state.message = 'Unable to import to IPFS:' state.progress = `${err}` emitter.emit('render') } From a67c7fad17b8a8bc439a01c6dfc7fae24518177d Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Wed, 13 Nov 2019 08:49:58 -0500 Subject: [PATCH 07/28] move default import directory to preferences screen --- add-on/_locales/en/messages.json | 8 ++++++ add-on/src/lib/ipfs-companion.js | 4 +++ add-on/src/lib/options.js | 3 ++- add-on/src/options/forms/experiments-form.js | 21 +++++++++++++++ add-on/src/options/page.js | 1 + add-on/src/popup/quick-upload.js | 27 +++----------------- 6 files changed, 39 insertions(+), 25 deletions(-) diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index 3566554b6..c326c3444 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -427,6 +427,14 @@ "message": "Customize which namespaces are logged to Browser Console. Changing this value will trigger extension restart.", "description": "An option description for the log level (option_logNamespaces_description)" }, + "option_uploadDir_title": { + "message": "MFS Import Directory", + "description": "An option title on the Preferences screen (option_uploadDir_title)" + }, + "option_uploadDir_description": { + "message": "Customize the directory used for uploading files to MFS.", + "description": "An option description on the Preferences screen (option_uploadDir_description)" + }, "option_resetAllOptions_title": { "message": "Reset Everything", "description": "An option title and button label on the Preferences screen (option_resetAllOptions_title)" diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index be6b1bd48..d110d3bbf 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -230,6 +230,7 @@ module.exports = async function init () { gwURLString: dropSlash(state.gwURLString), pubGwURLString: dropSlash(state.pubGwURLString), webuiRootUrl: state.webuiRootUrl, + uploadDir: state.uploadDir, apiURLString: dropSlash(state.apiURLString), redirect: state.redirect, noRedirectHostnames: state.noRedirectHostnames, @@ -715,6 +716,9 @@ module.exports = async function init () { shouldReloadExtension = true state[key] = localStorage.debug = change.newValue break + case 'uploadDir': + state[key] = change.newValue + break case 'linkify': case 'catchUnhandledProtocols': case 'displayNotifications': diff --git a/add-on/src/lib/options.js b/add-on/src/lib/options.js index 9ed986a1f..806000471 100644 --- a/add-on/src/lib/options.js +++ b/add-on/src/lib/options.js @@ -26,7 +26,8 @@ exports.optionDefaults = Object.freeze({ ipfsApiUrl: buildIpfsApiUrl(), ipfsApiPollMs: 3000, ipfsProxy: true, // window.ipfs - logNamespaces: 'jsipfs*,ipfs*,libp2p:mdns*,libp2p-delegated*,-*:ipns*,-ipfs:preload*,-ipfs-http-client:request*,-ipfs:http-api*' + logNamespaces: 'jsipfs*,ipfs*,libp2p:mdns*,libp2p-delegated*,-*:ipns*,-ipfs:preload*,-ipfs-http-client:request*,-ipfs:http-api*', + uploadDir: '/ipfs-companion-imports/' }) function buildCustomGatewayUrl () { diff --git a/add-on/src/options/forms/experiments-form.js b/add-on/src/options/forms/experiments-form.js index 6c1d95376..ce8eafd77 100644 --- a/add-on/src/options/forms/experiments-form.js +++ b/add-on/src/options/forms/experiments-form.js @@ -15,6 +15,7 @@ function experimentsForm ({ detectIpfsPathHeader, ipfsProxy, logNamespaces, + uploadDir, onOptionChange, onOptionsReset }) { @@ -153,6 +154,26 @@ function experimentsForm ({ onchange=${onOptionChange('logNamespaces')} value=${logNamespaces} /> +
+ + +
- ${quickUploadOptions(state, emit)} ` From d1f91ba5aec459e47d92496680d3100febef0ec1 Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Wed, 13 Nov 2019 22:07:51 -0500 Subject: [PATCH 08/28] Ensure imported files have the same CID when imported from Web UI or ipfs-companion This has been done using adding them to IPFS first (ipfs.add) first and then using the ipfs files copy api (ipfs.files.cp) to move the files into the desired directory. Please enter the commit message for your changes. Lines starting --- add-on/src/popup/quick-upload.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index a4c152b19..1bab4218c 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -62,14 +62,27 @@ async function processFiles (state, emitter, files) { const uploadTab = await browser.tabs.getCurrent() const streams = files2streams(files) emitter.emit('render') - const options = { - create: true, - parents: true - } state.progress = `Importing ${streams.length} files...` const uploadDir = state.uploadDir.replace(/\/$|$/, '/') try { - const files = streams.map(stream => (ipfsCompanion.ipfs.files.write(`${uploadDir}${stream.path}`, stream.content, options))) + // files are first `add`ed to IPFS + // and then copied to an MFS directory + // to ensure that CIDs for any created file + // remain the same for ipfs-companion and Web UI + const results = await ipfsCompanion.ipfs.add(streams) + // This is just an additional safety check, as in past combination + // of specific go-ipfs/js-ipfs-http-client versions + // produced silent errors in form of partial responses: + // https://github.com/ipfs-shipyard/ipfs-companion/issues/480 + const partialResponse = results.length !== streams.length + if (partialResponse) { + throw new Error('Result of ipfs.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') + } + + // cp will fail if directory does not exist + await ipfsCompanion.ipfs.files.mkdir(`${uploadDir}`, { parents: true }) + + const files = results.map(result => (ipfsCompanion.ipfs.files.cp(`/ipfs/${result.hash}`, `${uploadDir}${result.path}`))) await Promise.all(files) } catch (err) { console.error('Failed to import files to IPFS', err) From 87ee1a0242c2a19d8e889b09b4dc84771d5a6de9 Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Thu, 14 Nov 2019 08:27:36 -0500 Subject: [PATCH 09/28] open resource at CID if using embedded node --- add-on/src/popup/quick-upload.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index 1bab4218c..a06d7f89d 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -64,17 +64,18 @@ async function processFiles (state, emitter, files) { emitter.emit('render') state.progress = `Importing ${streams.length} files...` const uploadDir = state.uploadDir.replace(/\/$|$/, '/') + let result try { // files are first `add`ed to IPFS // and then copied to an MFS directory // to ensure that CIDs for any created file // remain the same for ipfs-companion and Web UI - const results = await ipfsCompanion.ipfs.add(streams) + result = await ipfsCompanion.ipfs.add(streams) // This is just an additional safety check, as in past combination // of specific go-ipfs/js-ipfs-http-client versions // produced silent errors in form of partial responses: // https://github.com/ipfs-shipyard/ipfs-companion/issues/480 - const partialResponse = results.length !== streams.length + const partialResponse = result.length !== streams.length if (partialResponse) { throw new Error('Result of ipfs.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') } @@ -82,7 +83,7 @@ async function processFiles (state, emitter, files) { // cp will fail if directory does not exist await ipfsCompanion.ipfs.files.mkdir(`${uploadDir}`, { parents: true }) - const files = results.map(result => (ipfsCompanion.ipfs.files.cp(`/ipfs/${result.hash}`, `${uploadDir}${result.path}`))) + const files = result.map(result => (ipfsCompanion.ipfs.files.cp(`/ipfs/${result.hash}`, `${uploadDir}${result.path}`))) await Promise.all(files) } catch (err) { console.error('Failed to import files to IPFS', err) @@ -94,7 +95,13 @@ async function processFiles (state, emitter, files) { console.log(`Successfully imported ${streams.length} files`) // open web UI at proper directory - await ipfsCompanion.openWebUiAtDirectory(uploadDir) + // unless and embedded node is in use (no access to web UI) + // in which case, open resource. + if (state.ipfsNodeType === 'embedded') { + await ipfsCompanion.uploadResultHandler({ result, openRootInNewTab: true }) + } else { + await ipfsCompanion.openWebUiAtDirectory(uploadDir) + } // close upload tab as it will be replaced with a new tab with uploaded content await browser.tabs.remove(uploadTab.id) } catch (err) { From 7b0999a23c403396cc03ba875dc946fda96c843c Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Thu, 14 Nov 2019 21:52:24 -0500 Subject: [PATCH 10/28] add date symbols to import directory --- add-on/src/lib/options.js | 2 +- add-on/src/popup/quick-upload.js | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/add-on/src/lib/options.js b/add-on/src/lib/options.js index 806000471..318451749 100644 --- a/add-on/src/lib/options.js +++ b/add-on/src/lib/options.js @@ -27,7 +27,7 @@ exports.optionDefaults = Object.freeze({ ipfsApiPollMs: 3000, ipfsProxy: true, // window.ipfs logNamespaces: 'jsipfs*,ipfs*,libp2p:mdns*,libp2p-delegated*,-*:ipns*,-ipfs:preload*,-ipfs-http-client:request*,-ipfs:http-api*', - uploadDir: '/ipfs-companion-imports/' + uploadDir: '/ipfs-companion-imports/%Y-%M-%D_%h%m%s/' }) function buildCustomGatewayUrl () { diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index a06d7f89d..79a9d17c9 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -63,7 +63,7 @@ async function processFiles (state, emitter, files) { const streams = files2streams(files) emitter.emit('render') state.progress = `Importing ${streams.length} files...` - const uploadDir = state.uploadDir.replace(/\/$|$/, '/') + const uploadDir = formatUploadDirectory(state.uploadDir) let result try { // files are first `add`ed to IPFS @@ -123,6 +123,16 @@ function file2buffer (file) { }) } */ +function formatUploadDirectory (path) { + path = path.replace(/\/$|$/, '/') + // needed to handle date symbols in the import directory + const now = new Date() + const dateSymbols = [/%Y/g, /%M/g, /%D/g, /%h/g, /%m/g, /%s/g] + const symbolReplacements = [now.getFullYear(), now.getMonth() + 1, now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds()] + dateSymbols.forEach((symbol, i) => { path = path.replace(symbol, symbolReplacements[i]) }) + return path +} + function files2streams (files) { const streams = [] for (const file of files) { From 23afca3020c62776b11934be8d9123749eb7b174 Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Fri, 15 Nov 2019 08:47:30 -0500 Subject: [PATCH 11/28] add import options to import screen --- add-on/_locales/en/messages.json | 6 +++++- add-on/src/popup/quick-upload.js | 36 ++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index c326c3444..c5293f765 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -39,6 +39,10 @@ "message": "Share files via IPFS", "description": "A menu item in Browser Action pop-up (panel_quickUpload)" }, + "quickUpload_options_openViaWebUI": { + "message": "Open imported files in Web UI", + "description": "Checkbox label on the share files page (quickUpload_options_openViaWebUI)" + }, "panel_openWebui": { "message": "Open Web UI", "description": "A menu item in Browser Action pop-up (panel_openWebui)" @@ -480,7 +484,7 @@ "description": "Status label on the share files page (quickUpload_state_buffering)" }, "quickUpload_options_show": { - "message": "upload options", + "message": "import options", "description": "Button on the share files page (quickUpload_options_show)" }, "quickUpload_options_uploadDir": { diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index 79a9d17c9..0065cf360 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -21,12 +21,16 @@ function quickUploadStore (state, emitter) { state.peerCount = '' state.ipfsNodeType = 'external' state.expandOptions = false + state.openViaWebUI = true state.uploadDir = '' + state.userChangedUploadDir = false function updateState ({ ipfsNodeType, peerCount, uploadDir }) { state.ipfsNodeType = ipfsNodeType state.peerCount = peerCount - state.uploadDir = uploadDir + if (!state.userChangedUploadDir) { + state.uploadDir = uploadDir + } } let port @@ -97,7 +101,7 @@ async function processFiles (state, emitter, files) { // open web UI at proper directory // unless and embedded node is in use (no access to web UI) // in which case, open resource. - if (state.ipfsNodeType === 'embedded') { + if (state.ipfsNodeType === 'embedded' || !state.openViaWebUI) { await ipfsCompanion.uploadResultHandler({ result, openRootInNewTab: true }) } else { await ipfsCompanion.openWebUiAtDirectory(uploadDir) @@ -151,6 +155,33 @@ function files2streams (files) { return streams } +function quickUploadOptions (state, emit) { + const onExpandOptions = (e) => { state.expandOptions = true; emit('render') } + const onDirectoryChange = (e) => { state.userChangedUploadDir = true; state.uploadDir = e.target.value } + const onOpenViaWebUIChange = (e) => { state.openViaWebUI = e.target.checked } + if (state.expandOptions) { + return html` +
+ + +
+ ` + } + return html` + + ` +} + function quickUploadPage (state, emit) { const onFileInputChange = (e) => emit('fileInputChange', e) const { peerCount } = state @@ -190,6 +221,7 @@ function quickUploadPage (state, emit) { + ${quickUploadOptions(state, emit)} ` From e816d3bd441611c02ac993e0979750dc74e0c360 Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Sun, 17 Nov 2019 12:30:09 -0500 Subject: [PATCH 12/28] fix: only display openViaWebUI option if opening via web UI is possible --- add-on/src/popup/quick-upload.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index 0065cf360..b4d61f5fc 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -159,14 +159,16 @@ function quickUploadOptions (state, emit) { const onExpandOptions = (e) => { state.expandOptions = true; emit('render') } const onDirectoryChange = (e) => { state.userChangedUploadDir = true; state.uploadDir = e.target.value } const onOpenViaWebUIChange = (e) => { state.openViaWebUI = e.target.checked } + const displayOpenWebUI = state.ipfsNodeType !== 'embedded' + if (state.expandOptions) { return html`
- ` : null}
-
- - -
-
- -
${switchToggle({ id: 'preloadAtPublicGateway', checked: preloadAtPublicGateway, onchange: onPreloadAtPublicGatewayChange })}
-
+
+ +
${switchToggle({ id: 'preloadAtPublicGateway', checked: preloadAtPublicGateway, onchange: onPreloadAtPublicGatewayChange })}
+
` diff --git a/add-on/src/options/page.js b/add-on/src/options/page.js index 8636e9b65..a6395b448 100644 --- a/add-on/src/options/page.js +++ b/add-on/src/options/page.js @@ -73,11 +73,11 @@ module.exports = function optionsPage (state, emit) { })} ${fileImportForm({ importDir: state.options.importDir, + preloadAtPublicGateway: state.options.preloadAtPublicGateway, onOptionChange })} ${experimentsForm({ displayNotifications: state.options.displayNotifications, - preloadAtPublicGateway: state.options.preloadAtPublicGateway, catchUnhandledProtocols: state.options.catchUnhandledProtocols, linkify: state.options.linkify, dnslinkPolicy: state.options.dnslinkPolicy, From e0fe23b2caf88a704e385df3efc04308376143b6 Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Wed, 27 Nov 2019 08:21:22 -0500 Subject: [PATCH 24/28] preload files when opening imported files in web UI --- add-on/src/lib/ipfs-companion.js | 19 ++++++++++++++----- add-on/src/popup/quick-upload.js | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index d2284ae10..5383975ba 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -283,6 +283,16 @@ module.exports = async function init () { }) } + function preloadFilesAtPublicGateway (files) { + for (const file in files) { + if (file && file.hash) { + const { path } = getIpfsPathAndNativeAddress(file.hash) + preloadAtPublicGateway(path) + console.info('[ipfs-companion] successfully stored', file) + } + } + } + // Context Menu Uploader // ------------------------------------------------------------------- @@ -352,12 +362,10 @@ module.exports = async function init () { } async function uploadResultHandler ({ result, openRootInNewTab = false }) { + preloadFilesAtPublicGateway(result) for (const file of result) { if (file && file.hash) { - const { path, url } = getIpfsPathAndNativeAddress(file.hash) - preloadAtPublicGateway(path) - console.info('[ipfs-companion] successfully stored', file) - // open the wrapping directory (or the CID if wrapping was disabled) + const { url } = getIpfsPathAndNativeAddress(file.hash) if (openRootInNewTab && (result.length === 1 || file.path === '' || file.path === file.hash)) { await browser.tabs.create({ url: url @@ -368,7 +376,8 @@ module.exports = async function init () { return result } - async function openWebUiAtDirectory (dir) { + async function openWebUiAtDirectory (result, dir) { + preloadFilesAtPublicGateway(result) await browser.tabs.create({ url: `${state.webuiRootUrl}#/files${dir}` }) diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index b5f8d3a73..ebefaaa47 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -109,7 +109,7 @@ async function processFiles (state, emitter, files) { if (state.ipfsNodeType === 'embedded' || !state.openViaWebUI) { await ipfsCompanion.uploadResultHandler({ result, openRootInNewTab: true }) } else { - await ipfsCompanion.openWebUiAtDirectory(importDir) + await ipfsCompanion.openWebUiAtDirectory(result, importDir) } // close upload tab as it will be replaced with a new tab with uploaded content await browser.tabs.remove(uploadTab.id) From e4ba4944f935c05b0e2e73e792916e77bbcf8784 Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Wed, 27 Nov 2019 20:27:42 -0500 Subject: [PATCH 25/28] test quick-upload formatImportDirectory --- add-on/src/popup/quick-upload.js | 2 + test/functional/lib/quick-upload.test.js | 55 ++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 test/functional/lib/quick-upload.test.js diff --git a/add-on/src/popup/quick-upload.js b/add-on/src/popup/quick-upload.js index ebefaaa47..3df4abe61 100644 --- a/add-on/src/popup/quick-upload.js +++ b/add-on/src/popup/quick-upload.js @@ -144,6 +144,8 @@ function formatImportDirectory (path) { return path } +exports.formatImportDirectory = formatImportDirectory + function files2streams (files) { const streams = [] for (const file of files) { diff --git a/test/functional/lib/quick-upload.test.js b/test/functional/lib/quick-upload.test.js new file mode 100644 index 000000000..c41dc4592 --- /dev/null +++ b/test/functional/lib/quick-upload.test.js @@ -0,0 +1,55 @@ +'use strict' +const { describe, it, before, after } = require('mocha') +const { expect } = require('chai') +const { useFakeTimers } = require('sinon') +const browser = require('sinon-chrome') + +describe('quick-upload.js', function () { + let formatImportDirectory + let clock + before(function () { + global.document = {} + global.browser = browser + // quick-upload depends on webextension/polyfill which can't be imported + // in a non-browser environment unless global.browser is stubbed + formatImportDirectory = require('../../../add-on/src/popup/quick-upload').formatImportDirectory + // need to force Date to return a particular date + clock = useFakeTimers({ + now: new Date(2017, 10, 5, 12, 1, 1) + }) + }) + + describe('formatImportDirectory', function () { + it('should change nothing if path is properly formatted and date wildcards are not provided', function () { + const path = '/ipfs-companion-imports/my-directory/' + expect(formatImportDirectory(path)).to.equal('/ipfs-companion-imports/my-directory/') + }) + + it('should replace two successive slashes with a single slash', function () { + const path = '/ipfs-companion-imports//my-directory/' + expect(formatImportDirectory(path)).to.equal('/ipfs-companion-imports/my-directory/') + }) + + it('should replace multiple slashes with a single slash', function () { + const path = '/ipfs-companion-imports/////////my-directory/' + expect(formatImportDirectory(path)).to.equal('/ipfs-companion-imports/my-directory/') + }) + + it('should add trailing slash if not present', function () { + const path = '/ipfs-companion-imports/my-directory' + expect(formatImportDirectory(path)).to.equal('/ipfs-companion-imports/my-directory/') + }) + + it('should replace date wildcards with padded dates', function () { + const path = '/ipfs-companion-imports/%Y-%M-%D_%h%m%s/' + expect(formatImportDirectory(path)).to.equal('/ipfs-companion-imports/2017-11-05_120101/') + }) + }) + + after(function () { + clock.restore() + delete global.document + delete global.browser + browser.flush() + }) +}) From 3ff1e695be9c9380d0659ec2f4ad4bfcfe34faa7 Mon Sep 17 00:00:00 2001 From: colinfruit <17092461+colinfruit@users.noreply.github.com> Date: Wed, 27 Nov 2019 21:14:45 -0500 Subject: [PATCH 26/28] add openViaWebUI option to file import form --- add-on/_locales/en/messages.json | 8 ++++++++ add-on/src/lib/ipfs-companion.js | 2 ++ add-on/src/lib/options.js | 3 ++- add-on/src/options/forms/file-import-form.js | 12 +++++++++++- add-on/src/options/page.js | 1 + add-on/src/popup/quick-upload.js | 11 +++++++++-- 6 files changed, 33 insertions(+), 4 deletions(-) diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index f81a80c04..c987f091f 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -415,6 +415,14 @@ "message": "Manage permissions", "description": "Link text for managing permissions" }, + "option_openViaWebUI_title": { + "message": "Open imported files in Web UI", + "description": "An option title on the Preferences screen (option_openViaWebUI_title)" + }, + "option_openViaWebUI_description": { + "message": "Display files in Web UI rather than opening file or parent directory at gateway.", + "description": "An option description on the Preferences screen (option_openViaWebUI_description)" + }, "option_preloadAtPublicGateway_title": { "message": "Preload Imports", "description": "An option title on the Preferences screen (option_preloadAtPublicGateway_title)" diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 5383975ba..9a547d697 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -231,6 +231,7 @@ module.exports = async function init () { pubGwURLString: dropSlash(state.pubGwURLString), webuiRootUrl: state.webuiRootUrl, importDir: state.importDir, + openViaWebUI: state.openViaWebUI, apiURLString: dropSlash(state.apiURLString), redirect: state.redirect, noRedirectHostnames: state.noRedirectHostnames, @@ -734,6 +735,7 @@ module.exports = async function init () { case 'automaticMode': case 'detectIpfsPathHeader': case 'preloadAtPublicGateway': + case 'openViaWebUI': case 'noRedirectHostnames': state[key] = change.newValue break diff --git a/add-on/src/lib/options.js b/add-on/src/lib/options.js index d931f1d3e..dfc31076a 100644 --- a/add-on/src/lib/options.js +++ b/add-on/src/lib/options.js @@ -27,7 +27,8 @@ exports.optionDefaults = Object.freeze({ ipfsApiPollMs: 3000, ipfsProxy: true, // window.ipfs logNamespaces: 'jsipfs*,ipfs*,libp2p:mdns*,libp2p-delegated*,-*:ipns*,-ipfs:preload*,-ipfs-http-client:request*,-ipfs:http-api*', - importDir: '/ipfs-companion-imports/%Y-%M-%D_%h%m%s/' + importDir: '/ipfs-companion-imports/%Y-%M-%D_%h%m%s/', + openViaWebUI: true }) function buildCustomGatewayUrl () { diff --git a/add-on/src/options/forms/file-import-form.js b/add-on/src/options/forms/file-import-form.js index a8f7f20cc..7953d4a52 100644 --- a/add-on/src/options/forms/file-import-form.js +++ b/add-on/src/options/forms/file-import-form.js @@ -5,8 +5,9 @@ const browser = require('webextension-polyfill') const html = require('choo/html') const switchToggle = require('../../pages/components/switch-toggle') -function fileImportForm ({ importDir, preloadAtPublicGateway, onOptionChange }) { +function fileImportForm ({ importDir, openViaWebUI, preloadAtPublicGateway, onOptionChange }) { const onImportDirChange = onOptionChange('importDir') + const onOpenViaWebUIChange = onOptionChange('openViaWebUI') const onPreloadAtPublicGatewayChange = onOptionChange('preloadAtPublicGateway') return html`
@@ -32,6 +33,15 @@ function fileImportForm ({ importDir, preloadAtPublicGateway, onOptionChange }) onchange=${onImportDirChange} value=${importDir} /> +
+ +
${switchToggle({ id: 'openViaWebUI', checked: openViaWebUI, onchange: onOpenViaWebUIChange })}
+