From f517a3f268d32664db45014bdb2edaf314f2f398 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 19 Sep 2018 01:39:18 +0200 Subject: [PATCH 1/8] feat: separate context action for links Closes #579 --- add-on/_locales/en/messages.json | 10 ++++++--- add-on/src/lib/context-menus.js | 36 ++++++++++++++++++++++---------- add-on/src/lib/ipfs-companion.js | 17 +++++++-------- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index 13e38c672..f1291ff8f 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -84,17 +84,21 @@ "description": "Default label for icon hidden in Page Action menu (pageAction_titleNonIpfs)" }, "contextMenu_AddToIpfsSelection": { - "message": "Add selected text to IPFS", + "message": "Add Selected Text to IPFS", "description": "An item in right-click context menu (contextMenu_AddToIpfsSelection)" }, "contextMenu_AddToIpfsRawCid": { - "message": "Add to IPFS", + "message": "Add This Object to IPFS", "description": "An item in right-click context menu (contextMenu_AddToIpfsRawCid)" }, "contextMenu_AddToIpfsKeepFilename": { - "message": "Add to IPFS (Keep Filename)", + "message": "Add This Object to IPFS (Keep Filename)", "description": "An item in right-click context menu (contextMenu_AddToIpfsKeepFilename)" }, + "contextMenu_AddToIpfsLink": { + "message": "Add This Link to IPFS", + "description": "An item in right-click context menu (contextMenu_AddToIpfsLink)" + }, "notify_addonIssueTitle": { "message": "IPFS Add-on Issue", "description": "A title of system notification (notify_addonIssueTitle)" diff --git a/add-on/src/lib/context-menus.js b/add-on/src/lib/context-menus.js index 9e5de833b..0c7213886 100644 --- a/add-on/src/lib/context-menus.js +++ b/add-on/src/lib/context-menus.js @@ -2,16 +2,19 @@ const browser = require('webextension-polyfill') -async function findUrlForContext (context) { +async function findUrlForContext (context, contextField) { if (context) { - if (context.linkUrl) { - // present when clicked on a link - return context.linkUrl + if (contextField) { + return context[contextField] } if (context.srcUrl) { // present when clicked on page element such as image or video return context.srcUrl } + if (context.linkUrl) { + // present when clicked on a link + return context.linkUrl + } if (context.pageUrl) { // pageUrl is the root frame return context.pageUrl @@ -27,10 +30,11 @@ module.exports.findUrlForContext = findUrlForContext const contextMenuAddToIpfsSelection = 'contextMenu_AddToIpfsSelection' const contextMenuAddToIpfsRawCid = 'contextMenu_AddToIpfsRawCid' const contextMenuAddToIpfsKeepFilename = 'contextMenu_AddToIpfsKeepFilename' +const contextMenuAddToIpfsLink = 'contextMenu_AddToIpfsLink' const contextMenuCopyCanonicalAddress = 'panelCopy_currentIpfsAddress' const contextMenuCopyAddressAtPublicGw = 'panel_copyCurrentPublicGwUrl' -function createContextMenus (getState, runtime, ipfsPathValidator, { onAddToIpfs, onAddToIpfsKeepFilename, onCopyCanonicalAddress, onCopyAddressAtPublicGw }) { +function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromContext, onCopyCanonicalAddress, onCopyAddressAtPublicGw }) { let copyAddressContexts = ['page', 'image', 'video', 'audio', 'link'] if (runtime.isFirefox) { // https://github.com/ipfs-shipyard/ipfs-companion/issues/398 @@ -43,25 +47,34 @@ function createContextMenus (getState, runtime, ipfsPathValidator, { onAddToIpfs contexts: ['selection'], documentUrlPatterns: [''], enabled: false, - onclick: onAddToIpfs + onclick: (context) => onAddFromContext(context, 'selectionText') }) browser.contextMenus.create({ id: contextMenuAddToIpfsRawCid, title: browser.i18n.getMessage(contextMenuAddToIpfsRawCid), - contexts: ['image', 'video', 'audio', 'link'], + contexts: ['image', 'video', 'audio'], documentUrlPatterns: [''], enabled: false, - onclick: onAddToIpfs + onclick: (context) => onAddFromContext(context, 'srcUrl') }) browser.contextMenus.create({ id: contextMenuAddToIpfsKeepFilename, title: browser.i18n.getMessage(contextMenuAddToIpfsKeepFilename), - contexts: ['image', 'video', 'audio', 'link'], + contexts: ['image', 'video', 'audio'], + documentUrlPatterns: [''], + enabled: false, + onclick: (context) => onAddFromContext(context, 'srcUrl', { wrapWithDirectory: true }) + }) + + browser.contextMenus.create({ + id: contextMenuAddToIpfsLink, + title: browser.i18n.getMessage(contextMenuAddToIpfsLink), + contexts: ['link'], documentUrlPatterns: [''], enabled: false, - onclick: onAddToIpfsKeepFilename + onclick: (context) => onAddFromContext(context, 'linkUrl', { wrapWithDirectory: true }) }) browser.contextMenus.create({ @@ -99,7 +112,8 @@ function createContextMenus (getState, runtime, ipfsPathValidator, { onAddToIpfs const canUpload = getState().peerCount > 0 const items = [ contextMenuAddToIpfsSelection, contextMenuAddToIpfsRawCid, - contextMenuAddToIpfsKeepFilename + contextMenuAddToIpfsKeepFilename, + contextMenuAddToIpfsLink ] for (let item of items) { await browser.contextMenus.update(item, { enabled: canUpload }) diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 39d132ec0..09be9d842 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -59,8 +59,7 @@ module.exports = async function init () { dnslinkResolver = createDnslinkResolver(getState) ipfsPathValidator = createIpfsPathValidator(getState, dnslinkResolver) contextMenus = createContextMenus(getState, runtime, ipfsPathValidator, { - onAddToIpfs: addFromContext, - onAddToIpfsKeepFilename: (info) => addFromContext(info, { wrapWithDirectory: true }), + onAddFromContext, onCopyCanonicalAddress: copier.copyCanonicalAddress, onCopyAddressAtPublicGw: copier.copyAddressAtPublicGw }) @@ -256,20 +255,20 @@ module.exports = async function init () { // Context Menu Uploader // ------------------------------------------------------------------- - async function addFromContext (info, options) { + async function onAddFromContext (context, contextField, options) { let result try { - const srcUrl = await findUrlForContext(info) - if (info.selectionText) { - result = await ipfs.files.add(Buffer.from(info.selectionText), options) + const srcUrl = await findUrlForContext(context, contextField) + if (context.selectionText) { + result = await ipfs.files.add(Buffer.from(context.selectionText), options) } else if (runtime.isFirefox) { // workaround due to https://github.com/ipfs/ipfs-companion/issues/227 const fetchOptions = { cache: 'force-cache', - referrer: info.pageUrl + referrer: context.pageUrl } - // console.log('addFromContext.info', info) - // console.log('addFromContext.fetchOptions', fetchOptions) + // console.log('onAddFromContext.context', context) + // console.log('onAddFromContext.fetchOptions', fetchOptions) const response = await fetch(srcUrl, fetchOptions) const blob = await response.blob() const buffer = await new Promise((resolve, reject) => { From bd274f752dc45c30d0fa2ad4cb1942cd33fbf7ee Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 19 Sep 2018 13:03:43 +0200 Subject: [PATCH 2/8] feat: separate labels for Image, Video and Audio --- add-on/_locales/en/messages.json | 18 ++++---- add-on/src/lib/context-menus.js | 72 +++++++++++++------------------- 2 files changed, 40 insertions(+), 50 deletions(-) diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index f1291ff8f..e9a835459 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -87,16 +87,20 @@ "message": "Add Selected Text to IPFS", "description": "An item in right-click context menu (contextMenu_AddToIpfsSelection)" }, - "contextMenu_AddToIpfsRawCid": { - "message": "Add This Object to IPFS", - "description": "An item in right-click context menu (contextMenu_AddToIpfsRawCid)" + "contextMenu_AddToIpfsImage": { + "message": "Add this Image to IPFS", + "description": "An item in right-click context menu (contextMenu_AddToIpfsImage)" }, - "contextMenu_AddToIpfsKeepFilename": { - "message": "Add This Object to IPFS (Keep Filename)", - "description": "An item in right-click context menu (contextMenu_AddToIpfsKeepFilename)" + "contextMenu_AddToIpfsVideo": { + "message": "Add this Video to IPFS", + "description": "An item in right-click context menu (contextMenu_AddToIpfsVideo)" + }, + "contextMenu_AddToIpfsAudio": { + "message": "Add this Audio to IPFS", + "description": "An item in right-click context menu (contextMenu_AddToIpfsAudio)" }, "contextMenu_AddToIpfsLink": { - "message": "Add This Link to IPFS", + "message": "Add Link Destination to IPFS", "description": "An item in right-click context menu (contextMenu_AddToIpfsLink)" }, "notify_addonIssueTitle": { diff --git a/add-on/src/lib/context-menus.js b/add-on/src/lib/context-menus.js index 0c7213886..703fb8173 100644 --- a/add-on/src/lib/context-menus.js +++ b/add-on/src/lib/context-menus.js @@ -28,12 +28,23 @@ async function findUrlForContext (context, contextField) { module.exports.findUrlForContext = findUrlForContext const contextMenuAddToIpfsSelection = 'contextMenu_AddToIpfsSelection' -const contextMenuAddToIpfsRawCid = 'contextMenu_AddToIpfsRawCid' -const contextMenuAddToIpfsKeepFilename = 'contextMenu_AddToIpfsKeepFilename' +// const contextMenuAddToIpfsRawCid = 'contextMenu_AddToIpfsRawCid' +const contextMenuAddToIpfsImage = 'contextMenu_AddToIpfsImage' +const contextMenuAddToIpfsVideo = 'contextMenu_AddToIpfsVideo' +const contextMenuAddToIpfsAudio = 'contextMenu_AddToIpfsAudio' const contextMenuAddToIpfsLink = 'contextMenu_AddToIpfsLink' const contextMenuCopyCanonicalAddress = 'panelCopy_currentIpfsAddress' const contextMenuCopyAddressAtPublicGw = 'panel_copyCurrentPublicGwUrl' +// menu items that are enabled only when API is online +const apiMenuItems = [ + contextMenuAddToIpfsSelection, + contextMenuAddToIpfsImage, + contextMenuAddToIpfsVideo, + contextMenuAddToIpfsAudio, + contextMenuAddToIpfsLink +] + function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromContext, onCopyCanonicalAddress, onCopyAddressAtPublicGw }) { let copyAddressContexts = ['page', 'image', 'video', 'audio', 'link'] if (runtime.isFirefox) { @@ -41,41 +52,21 @@ function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromCo copyAddressContexts.push('page_action') } try { - browser.contextMenus.create({ - id: contextMenuAddToIpfsSelection, - title: browser.i18n.getMessage(contextMenuAddToIpfsSelection), - contexts: ['selection'], - documentUrlPatterns: [''], - enabled: false, - onclick: (context) => onAddFromContext(context, 'selectionText') - }) - - browser.contextMenus.create({ - id: contextMenuAddToIpfsRawCid, - title: browser.i18n.getMessage(contextMenuAddToIpfsRawCid), - contexts: ['image', 'video', 'audio'], - documentUrlPatterns: [''], - enabled: false, - onclick: (context) => onAddFromContext(context, 'srcUrl') - }) - - browser.contextMenus.create({ - id: contextMenuAddToIpfsKeepFilename, - title: browser.i18n.getMessage(contextMenuAddToIpfsKeepFilename), - contexts: ['image', 'video', 'audio'], - documentUrlPatterns: [''], - enabled: false, - onclick: (context) => onAddFromContext(context, 'srcUrl', { wrapWithDirectory: true }) - }) - - browser.contextMenus.create({ - id: contextMenuAddToIpfsLink, - title: browser.i18n.getMessage(contextMenuAddToIpfsLink), - contexts: ['link'], - documentUrlPatterns: [''], - enabled: false, - onclick: (context) => onAddFromContext(context, 'linkUrl', { wrapWithDirectory: true }) - }) + const createAddToIpfsMenuItem = (menuItemId, contextName, contextField, ipfsAddOptions) => { + browser.contextMenus.create({ + id: menuItemId, + title: browser.i18n.getMessage(menuItemId), + contexts: [contextName], + documentUrlPatterns: [''], + enabled: false, + onclick: (context) => onAddFromContext(context, 'srcUrl', ipfsAddOptions) + }) + } + createAddToIpfsMenuItem(contextMenuAddToIpfsSelection, 'selection', 'selectionText') + createAddToIpfsMenuItem(contextMenuAddToIpfsImage, 'image', 'srcUrl', { wrapWithDirectory: true }) + createAddToIpfsMenuItem(contextMenuAddToIpfsVideo, 'video', 'srcUrl', { wrapWithDirectory: true }) + createAddToIpfsMenuItem(contextMenuAddToIpfsAudio, 'audio', 'srcUrl', { wrapWithDirectory: true }) + createAddToIpfsMenuItem(contextMenuAddToIpfsLink, 'link', 'linkUrl', { wrapWithDirectory: true }) browser.contextMenus.create({ id: contextMenuCopyCanonicalAddress, @@ -110,12 +101,7 @@ function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromCo async update (changedTabId) { try { const canUpload = getState().peerCount > 0 - const items = [ contextMenuAddToIpfsSelection, - contextMenuAddToIpfsRawCid, - contextMenuAddToIpfsKeepFilename, - contextMenuAddToIpfsLink - ] - for (let item of items) { + for (let item of apiMenuItems) { await browser.contextMenus.update(item, { enabled: canUpload }) } if (changedTabId) { From 754264c77ef5e65db1da24efd510abb958dfa277 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 19 Sep 2018 14:01:31 +0200 Subject: [PATCH 3/8] style: "Add Linked Content to IPFS" --- add-on/_locales/en/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index e9a835459..f112e1fcd 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -100,7 +100,7 @@ "description": "An item in right-click context menu (contextMenu_AddToIpfsAudio)" }, "contextMenu_AddToIpfsLink": { - "message": "Add Link Destination to IPFS", + "message": "Add Linked Content to IPFS", "description": "An item in right-click context menu (contextMenu_AddToIpfsLink)" }, "notify_addonIssueTitle": { From bc35aa7909654d518d31e303e7053111d69da1ff Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 19 Sep 2018 15:41:29 +0200 Subject: [PATCH 4/8] feat: context option to Copy Direct CID Closes #508 --- add-on/_locales/en/messages.json | 14 +++---- add-on/src/lib/context-menus.js | 41 ++++++++++--------- add-on/src/lib/copier.js | 22 ++++++++-- add-on/src/lib/ipfs-companion.js | 12 +++--- add-on/src/lib/ipfs-path.js | 17 ++++++-- .../popup/browser-action/context-actions.js | 16 +++++--- add-on/src/popup/browser-action/page.js | 5 +-- add-on/src/popup/browser-action/store.js | 24 +++++++---- add-on/src/popup/page-action/page.js | 5 +-- 9 files changed, 96 insertions(+), 60 deletions(-) diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index f112e1fcd..a9ed17ca3 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -67,6 +67,10 @@ "message": "Copy Canonical Address", "description": "A menu item in Browser Action pop-up and right-click context menu (panelCopy_currentIpfsAddress)" }, + "panelCopy_copyDirectCid": { + "message": "Copy Direct CID", + "description": "A menu item in Browser Action pop-up and right-click context menu (panelCopy_copyDirectCid)" + }, "panel_copyCurrentPublicGwUrl": { "message": "Copy Public Gateway URL", "description": "A menu item in Browser Action pop-up and right-click context menu (panel_copyCurrentPublicGwUrl)" @@ -111,13 +115,9 @@ "message": "See Browser Console for more details", "description": "A message in system notification (notify_addonIssueMsg)" }, - "notify_copiedPublicURLTitle": { - "message": "Copied Public URL", - "description": "A title of system notification (notify_copiedPublicURLTitle)" - }, - "notify_copiedCanonicalAddressTitle": { - "message": "Copied Canonical Address", - "description": "A title of system notification (notify_copiedCanonicalAddressTitle)" + "notify_copiedTitle": { + "message": "Copied", + "description": "A title of system notification (notify_copiedTitle)" }, "notify_pinnedIpfsResourceTitle": { "message": "IPFS Resource is now pinned", diff --git a/add-on/src/lib/context-menus.js b/add-on/src/lib/context-menus.js index 703fb8173..a019a9f85 100644 --- a/add-on/src/lib/context-menus.js +++ b/add-on/src/lib/context-menus.js @@ -27,14 +27,19 @@ async function findUrlForContext (context, contextField) { module.exports.findUrlForContext = findUrlForContext +// Add X to IPFS const contextMenuAddToIpfsSelection = 'contextMenu_AddToIpfsSelection' -// const contextMenuAddToIpfsRawCid = 'contextMenu_AddToIpfsRawCid' const contextMenuAddToIpfsImage = 'contextMenu_AddToIpfsImage' const contextMenuAddToIpfsVideo = 'contextMenu_AddToIpfsVideo' const contextMenuAddToIpfsAudio = 'contextMenu_AddToIpfsAudio' const contextMenuAddToIpfsLink = 'contextMenu_AddToIpfsLink' +// Copy X const contextMenuCopyCanonicalAddress = 'panelCopy_currentIpfsAddress' +const contextMenuCopyDirectCid = 'panelCopy_copyDirectCid' const contextMenuCopyAddressAtPublicGw = 'panel_copyCurrentPublicGwUrl' +module.exports.contextMenuCopyCanonicalAddress = contextMenuCopyCanonicalAddress +module.exports.contextMenuCopyDirectCid = contextMenuCopyDirectCid +module.exports.contextMenuCopyAddressAtPublicGw = contextMenuCopyAddressAtPublicGw // menu items that are enabled only when API is online const apiMenuItems = [ @@ -42,10 +47,11 @@ const apiMenuItems = [ contextMenuAddToIpfsImage, contextMenuAddToIpfsVideo, contextMenuAddToIpfsAudio, - contextMenuAddToIpfsLink + contextMenuAddToIpfsLink, + contextMenuCopyDirectCid ] -function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromContext, onCopyCanonicalAddress, onCopyAddressAtPublicGw }) { +function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromContext, onCopyCanonicalAddress, onCopyDirectCid, onCopyAddressAtPublicGw }) { let copyAddressContexts = ['page', 'image', 'video', 'audio', 'link'] if (runtime.isFirefox) { // https://github.com/ipfs-shipyard/ipfs-companion/issues/398 @@ -59,7 +65,7 @@ function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromCo contexts: [contextName], documentUrlPatterns: [''], enabled: false, - onclick: (context) => onAddFromContext(context, 'srcUrl', ipfsAddOptions) + onclick: (context) => onAddFromContext(context, contextField, ipfsAddOptions) }) } createAddToIpfsMenuItem(contextMenuAddToIpfsSelection, 'selection', 'selectionText') @@ -68,21 +74,18 @@ function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromCo createAddToIpfsMenuItem(contextMenuAddToIpfsAudio, 'audio', 'srcUrl', { wrapWithDirectory: true }) createAddToIpfsMenuItem(contextMenuAddToIpfsLink, 'link', 'linkUrl', { wrapWithDirectory: true }) - browser.contextMenus.create({ - id: contextMenuCopyCanonicalAddress, - title: browser.i18n.getMessage(contextMenuCopyCanonicalAddress), - contexts: copyAddressContexts, - documentUrlPatterns: ['*://*/ipfs/*', '*://*/ipns/*'], - onclick: onCopyCanonicalAddress - }) - - browser.contextMenus.create({ - id: contextMenuCopyAddressAtPublicGw, - title: browser.i18n.getMessage(contextMenuCopyAddressAtPublicGw), - contexts: copyAddressContexts, - documentUrlPatterns: ['*://*/ipfs/*', '*://*/ipns/*'], - onclick: onCopyAddressAtPublicGw - }) + const createCopierMenuItem = (menuItemId, handler) => { + browser.contextMenus.create({ + id: menuItemId, + title: browser.i18n.getMessage(menuItemId), + contexts: copyAddressContexts, + documentUrlPatterns: ['*://*/ipfs/*', '*://*/ipns/*'], + onclick: handler + }) + } + createCopierMenuItem(contextMenuCopyCanonicalAddress, onCopyCanonicalAddress) + createCopierMenuItem(contextMenuCopyDirectCid, onCopyDirectCid) + createCopierMenuItem(contextMenuCopyAddressAtPublicGw, onCopyAddressAtPublicGw) } catch (err) { // documentUrlPatterns is not supported in Brave if (err.message.indexOf('createProperties.documentUrlPatterns of contextMenus.create is not supported yet') > -1) { diff --git a/add-on/src/lib/copier.js b/add-on/src/lib/copier.js index 56fa7c2d5..99366ed72 100644 --- a/add-on/src/lib/copier.js +++ b/add-on/src/lib/copier.js @@ -1,7 +1,7 @@ 'use strict' const browser = require('webextension-polyfill') -const { safeIpfsPath } = require('./ipfs-path') +const { safeIpfsPath, trimHashAndSearch } = require('./ipfs-path') const { findUrlForContext } = require('./context-menus') async function copyTextToClipboard (copyText) { @@ -34,13 +34,27 @@ async function copyTextToClipboard (copyText) { } } -function createCopier (getState, notify) { +function createCopier (getState, getIpfs, notify) { return { async copyCanonicalAddress (context) { const url = await findUrlForContext(context) const rawIpfsAddress = safeIpfsPath(url) copyTextToClipboard(rawIpfsAddress) - notify('notify_copiedCanonicalAddressTitle', rawIpfsAddress) + notify('notify_copiedTitle', rawIpfsAddress) + }, + + async copyDirectCid (context) { + try { + const ipfs = getIpfs() + const url = await findUrlForContext(context) + const rawIpfsAddress = trimHashAndSearch(safeIpfsPath(url)) + const directCid = (await ipfs.resolve(rawIpfsAddress, { recursive: true })).split('/')[2] + copyTextToClipboard(directCid) + notify('notify_copiedTitle', directCid) + } catch (error) { + console.error('Unable to resolve/copy direct CID:', error.message) + if (notify) notify('notify_addonIssueTitle', 'notify_addonIssueMsg') + } }, async copyAddressAtPublicGw (context) { @@ -48,7 +62,7 @@ function createCopier (getState, notify) { const state = getState() const urlAtPubGw = url.replace(state.gwURLString, state.pubGwURLString) copyTextToClipboard(urlAtPubGw) - notify('notify_copiedPublicURLTitle', urlAtPubGw) + notify('notify_copiedTitle', urlAtPubGw) } } } diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index 09be9d842..a78193c84 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -12,7 +12,7 @@ const { createIpfsUrlProtocolHandler } = require('./ipfs-protocol') const createNotifier = require('./notifier') const createCopier = require('./copier') const createRuntimeChecks = require('./runtime-checks') -const { createContextMenus, findUrlForContext } = require('./context-menus') +const { createContextMenus, findUrlForContext, contextMenuCopyAddressAtPublicGw, contextMenuCopyDirectCid, contextMenuCopyCanonicalAddress } = require('./context-menus') const createIpfsProxy = require('./ipfs-proxy') const { showPendingLandingPages } = require('./on-installed') @@ -55,12 +55,13 @@ module.exports = async function init () { } } - copier = createCopier(getState, notify) + copier = createCopier(getState, getIpfs, notify) dnslinkResolver = createDnslinkResolver(getState) ipfsPathValidator = createIpfsPathValidator(getState, dnslinkResolver) contextMenus = createContextMenus(getState, runtime, ipfsPathValidator, { onAddFromContext, onCopyCanonicalAddress: copier.copyCanonicalAddress, + onCopyDirectCid: copier.copyDirectCid, onCopyAddressAtPublicGw: copier.copyAddressAtPublicGw }) modifyRequest = createRequestModifier(getState, dnslinkResolver, ipfsPathValidator, runtime) @@ -190,8 +191,9 @@ module.exports = async function init () { const BrowserActionMessageHandlers = { notification: (message) => notify(message.title, message.message), - copyCanonicalAddress: () => copier.copyCanonicalAddress(), - copyAddressAtPublicGw: () => copier.copyAddressAtPublicGw() + [contextMenuCopyCanonicalAddress]: copier.copyCanonicalAddress, + [contextMenuCopyDirectCid]: copier.copyDirectCid, + [contextMenuCopyAddressAtPublicGw]: copier.copyAddressAtPublicGw } function handleMessageFromBrowserAction (message) { @@ -259,7 +261,7 @@ module.exports = async function init () { let result try { const srcUrl = await findUrlForContext(context, contextField) - if (context.selectionText) { + if (contextField === 'selectionText') { result = await ipfs.files.add(Buffer.from(context.selectionText), options) } else if (runtime.isFirefox) { // workaround due to https://github.com/ipfs/ipfs-companion/issues/227 diff --git a/add-on/src/lib/ipfs-path.js b/add-on/src/lib/ipfs-path.js index 64391e8a1..79200a545 100644 --- a/add-on/src/lib/ipfs-path.js +++ b/add-on/src/lib/ipfs-path.js @@ -10,6 +10,7 @@ function safeIpfsPath (urlOrPath) { // better safe than sorry: https://github.com/ipfs/ipfs-companion/issues/303 return decodeURIComponent(urlOrPath.replace(/^.*(\/ip(f|n)s\/.+)$/, '$1')) } +exports.safeIpfsPath = safeIpfsPath function subdomainToIpfsPath (url) { if (typeof url === 'string') { @@ -21,15 +22,23 @@ function subdomainToIpfsPath (url) { return `/${protocol}/${cid}${url.pathname}` } -exports.safeIpfsPath = safeIpfsPath - function pathAtHttpGateway (path, gatewayUrl) { // return URL without duplicated slashes - return new URL(`${gatewayUrl}${path}`).toString().replace(/([^:]\/)\/+/g, '$1') + return trimDoubleSlashes(new URL(`${gatewayUrl}${path}`).toString()) } - exports.pathAtHttpGateway = pathAtHttpGateway +function trimDoubleSlashes (urlString) { + return urlString.replace(/([^:]\/)\/+/g, '$1') +} +exports.trimDoubleSlashes = trimDoubleSlashes + +function trimHashAndSearch (urlString) { + // https://github.com/ipfs-shipyard/ipfs-companion/issues/567 + return urlString.split('#')[0].split('?')[0] +} +exports.trimHashAndSearch = trimHashAndSearch + function createIpfsPathValidator (getState, dnsLink) { const ipfsPathValidator = { // Test if URL is a Public IPFS resource diff --git a/add-on/src/popup/browser-action/context-actions.js b/add-on/src/popup/browser-action/context-actions.js index ae94c3cd2..be9764bb4 100644 --- a/add-on/src/popup/browser-action/context-actions.js +++ b/add-on/src/popup/browser-action/context-actions.js @@ -4,6 +4,7 @@ const browser = require('webextension-polyfill') const html = require('choo/html') const navItem = require('./nav-item') +const { contextMenuCopyAddressAtPublicGw, contextMenuCopyDirectCid, contextMenuCopyCanonicalAddress } = require('../../lib/context-menus') module.exports = function contextActions ({ active, @@ -14,8 +15,7 @@ module.exports = function contextActions ({ isPinned, isIpfsOnline, isApiAvailable, - onCopyIpfsAddr, - onCopyPublicGwAddr, + onCopy, onPin, onUnPin }) { @@ -24,12 +24,16 @@ module.exports = function contextActions ({ return html`
${navItem({ - text: browser.i18n.getMessage('panelCopy_currentIpfsAddress'), - onClick: onCopyIpfsAddr + text: browser.i18n.getMessage(contextMenuCopyCanonicalAddress), + onClick: () => onCopy(contextMenuCopyCanonicalAddress) })} ${navItem({ - text: browser.i18n.getMessage('panel_copyCurrentPublicGwUrl'), - onClick: onCopyPublicGwAddr + text: browser.i18n.getMessage(contextMenuCopyDirectCid), + onClick: () => onCopy(contextMenuCopyDirectCid) + })} + ${navItem({ + text: browser.i18n.getMessage(contextMenuCopyAddressAtPublicGw), + onClick: () => onCopy(contextMenuCopyAddressAtPublicGw) })} ${!isPinned ? ( navItem({ diff --git a/add-on/src/popup/browser-action/page.js b/add-on/src/popup/browser-action/page.js index 3260648ed..5910c6a1e 100644 --- a/add-on/src/popup/browser-action/page.js +++ b/add-on/src/popup/browser-action/page.js @@ -10,8 +10,7 @@ const operations = require('./operations') // Passed current app `state` from the store and `emit`, a function to create // events, allowing views to signal back to the store that something happened. module.exports = function browserActionPage (state, emit) { - const onCopyIpfsAddr = () => emit('copyIpfsAddr') - const onCopyPublicGwAddr = () => emit('copyPublicGwAddr') + const onCopy = (copyAction) => emit('copy', copyAction) const onPin = () => emit('pin') const onUnPin = () => emit('unPin') @@ -23,7 +22,7 @@ module.exports = function browserActionPage (state, emit) { const onToggleActive = () => emit('toggleActive') const headerProps = Object.assign({ onToggleNodeType, onToggleActive, onOpenPrefs }, state) - const contextActionsProps = Object.assign({ onCopyIpfsAddr, onCopyPublicGwAddr, onPin, onUnPin }, state) + const contextActionsProps = Object.assign({ onCopy, onPin, onUnPin }, state) const opsProps = Object.assign({ onQuickUpload, onOpenWebUi, onToggleRedirect }, state) return html` diff --git a/add-on/src/popup/browser-action/store.js b/add-on/src/popup/browser-action/store.js index 65dbfc6db..2bd61c747 100644 --- a/add-on/src/popup/browser-action/store.js +++ b/add-on/src/popup/browser-action/store.js @@ -2,7 +2,8 @@ /* eslint-env browser, webextensions */ const browser = require('webextension-polyfill') -const { safeIpfsPath } = require('../../lib/ipfs-path') +const { safeIpfsPath, trimHashAndSearch } = require('../../lib/ipfs-path') +const { contextMenuCopyAddressAtPublicGw, contextMenuCopyDirectCid, contextMenuCopyCanonicalAddress } = require('../../lib/context-menus') // The store contains and mutates the state for the app module.exports = (state, emitter) => { @@ -55,13 +56,18 @@ module.exports = (state, emitter) => { }, 100) }) - emitter.on('copyPublicGwAddr', async function copyCurrentPublicGwAddress () { - port.postMessage({ event: 'copyAddressAtPublicGw' }) - window.close() - }) - - emitter.on('copyIpfsAddr', async function copyCurrentCanonicalAddress () { - port.postMessage({ event: 'copyCanonicalAddress' }) + emitter.on('copy', function (copyAction) { + switch (copyAction) { + case contextMenuCopyCanonicalAddress: + port.postMessage({ event: contextMenuCopyCanonicalAddress }) + break + case contextMenuCopyDirectCid: + port.postMessage({ event: contextMenuCopyDirectCid }) + break + case contextMenuCopyAddressAtPublicGw: + port.postMessage({ event: contextMenuCopyAddressAtPublicGw }) + break + } window.close() }) @@ -275,7 +281,7 @@ async function getIpfsApi () { async function resolveToPinPath (ipfs, url) { // https://github.com/ipfs-shipyard/ipfs-companion/issues/567 - url = url.split('#')[0].split('?')[0] + url = trimHashAndSearch(url) // https://github.com/ipfs/ipfs-companion/issues/303 let path = safeIpfsPath(url) if (/^\/ipns/.test(path)) { diff --git a/add-on/src/popup/page-action/page.js b/add-on/src/popup/page-action/page.js index 890329f4e..a43308e58 100644 --- a/add-on/src/popup/page-action/page.js +++ b/add-on/src/popup/page-action/page.js @@ -9,11 +9,10 @@ const contextActions = require('../browser-action/context-actions') // Passed current app `state` from the store and `emit`, a function to create // events, allowing views to signal back to the store that something happened. module.exports = function pageActionPage (state, emit) { - const onCopyIpfsAddr = () => emit('copyIpfsAddr') - const onCopyPublicGwAddr = () => emit('copyPublicGwAddr') + const onCopy = (copyAction) => emit('copy', copyAction) const onPin = () => emit('pin') const onUnPin = () => emit('unPin') - const contextActionsProps = Object.assign({ onCopyIpfsAddr, onCopyPublicGwAddr, onPin, onUnPin }, state) + const contextActionsProps = Object.assign({ onCopy, onPin, onUnPin }, state) // Instant init: page-action is shown only in ipfsContext contextActionsProps.isIpfsContext = true From 2336d9d24c5c7231fd226a094f043c49d43473ee Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Thu, 20 Sep 2018 01:46:27 +0200 Subject: [PATCH 5/8] feat: separate submenus for different contexts --- add-on/_locales/en/messages.json | 54 ++++--- add-on/src/lib/context-menus.js | 149 ++++++++++++------ add-on/src/lib/copier.js | 14 +- add-on/src/lib/ipfs-companion.js | 18 +-- .../popup/browser-action/context-actions.js | 6 +- add-on/src/popup/browser-action/store.js | 6 +- package.json | 1 + 7 files changed, 160 insertions(+), 88 deletions(-) diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index a9ed17ca3..d4b84be4c 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -64,12 +64,12 @@ "description": "A menu item in Browser Action pop-up (panel_unpinCurrentIpfsAddress)" }, "panelCopy_currentIpfsAddress": { - "message": "Copy Canonical Address", + "message": "Copy IPFS Path", "description": "A menu item in Browser Action pop-up and right-click context menu (panelCopy_currentIpfsAddress)" }, - "panelCopy_copyDirectCid": { - "message": "Copy Direct CID", - "description": "A menu item in Browser Action pop-up and right-click context menu (panelCopy_copyDirectCid)" + "panelCopy_copyRawCid": { + "message": "Copy Resolved CID", + "description": "A menu item in Browser Action pop-up and right-click context menu (panelCopy_copyRawCid)" }, "panel_copyCurrentPublicGwUrl": { "message": "Copy Public Gateway URL", @@ -87,25 +87,41 @@ "message": "Non-IPFS resource", "description": "Default label for icon hidden in Page Action menu (pageAction_titleNonIpfs)" }, - "contextMenu_AddToIpfsSelection": { - "message": "Add Selected Text to IPFS", - "description": "An item in right-click context menu (contextMenu_AddToIpfsSelection)" + "contextMenu_parentImage": { + "message": "Selected Image", + "description": "An item in right-click context menu (contextMenu_parentImage)" + }, + "contextMenu_parentVideo": { + "message": "Selected Video", + "description": "An item in right-click context menu (contextMenu_parentVideo)" + }, + "contextMenu_parentAudio": { + "message": "Selected Audio", + "description": "An item in right-click context menu (contextMenu_parentAudio)" }, - "contextMenu_AddToIpfsImage": { - "message": "Add this Image to IPFS", - "description": "An item in right-click context menu (contextMenu_AddToIpfsImage)" + "contextMenu_parentLink": { + "message": "Linked Content", + "description": "An item in right-click context menu (contextMenu_parentLink)" }, - "contextMenu_AddToIpfsVideo": { - "message": "Add this Video to IPFS", - "description": "An item in right-click context menu (contextMenu_AddToIpfsVideo)" + "contextMenu_parentText": { + "message": "Selected Text", + "description": "An item in right-click context menu (contextMenu_parentText)" }, - "contextMenu_AddToIpfsAudio": { - "message": "Add this Audio to IPFS", - "description": "An item in right-click context menu (contextMenu_AddToIpfsAudio)" + "contextMenu_parentPage": { + "message": "This Page", + "description": "An item in right-click context menu (contextMenu_parentPage)" }, - "contextMenu_AddToIpfsLink": { - "message": "Add Linked Content to IPFS", - "description": "An item in right-click context menu (contextMenu_AddToIpfsLink)" + "contextMenu_AddToIpfsKeepFilename": { + "message": "Add to IPFS (Keep Filename)", + "description": "An item in right-click context menu (contextMenu_AddToIpfsKeepFilename)" + }, + "contextMenu_AddToIpfsRawCid": { + "message": "Add to IPFS (Raw CID)", + "description": "An item in right-click context menu (contextMenu_AddToIpfsRawCid)" + }, + "contextMenu_AddToIpfsSelection": { + "message": "Add Selected Text to IPFS", + "description": "An item in right-click context menu (contextMenu_AddToIpfsSelection)" }, "notify_addonIssueTitle": { "message": "IPFS Add-on Issue", diff --git a/add-on/src/lib/context-menus.js b/add-on/src/lib/context-menus.js index a019a9f85..4e90f122c 100644 --- a/add-on/src/lib/context-menus.js +++ b/add-on/src/lib/context-menus.js @@ -2,10 +2,23 @@ const browser = require('webextension-polyfill') -async function findUrlForContext (context, contextField) { +// mapping between context name and field with data for it +// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/menus/ContextType +const contextSources = { + selection: 'selectionText', + image: 'srcUrl', + video: 'srcUrl', + audio: 'srcUrl', + link: 'linkUrl', + page: 'pageUrl' +} + +async function findValueForContext (context, contextType) { if (context) { - if (contextField) { - return context[contextField] + console.log(context) + if (contextType) { + const field = contextSources[contextType] + return context[field] } if (context.srcUrl) { // present when clicked on page element such as image or video @@ -25,69 +38,110 @@ async function findUrlForContext (context, contextField) { return currentTab.url } -module.exports.findUrlForContext = findUrlForContext +module.exports.findValueForContext = findValueForContext +// Context Roots +const menuParentImage = 'contextMenu_parentImage' +const menuParentVideo = 'contextMenu_parentVideo' +const menuParentAudio = 'contextMenu_parentAudio' +const menuParentLink = 'contextMenu_parentLink' +const menuParentPage = 'contextMenu_parentPage' +// const menuParentText = 'contextMenu_parentText' +// Generic Add to IPFS +const contextMenuAddToIpfsRawCid = 'contextMenu_AddToIpfsRawCid' +const contextMenuAddToIpfsKeepFilename = 'contextMenu_AddToIpfsKeepFilename' // Add X to IPFS const contextMenuAddToIpfsSelection = 'contextMenu_AddToIpfsSelection' -const contextMenuAddToIpfsImage = 'contextMenu_AddToIpfsImage' -const contextMenuAddToIpfsVideo = 'contextMenu_AddToIpfsVideo' -const contextMenuAddToIpfsAudio = 'contextMenu_AddToIpfsAudio' -const contextMenuAddToIpfsLink = 'contextMenu_AddToIpfsLink' // Copy X const contextMenuCopyCanonicalAddress = 'panelCopy_currentIpfsAddress' -const contextMenuCopyDirectCid = 'panelCopy_copyDirectCid' +const contextMenuCopyRawCid = 'panelCopy_copyRawCid' const contextMenuCopyAddressAtPublicGw = 'panel_copyCurrentPublicGwUrl' module.exports.contextMenuCopyCanonicalAddress = contextMenuCopyCanonicalAddress -module.exports.contextMenuCopyDirectCid = contextMenuCopyDirectCid +module.exports.contextMenuCopyRawCid = contextMenuCopyRawCid module.exports.contextMenuCopyAddressAtPublicGw = contextMenuCopyAddressAtPublicGw // menu items that are enabled only when API is online -const apiMenuItems = [ - contextMenuAddToIpfsSelection, - contextMenuAddToIpfsImage, - contextMenuAddToIpfsVideo, - contextMenuAddToIpfsAudio, - contextMenuAddToIpfsLink, - contextMenuCopyDirectCid -] +const apiMenuItems = new Set() +// menu items enabled only in IPFS context +const ipfsContextItems = new Set() -function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromContext, onCopyCanonicalAddress, onCopyDirectCid, onCopyAddressAtPublicGw }) { - let copyAddressContexts = ['page', 'image', 'video', 'audio', 'link'] - if (runtime.isFirefox) { - // https://github.com/ipfs-shipyard/ipfs-companion/issues/398 - copyAddressContexts.push('page_action') - } +function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromContext, onCopyCanonicalAddress, onCopyRawCid, onCopyAddressAtPublicGw }) { try { - const createAddToIpfsMenuItem = (menuItemId, contextName, contextField, ipfsAddOptions) => { + const createSubmenu = (id, contextType, menuBuilder) => { browser.contextMenus.create({ - id: menuItemId, - title: browser.i18n.getMessage(menuItemId), - contexts: [contextName], + id, + title: browser.i18n.getMessage(id), + documentUrlPatterns: [''], + contexts: [contextType] + }) + } + const createSeparator = (parentId, id, contextType) => { + return browser.contextMenus.create({ + id: `${parentId}_${id}`, + parentId, + type: 'separator', + contexts: ['all'] + }) + } + const createAddToIpfsMenuItem = (parentId, id, contextType, ipfsAddOptions) => { + const itemId = `${parentId}_${id}` + apiMenuItems.add(itemId) + return browser.contextMenus.create({ + id: itemId, + parentId, + title: browser.i18n.getMessage(id), + contexts: [contextType], documentUrlPatterns: [''], enabled: false, - onclick: (context) => onAddFromContext(context, contextField, ipfsAddOptions) + /* no support for 'icons' in Chrome + icons: { + '48': '/ui-kit/icons/stroke_cube.svg' + },*/ + onclick: (context) => onAddFromContext(context, contextType, ipfsAddOptions) }) } - createAddToIpfsMenuItem(contextMenuAddToIpfsSelection, 'selection', 'selectionText') - createAddToIpfsMenuItem(contextMenuAddToIpfsImage, 'image', 'srcUrl', { wrapWithDirectory: true }) - createAddToIpfsMenuItem(contextMenuAddToIpfsVideo, 'video', 'srcUrl', { wrapWithDirectory: true }) - createAddToIpfsMenuItem(contextMenuAddToIpfsAudio, 'audio', 'srcUrl', { wrapWithDirectory: true }) - createAddToIpfsMenuItem(contextMenuAddToIpfsLink, 'link', 'linkUrl', { wrapWithDirectory: true }) - - const createCopierMenuItem = (menuItemId, handler) => { - browser.contextMenus.create({ - id: menuItemId, - title: browser.i18n.getMessage(menuItemId), - contexts: copyAddressContexts, + const createCopierMenuItem = (parentId, id, contextType, handler) => { + const itemId = `${parentId}_${id}` + ipfsContextItems.add(itemId) + // some items also require API access + if (id === contextMenuCopyRawCid) { + apiMenuItems.add(itemId) + } + return browser.contextMenus.create({ + id: itemId, + parentId, + title: browser.i18n.getMessage(id), + contexts: [contextType], documentUrlPatterns: ['*://*/ipfs/*', '*://*/ipns/*'], - onclick: handler + /* no support for 'icons' in Chrome + icons: { + '48': '/ui-kit/icons/stroke_copy.svg' + },*/ + onclick: (context) => handler(context, contextType) }) } - createCopierMenuItem(contextMenuCopyCanonicalAddress, onCopyCanonicalAddress) - createCopierMenuItem(contextMenuCopyDirectCid, onCopyDirectCid) - createCopierMenuItem(contextMenuCopyAddressAtPublicGw, onCopyAddressAtPublicGw) + const buildSubmenu = (parentId, contextType) => { + createSubmenu(parentId, contextType) + createAddToIpfsMenuItem(parentId, contextMenuAddToIpfsKeepFilename, contextType, { wrapWithDirectory: true }) + createAddToIpfsMenuItem(parentId, contextMenuAddToIpfsRawCid, contextType, { wrapWithDirectory: false }) + createSeparator(parentId, 'separator-1', contextType) + createCopierMenuItem(parentId, contextMenuCopyCanonicalAddress, contextType, onCopyCanonicalAddress) + createCopierMenuItem(parentId, contextMenuCopyRawCid, contextType, onCopyRawCid) + createCopierMenuItem(parentId, contextMenuCopyAddressAtPublicGw, contextType, onCopyAddressAtPublicGw) + } + + /* + createSubmenu(menuParentText, 'selection') + createAddToIpfsMenuItem(menuParentText, contextMenuAddToIpfsSelection, 'selection') + */ + createAddToIpfsMenuItem(null, contextMenuAddToIpfsSelection, 'selection') + buildSubmenu(menuParentImage, 'image') + buildSubmenu(menuParentVideo, 'video') + buildSubmenu(menuParentAudio, 'audio') + buildSubmenu(menuParentLink, 'link') + buildSubmenu(menuParentPage, 'page') } catch (err) { - // documentUrlPatterns is not supported in Brave + // documentUrlPatterns is not supported in Muon-Brave if (err.message.indexOf('createProperties.documentUrlPatterns of contextMenus.create is not supported yet') > -1) { console.warn('[ipfs-companion] Context menus disabled - createProperties.documentUrlPatterns of contextMenus.create is not supported yet') return { update: () => Promise.resolve() } @@ -112,8 +166,9 @@ function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromCo const currentTab = await browser.tabs.query({ active: true, currentWindow: true }).then(tabs => tabs[0]) if (currentTab && currentTab.id === changedTabId) { const ipfsContext = ipfsPathValidator.isIpfsPageActionsContext(currentTab.url) - browser.contextMenus.update(contextMenuCopyCanonicalAddress, { enabled: ipfsContext }) - browser.contextMenus.update(contextMenuCopyAddressAtPublicGw, { enabled: ipfsContext }) + for (let item of ipfsContextItems) { + browser.contextMenus.update(item, { enabled: ipfsContext }) + } } } } catch (err) { diff --git a/add-on/src/lib/copier.js b/add-on/src/lib/copier.js index 99366ed72..4986e48b9 100644 --- a/add-on/src/lib/copier.js +++ b/add-on/src/lib/copier.js @@ -2,7 +2,7 @@ const browser = require('webextension-polyfill') const { safeIpfsPath, trimHashAndSearch } = require('./ipfs-path') -const { findUrlForContext } = require('./context-menus') +const { findValueForContext } = require('./context-menus') async function copyTextToClipboard (copyText) { const currentTab = await browser.tabs.query({ active: true, currentWindow: true }).then(tabs => tabs[0]) @@ -36,17 +36,17 @@ async function copyTextToClipboard (copyText) { function createCopier (getState, getIpfs, notify) { return { - async copyCanonicalAddress (context) { - const url = await findUrlForContext(context) + async copyCanonicalAddress (context, contextType) { + const url = await findValueForContext(context, contextType) const rawIpfsAddress = safeIpfsPath(url) copyTextToClipboard(rawIpfsAddress) notify('notify_copiedTitle', rawIpfsAddress) }, - async copyDirectCid (context) { + async copyRawCid (context, contextType) { try { const ipfs = getIpfs() - const url = await findUrlForContext(context) + const url = await findValueForContext(context, contextType) const rawIpfsAddress = trimHashAndSearch(safeIpfsPath(url)) const directCid = (await ipfs.resolve(rawIpfsAddress, { recursive: true })).split('/')[2] copyTextToClipboard(directCid) @@ -57,8 +57,8 @@ function createCopier (getState, getIpfs, notify) { } }, - async copyAddressAtPublicGw (context) { - const url = await findUrlForContext(context) + async copyAddressAtPublicGw (context, contextType) { + const url = await findValueForContext(context, contextType) const state = getState() const urlAtPubGw = url.replace(state.gwURLString, state.pubGwURLString) copyTextToClipboard(urlAtPubGw) diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index a78193c84..bbcc44721 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -12,7 +12,7 @@ const { createIpfsUrlProtocolHandler } = require('./ipfs-protocol') const createNotifier = require('./notifier') const createCopier = require('./copier') const createRuntimeChecks = require('./runtime-checks') -const { createContextMenus, findUrlForContext, contextMenuCopyAddressAtPublicGw, contextMenuCopyDirectCid, contextMenuCopyCanonicalAddress } = require('./context-menus') +const { createContextMenus, findValueForContext, contextMenuCopyAddressAtPublicGw, contextMenuCopyRawCid, contextMenuCopyCanonicalAddress } = require('./context-menus') const createIpfsProxy = require('./ipfs-proxy') const { showPendingLandingPages } = require('./on-installed') @@ -61,7 +61,7 @@ module.exports = async function init () { contextMenus = createContextMenus(getState, runtime, ipfsPathValidator, { onAddFromContext, onCopyCanonicalAddress: copier.copyCanonicalAddress, - onCopyDirectCid: copier.copyDirectCid, + onCopyRawCid: copier.copyRawCid, onCopyAddressAtPublicGw: copier.copyAddressAtPublicGw }) modifyRequest = createRequestModifier(getState, dnslinkResolver, ipfsPathValidator, runtime) @@ -192,7 +192,7 @@ module.exports = async function init () { const BrowserActionMessageHandlers = { notification: (message) => notify(message.title, message.message), [contextMenuCopyCanonicalAddress]: copier.copyCanonicalAddress, - [contextMenuCopyDirectCid]: copier.copyDirectCid, + [contextMenuCopyRawCid]: copier.copyRawCid, [contextMenuCopyAddressAtPublicGw]: copier.copyAddressAtPublicGw } @@ -257,12 +257,12 @@ module.exports = async function init () { // Context Menu Uploader // ------------------------------------------------------------------- - async function onAddFromContext (context, contextField, options) { + async function onAddFromContext (context, contextType, options) { let result try { - const srcUrl = await findUrlForContext(context, contextField) - if (contextField === 'selectionText') { - result = await ipfs.files.add(Buffer.from(context.selectionText), options) + const dataSrc = await findValueForContext(context, contextType) + if (contextType === 'selection') { + result = await ipfs.files.add(Buffer.from(dataSrc), options) } else if (runtime.isFirefox) { // workaround due to https://github.com/ipfs/ipfs-companion/issues/227 const fetchOptions = { @@ -271,7 +271,7 @@ module.exports = async function init () { } // console.log('onAddFromContext.context', context) // console.log('onAddFromContext.fetchOptions', fetchOptions) - const response = await fetch(srcUrl, fetchOptions) + const response = await fetch(dataSrc, fetchOptions) const blob = await response.blob() const buffer = await new Promise((resolve, reject) => { const reader = new FileReader() @@ -285,7 +285,7 @@ module.exports = async function init () { } result = await ipfs.files.add(data, options) } else { - result = await ipfs.util.addFromURL(srcUrl, options) + result = await ipfs.util.addFromURL(dataSrc, options) } } catch (error) { console.error('Error in upload to IPFS context menu', error) diff --git a/add-on/src/popup/browser-action/context-actions.js b/add-on/src/popup/browser-action/context-actions.js index be9764bb4..1842f674e 100644 --- a/add-on/src/popup/browser-action/context-actions.js +++ b/add-on/src/popup/browser-action/context-actions.js @@ -4,7 +4,7 @@ const browser = require('webextension-polyfill') const html = require('choo/html') const navItem = require('./nav-item') -const { contextMenuCopyAddressAtPublicGw, contextMenuCopyDirectCid, contextMenuCopyCanonicalAddress } = require('../../lib/context-menus') +const { contextMenuCopyAddressAtPublicGw, contextMenuCopyRawCid, contextMenuCopyCanonicalAddress } = require('../../lib/context-menus') module.exports = function contextActions ({ active, @@ -28,8 +28,8 @@ module.exports = function contextActions ({ onClick: () => onCopy(contextMenuCopyCanonicalAddress) })} ${navItem({ - text: browser.i18n.getMessage(contextMenuCopyDirectCid), - onClick: () => onCopy(contextMenuCopyDirectCid) + text: browser.i18n.getMessage(contextMenuCopyRawCid), + onClick: () => onCopy(contextMenuCopyRawCid) })} ${navItem({ text: browser.i18n.getMessage(contextMenuCopyAddressAtPublicGw), diff --git a/add-on/src/popup/browser-action/store.js b/add-on/src/popup/browser-action/store.js index 2bd61c747..976f0f404 100644 --- a/add-on/src/popup/browser-action/store.js +++ b/add-on/src/popup/browser-action/store.js @@ -3,7 +3,7 @@ const browser = require('webextension-polyfill') const { safeIpfsPath, trimHashAndSearch } = require('../../lib/ipfs-path') -const { contextMenuCopyAddressAtPublicGw, contextMenuCopyDirectCid, contextMenuCopyCanonicalAddress } = require('../../lib/context-menus') +const { contextMenuCopyAddressAtPublicGw, contextMenuCopyRawCid, contextMenuCopyCanonicalAddress } = require('../../lib/context-menus') // The store contains and mutates the state for the app module.exports = (state, emitter) => { @@ -61,8 +61,8 @@ module.exports = (state, emitter) => { case contextMenuCopyCanonicalAddress: port.postMessage({ event: contextMenuCopyCanonicalAddress }) break - case contextMenuCopyDirectCid: - port.postMessage({ event: contextMenuCopyDirectCid }) + case contextMenuCopyRawCid: + port.postMessage({ event: contextMenuCopyRawCid }) break case contextMenuCopyAddressAtPublicGw: port.postMessage({ event: contextMenuCopyAddressAtPublicGw }) diff --git a/package.json b/package.json index 6b54b2e18..5e379a89d 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "build:copy:ui-kit:ipfs-css": "run-p build:copy:ui-kit:ipfs-css:*", "build:copy:ui-kit:ipfs-css:css": "shx mkdir -p add-on/ui-kit && shx cp node_modules/ipfs-css/ipfs.css add-on/ui-kit", "build:copy:ui-kit:ipfs-css:fonts": "shx mkdir -p add-on/ui-kit/fonts && shx cp node_modules/ipfs-css/fonts/* add-on/ui-kit/fonts", + "build:copy:ui-kit:ipfs-css:icons": "shx mkdir -p add-on/ui-kit/icons && shx cp node_modules/ipfs-css/icons/* add-on/ui-kit/icons", "build:copy:ui-kit:tachyons": "shx mkdir -p add-on/ui-kit && shx cp node_modules/tachyons/css/tachyons.css add-on/ui-kit", "build:js": "run-p build:js:*", "build:js:webpack": "webpack -p", From b6b8638d2abdc973f8f3dde332312fcfd37fccc9 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Mon, 1 Oct 2018 16:46:46 +0200 Subject: [PATCH 6/8] feat: smarter filename when adding via context menu Closes #599 Also, simplified some labels and did some cleanup. --- add-on/_locales/en/messages.json | 6 +++--- add-on/src/lib/context-menus.js | 7 +++---- add-on/src/lib/copier.js | 4 ++-- add-on/src/lib/ipfs-companion.js | 16 +++++++++++----- .../src/popup/browser-action/context-actions.js | 8 ++++---- 5 files changed, 23 insertions(+), 18 deletions(-) diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index d4b84be4c..8dd567c15 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -68,7 +68,7 @@ "description": "A menu item in Browser Action pop-up and right-click context menu (panelCopy_currentIpfsAddress)" }, "panelCopy_copyRawCid": { - "message": "Copy Resolved CID", + "message": "Copy CID", "description": "A menu item in Browser Action pop-up and right-click context menu (panelCopy_copyRawCid)" }, "panel_copyCurrentPublicGwUrl": { @@ -112,11 +112,11 @@ "description": "An item in right-click context menu (contextMenu_parentPage)" }, "contextMenu_AddToIpfsKeepFilename": { - "message": "Add to IPFS (Keep Filename)", + "message": "Add to IPFS with Filename", "description": "An item in right-click context menu (contextMenu_AddToIpfsKeepFilename)" }, "contextMenu_AddToIpfsRawCid": { - "message": "Add to IPFS (Raw CID)", + "message": "Add to IPFS", "description": "An item in right-click context menu (contextMenu_AddToIpfsRawCid)" }, "contextMenu_AddToIpfsSelection": { diff --git a/add-on/src/lib/context-menus.js b/add-on/src/lib/context-menus.js index 4e90f122c..253bf8a41 100644 --- a/add-on/src/lib/context-menus.js +++ b/add-on/src/lib/context-menus.js @@ -15,7 +15,6 @@ const contextSources = { async function findValueForContext (context, contextType) { if (context) { - console.log(context) if (contextType) { const field = contextSources[contextType] return context[field] @@ -96,7 +95,7 @@ function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromCo /* no support for 'icons' in Chrome icons: { '48': '/ui-kit/icons/stroke_cube.svg' - },*/ + }, */ onclick: (context) => onAddFromContext(context, contextType, ipfsAddOptions) }) } @@ -116,7 +115,7 @@ function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromCo /* no support for 'icons' in Chrome icons: { '48': '/ui-kit/icons/stroke_copy.svg' - },*/ + }, */ onclick: (context) => handler(context, contextType) }) } @@ -125,9 +124,9 @@ function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromCo createAddToIpfsMenuItem(parentId, contextMenuAddToIpfsKeepFilename, contextType, { wrapWithDirectory: true }) createAddToIpfsMenuItem(parentId, contextMenuAddToIpfsRawCid, contextType, { wrapWithDirectory: false }) createSeparator(parentId, 'separator-1', contextType) + createCopierMenuItem(parentId, contextMenuCopyAddressAtPublicGw, contextType, onCopyAddressAtPublicGw) createCopierMenuItem(parentId, contextMenuCopyCanonicalAddress, contextType, onCopyCanonicalAddress) createCopierMenuItem(parentId, contextMenuCopyRawCid, contextType, onCopyRawCid) - createCopierMenuItem(parentId, contextMenuCopyAddressAtPublicGw, contextType, onCopyAddressAtPublicGw) } /* diff --git a/add-on/src/lib/copier.js b/add-on/src/lib/copier.js index 4986e48b9..0015e3fa6 100644 --- a/add-on/src/lib/copier.js +++ b/add-on/src/lib/copier.js @@ -48,12 +48,12 @@ function createCopier (getState, getIpfs, notify) { const ipfs = getIpfs() const url = await findValueForContext(context, contextType) const rawIpfsAddress = trimHashAndSearch(safeIpfsPath(url)) - const directCid = (await ipfs.resolve(rawIpfsAddress, { recursive: true })).split('/')[2] + const directCid = (await ipfs.resolve(rawIpfsAddress, { recursive: true, dhtt: '5s', dhtrc: 1 })).split('/')[2] copyTextToClipboard(directCid) notify('notify_copiedTitle', directCid) } catch (error) { console.error('Unable to resolve/copy direct CID:', error.message) - if (notify) notify('notify_addonIssueTitle', 'notify_addonIssueMsg') + if (notify) notify('notify_addonIssueTitle', 'notify_inlineErrorMsg', error.message) } }, diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index bbcc44721..e24b64c39 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -263,8 +263,11 @@ module.exports = async function init () { const dataSrc = await findValueForContext(context, contextType) if (contextType === 'selection') { result = await ipfs.files.add(Buffer.from(dataSrc), options) - } else if (runtime.isFirefox) { - // workaround due to https://github.com/ipfs/ipfs-companion/issues/227 + } else { + // Enchanced addFromURL + // -------------------- + // Initially, this was a workaround due to https://github.com/ipfs/ipfs-companion/issues/227 + // but now we have additional rules about keeping file name, so we can't use valilla ipfs.addFromURL const fetchOptions = { cache: 'force-cache', referrer: context.pageUrl @@ -279,13 +282,16 @@ module.exports = async function init () { reader.onerror = reject reader.readAsArrayBuffer(blob) }) + const url = new URL(response.url) + // https://github.com/ipfs-shipyard/ipfs-companion/issues/599 + const filename = url.pathname === '/' + ? url.hostname + : url.pathname.replace(/[\\/]+$/, '').split('/').pop() const data = { - path: decodeURIComponent(new URL(response.url).pathname.split('/').pop()), + path: decodeURIComponent(filename), content: buffer } result = await ipfs.files.add(data, options) - } else { - result = await ipfs.util.addFromURL(dataSrc, options) } } catch (error) { console.error('Error in upload to IPFS context menu', error) diff --git a/add-on/src/popup/browser-action/context-actions.js b/add-on/src/popup/browser-action/context-actions.js index 1842f674e..7ef4e9e24 100644 --- a/add-on/src/popup/browser-action/context-actions.js +++ b/add-on/src/popup/browser-action/context-actions.js @@ -23,6 +23,10 @@ module.exports = function contextActions ({ const activePinControls = active && isIpfsOnline && isApiAvailable && !(isPinning || isUnPinning) return html`
+ ${navItem({ + text: browser.i18n.getMessage(contextMenuCopyAddressAtPublicGw), + onClick: () => onCopy(contextMenuCopyAddressAtPublicGw) + })} ${navItem({ text: browser.i18n.getMessage(contextMenuCopyCanonicalAddress), onClick: () => onCopy(contextMenuCopyCanonicalAddress) @@ -31,10 +35,6 @@ module.exports = function contextActions ({ text: browser.i18n.getMessage(contextMenuCopyRawCid), onClick: () => onCopy(contextMenuCopyRawCid) })} - ${navItem({ - text: browser.i18n.getMessage(contextMenuCopyAddressAtPublicGw), - onClick: () => onCopy(contextMenuCopyAddressAtPublicGw) - })} ${!isPinned ? ( navItem({ text: browser.i18n.getMessage('panel_pinCurrentIpfsAddress'), From 7964f9bfc7fc58219afd5cf8601e9997ead1ede8 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Mon, 1 Oct 2018 17:03:48 +0200 Subject: [PATCH 7/8] style: restore existing label --- add-on/_locales/en/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index 8dd567c15..c4e3d606b 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -112,7 +112,7 @@ "description": "An item in right-click context menu (contextMenu_parentPage)" }, "contextMenu_AddToIpfsKeepFilename": { - "message": "Add to IPFS with Filename", + "message": "Add to IPFS (Keep Filename)", "description": "An item in right-click context menu (contextMenu_AddToIpfsKeepFilename)" }, "contextMenu_AddToIpfsRawCid": { From 1bcaa9dee5837d8f2033db8bf9d520520d0b0b17 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 2 Oct 2018 01:54:24 +0200 Subject: [PATCH 8/8] fix: menu items requiring both api and ipfs context --- add-on/src/lib/context-menus.js | 24 ++++++++++++++++-------- add-on/src/lib/copier.js | 12 +++++++++++- add-on/src/lib/ipfs-companion.js | 12 ++++++++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/add-on/src/lib/context-menus.js b/add-on/src/lib/context-menus.js index 253bf8a41..fe889cad3 100644 --- a/add-on/src/lib/context-menus.js +++ b/add-on/src/lib/context-menus.js @@ -153,23 +153,31 @@ function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromCo throw err } + // enabled only when ipfsContext is shown when API is up + const apiAndIpfsContextItems = new Set([...apiMenuItems].filter(i => ipfsContextItems.has(i))) + // state to avoid async tab lookups + let ipfsContext = false + return { async update (changedTabId) { try { - const canUpload = getState().peerCount > 0 - for (let item of apiMenuItems) { - await browser.contextMenus.update(item, { enabled: canUpload }) - } if (changedTabId) { // recalculate tab-dependant menu items const currentTab = await browser.tabs.query({ active: true, currentWindow: true }).then(tabs => tabs[0]) if (currentTab && currentTab.id === changedTabId) { - const ipfsContext = ipfsPathValidator.isIpfsPageActionsContext(currentTab.url) - for (let item of ipfsContextItems) { - browser.contextMenus.update(item, { enabled: ipfsContext }) - } + ipfsContext = ipfsPathValidator.isIpfsPageActionsContext(currentTab.url) } } + const ifApi = getState().peerCount > 0 + for (let item of apiMenuItems) { + await browser.contextMenus.update(item, { enabled: ifApi }) + } + for (let item of ipfsContextItems) { + await browser.contextMenus.update(item, { enabled: ipfsContext }) + } + for (let item of apiAndIpfsContextItems) { + await browser.contextMenus.update(item, { enabled: (ifApi && ipfsContext) }) + } } catch (err) { console.log('[ipfs-companion] Error updating context menus', err) } diff --git a/add-on/src/lib/copier.js b/add-on/src/lib/copier.js index 0015e3fa6..19741410f 100644 --- a/add-on/src/lib/copier.js +++ b/add-on/src/lib/copier.js @@ -53,7 +53,17 @@ function createCopier (getState, getIpfs, notify) { notify('notify_copiedTitle', directCid) } catch (error) { console.error('Unable to resolve/copy direct CID:', error.message) - if (notify) notify('notify_addonIssueTitle', 'notify_inlineErrorMsg', error.message) + if (notify) { + const errMsg = error.toString() + if (errMsg.startsWith('Error: no link')) { + // Sharding support is limited: + // - https://github.com/ipfs/js-ipfs/issues/1279 + // - https://github.com/ipfs/go-ipfs/issues/5270 + notify('notify_addonIssueTitle', 'Unable to resolve CID within HAMT-sharded directory, sorry! Will be fixed soon.') + } else { + notify('notify_addonIssueTitle', 'notify_inlineErrorMsg', error.message) + } + } } }, diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index e24b64c39..7fd94f38a 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -100,6 +100,9 @@ module.exports = async function init () { browser.webNavigation.onCommitted.addListener(onNavigationCommitted) browser.tabs.onUpdated.addListener(onUpdatedTab) browser.tabs.onActivated.addListener(onActivatedTab) + if (browser.windows) { + browser.windows.onFocusChanged.addListener(onWindowFocusChanged) + } browser.runtime.onMessage.addListener(onRuntimeMessage) browser.runtime.onConnect.addListener(onRuntimeConnect) @@ -342,6 +345,15 @@ module.exports = async function init () { // Page-specific Actions // ------------------------------------------------------------------- + async function onWindowFocusChanged (windowId) { + // Note: On some Linux window managers, WINDOW_ID_NONE will always be sent + // immediately preceding a switch from one browser window to another. + if (windowId !== browser.windows.WINDOW_ID_NONE) { + const currentTab = await browser.tabs.query({ active: true, windowId }).then(tabs => tabs[0]) + await contextMenus.update(currentTab.id) + } + } + async function onActivatedTab (activeInfo) { await contextMenus.update(activeInfo.tabId) }