diff --git a/add-on/_locales/en/messages.json b/add-on/_locales/en/messages.json index 13e38c672..c4e3d606b 100644 --- a/add-on/_locales/en/messages.json +++ b/add-on/_locales/en/messages.json @@ -64,9 +64,13 @@ "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_copyRawCid": { + "message": "Copy CID", + "description": "A menu item in Browser Action pop-up and right-click context menu (panelCopy_copyRawCid)" + }, "panel_copyCurrentPublicGwUrl": { "message": "Copy Public Gateway URL", "description": "A menu item in Browser Action pop-up and right-click context menu (panel_copyCurrentPublicGwUrl)" @@ -83,18 +87,42 @@ "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_AddToIpfsRawCid": { - "message": "Add to IPFS", - "description": "An item in right-click context menu (contextMenu_AddToIpfsRawCid)" + "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_parentLink": { + "message": "Linked Content", + "description": "An item in right-click context menu (contextMenu_parentLink)" + }, + "contextMenu_parentText": { + "message": "Selected Text", + "description": "An item in right-click context menu (contextMenu_parentText)" + }, + "contextMenu_parentPage": { + "message": "This Page", + "description": "An item in right-click context menu (contextMenu_parentPage)" }, "contextMenu_AddToIpfsKeepFilename": { "message": "Add to IPFS (Keep Filename)", "description": "An item in right-click context menu (contextMenu_AddToIpfsKeepFilename)" }, + "contextMenu_AddToIpfsRawCid": { + "message": "Add to IPFS", + "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", "description": "A title of system notification (notify_addonIssueTitle)" @@ -103,13 +131,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 9e5de833b..fe889cad3 100644 --- a/add-on/src/lib/context-menus.js +++ b/add-on/src/lib/context-menus.js @@ -2,16 +2,31 @@ const browser = require('webextension-polyfill') -async function findUrlForContext (context) { +// 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 (context.linkUrl) { - // present when clicked on a link - return context.linkUrl + if (contextType) { + const field = contextSources[contextType] + return context[field] } 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 @@ -22,65 +37,110 @@ async function findUrlForContext (context) { return currentTab.url } -module.exports.findUrlForContext = findUrlForContext +module.exports.findValueForContext = findValueForContext -const contextMenuAddToIpfsSelection = 'contextMenu_AddToIpfsSelection' +// 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' +// Copy X const contextMenuCopyCanonicalAddress = 'panelCopy_currentIpfsAddress' +const contextMenuCopyRawCid = 'panelCopy_copyRawCid' const contextMenuCopyAddressAtPublicGw = 'panel_copyCurrentPublicGwUrl' +module.exports.contextMenuCopyCanonicalAddress = contextMenuCopyCanonicalAddress +module.exports.contextMenuCopyRawCid = contextMenuCopyRawCid +module.exports.contextMenuCopyAddressAtPublicGw = contextMenuCopyAddressAtPublicGw -function createContextMenus (getState, runtime, ipfsPathValidator, { onAddToIpfs, onAddToIpfsKeepFilename, onCopyCanonicalAddress, onCopyAddressAtPublicGw }) { - let copyAddressContexts = ['page', 'image', 'video', 'audio', 'link'] - if (runtime.isFirefox) { - // https://github.com/ipfs-shipyard/ipfs-companion/issues/398 - copyAddressContexts.push('page_action') - } - try { - browser.contextMenus.create({ - id: contextMenuAddToIpfsSelection, - title: browser.i18n.getMessage(contextMenuAddToIpfsSelection), - contexts: ['selection'], - documentUrlPatterns: [''], - enabled: false, - onclick: onAddToIpfs - }) - - browser.contextMenus.create({ - id: contextMenuAddToIpfsRawCid, - title: browser.i18n.getMessage(contextMenuAddToIpfsRawCid), - contexts: ['image', 'video', 'audio', 'link'], - documentUrlPatterns: [''], - enabled: false, - onclick: onAddToIpfs - }) +// menu items that are enabled only when API is online +const apiMenuItems = new Set() +// menu items enabled only in IPFS context +const ipfsContextItems = new Set() - browser.contextMenus.create({ - id: contextMenuAddToIpfsKeepFilename, - title: browser.i18n.getMessage(contextMenuAddToIpfsKeepFilename), - contexts: ['image', 'video', 'audio', 'link'], - documentUrlPatterns: [''], - enabled: false, - onclick: onAddToIpfsKeepFilename - }) - - browser.contextMenus.create({ - id: contextMenuCopyCanonicalAddress, - title: browser.i18n.getMessage(contextMenuCopyCanonicalAddress), - contexts: copyAddressContexts, - documentUrlPatterns: ['*://*/ipfs/*', '*://*/ipns/*'], - onclick: onCopyCanonicalAddress - }) +function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromContext, onCopyCanonicalAddress, onCopyRawCid, onCopyAddressAtPublicGw }) { + try { + const createSubmenu = (id, contextType, menuBuilder) => { + browser.contextMenus.create({ + 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, + /* no support for 'icons' in Chrome + icons: { + '48': '/ui-kit/icons/stroke_cube.svg' + }, */ + onclick: (context) => onAddFromContext(context, contextType, ipfsAddOptions) + }) + } + 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/*'], + /* no support for 'icons' in Chrome + icons: { + '48': '/ui-kit/icons/stroke_copy.svg' + }, */ + onclick: (context) => handler(context, contextType) + }) + } + 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, contextMenuCopyAddressAtPublicGw, contextType, onCopyAddressAtPublicGw) + createCopierMenuItem(parentId, contextMenuCopyCanonicalAddress, contextType, onCopyCanonicalAddress) + createCopierMenuItem(parentId, contextMenuCopyRawCid, contextType, onCopyRawCid) + } - browser.contextMenus.create({ - id: contextMenuCopyAddressAtPublicGw, - title: browser.i18n.getMessage(contextMenuCopyAddressAtPublicGw), - contexts: copyAddressContexts, - documentUrlPatterns: ['*://*/ipfs/*', '*://*/ipns/*'], - onclick: 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() } @@ -93,26 +153,31 @@ function createContextMenus (getState, runtime, ipfsPathValidator, { onAddToIpfs 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 - const items = [ contextMenuAddToIpfsSelection, - contextMenuAddToIpfsRawCid, - contextMenuAddToIpfsKeepFilename - ] - for (let item of items) { - 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) - browser.contextMenus.update(contextMenuCopyCanonicalAddress, { enabled: ipfsContext }) - browser.contextMenus.update(contextMenuCopyAddressAtPublicGw, { 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 56fa7c2d5..19741410f 100644 --- a/add-on/src/lib/copier.js +++ b/add-on/src/lib/copier.js @@ -1,8 +1,8 @@ 'use strict' const browser = require('webextension-polyfill') -const { safeIpfsPath } = require('./ipfs-path') -const { findUrlForContext } = require('./context-menus') +const { safeIpfsPath, trimHashAndSearch } = require('./ipfs-path') +const { findValueForContext } = require('./context-menus') async function copyTextToClipboard (copyText) { const currentTab = await browser.tabs.query({ active: true, currentWindow: true }).then(tabs => tabs[0]) @@ -34,21 +34,45 @@ async function copyTextToClipboard (copyText) { } } -function createCopier (getState, notify) { +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_copiedCanonicalAddressTitle', rawIpfsAddress) + notify('notify_copiedTitle', rawIpfsAddress) }, - async copyAddressAtPublicGw (context) { - const url = await findUrlForContext(context) + async copyRawCid (context, contextType) { + try { + const ipfs = getIpfs() + const url = await findValueForContext(context, contextType) + const rawIpfsAddress = trimHashAndSearch(safeIpfsPath(url)) + 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) { + 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) + } + } + } + }, + + async copyAddressAtPublicGw (context, contextType) { + const url = await findValueForContext(context, contextType) 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 39d132ec0..7fd94f38a 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, findValueForContext, contextMenuCopyAddressAtPublicGw, contextMenuCopyRawCid, contextMenuCopyCanonicalAddress } = require('./context-menus') const createIpfsProxy = require('./ipfs-proxy') const { showPendingLandingPages } = require('./on-installed') @@ -55,13 +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, { - onAddToIpfs: addFromContext, - onAddToIpfsKeepFilename: (info) => addFromContext(info, { wrapWithDirectory: true }), + onAddFromContext, onCopyCanonicalAddress: copier.copyCanonicalAddress, + onCopyRawCid: copier.copyRawCid, onCopyAddressAtPublicGw: copier.copyAddressAtPublicGw }) modifyRequest = createRequestModifier(getState, dnslinkResolver, ipfsPathValidator, runtime) @@ -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) @@ -191,8 +194,9 @@ module.exports = async function init () { const BrowserActionMessageHandlers = { notification: (message) => notify(message.title, message.message), - copyCanonicalAddress: () => copier.copyCanonicalAddress(), - copyAddressAtPublicGw: () => copier.copyAddressAtPublicGw() + [contextMenuCopyCanonicalAddress]: copier.copyCanonicalAddress, + [contextMenuCopyRawCid]: copier.copyRawCid, + [contextMenuCopyAddressAtPublicGw]: copier.copyAddressAtPublicGw } function handleMessageFromBrowserAction (message) { @@ -256,21 +260,24 @@ module.exports = async function init () { // Context Menu Uploader // ------------------------------------------------------------------- - async function addFromContext (info, options) { + async function onAddFromContext (context, contextType, options) { let result try { - const srcUrl = await findUrlForContext(info) - if (info.selectionText) { - result = await ipfs.files.add(Buffer.from(info.selectionText), options) - } else if (runtime.isFirefox) { - // workaround due to https://github.com/ipfs/ipfs-companion/issues/227 + const dataSrc = await findValueForContext(context, contextType) + if (contextType === 'selection') { + result = await ipfs.files.add(Buffer.from(dataSrc), options) + } 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: info.pageUrl + referrer: context.pageUrl } - // console.log('addFromContext.info', info) - // console.log('addFromContext.fetchOptions', fetchOptions) - const response = await fetch(srcUrl, fetchOptions) + // console.log('onAddFromContext.context', context) + // console.log('onAddFromContext.fetchOptions', fetchOptions) + const response = await fetch(dataSrc, fetchOptions) const blob = await response.blob() const buffer = await new Promise((resolve, reject) => { const reader = new FileReader() @@ -278,13 +285,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(srcUrl, options) } } catch (error) { console.error('Error in upload to IPFS context menu', error) @@ -335,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) } 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..7ef4e9e24 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, contextMenuCopyRawCid, 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(contextMenuCopyAddressAtPublicGw), + onClick: () => onCopy(contextMenuCopyAddressAtPublicGw) })} ${navItem({ - text: browser.i18n.getMessage('panel_copyCurrentPublicGwUrl'), - onClick: onCopyPublicGwAddr + text: browser.i18n.getMessage(contextMenuCopyCanonicalAddress), + onClick: () => onCopy(contextMenuCopyCanonicalAddress) + })} + ${navItem({ + text: browser.i18n.getMessage(contextMenuCopyRawCid), + onClick: () => onCopy(contextMenuCopyRawCid) })} ${!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..976f0f404 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, contextMenuCopyRawCid, 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 contextMenuCopyRawCid: + port.postMessage({ event: contextMenuCopyRawCid }) + 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 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",