Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 53 additions & 13 deletions add-on/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,15 @@
"description": "A menu item in Browser Action pop-up and right-click context menu (panel_copyCurrentPublicGwUrl)"
},
"panel_copyCurrentPublicGwUrlTooltip": {
"message": "A shareable link to this tab that works for anyone, even if they don't use IPFS.",
"message": "A shareable link to this tab that anyone can open. When public gateway sharing is off in Preferences, a native IPFS address is copied instead.",
"description": "A menu item tooltip in Browser Action pop-up (panel_copyCurrentPublicGwUrlTooltip)"
},
"panel_copyCurrentPermalink": {
"message": "Copy Snapshot Link",
"description": "A menu item in Browser Action pop-up (panel_copyCurrentPermalink)"
},
"panel_copyCurrentPermalinkTooltip": {
"message": "A link to a snapshot of this tab at this moment in time; it won't change, even if content changes later. This link works for anyone, even if they don't use IPFS.",
"message": "A link to a snapshot of this tab at this moment in time; it won't change, even if content changes later.",
"description": "A menu item tooltip in Browser Action pop-up (panel_copyCurrentPermalinkTooltip)"
},
"panel_contextMenuViewOnGateway": {
Expand Down Expand Up @@ -259,6 +259,22 @@
"message": "External (Kubo RPC)",
"description": "An option on the Preferences screen (option_ipfsNodeType_external)"
},
"option_ipfsNodeType_external_explainer": {
"message": "IPFS Companion controls your node over the Kubo RPC API, an HTTP interface served by Kubo, the standard IPFS node software. Don't have a node yet? Install Kubo (command line), or IPFS Desktop, a graphical app that runs the same Kubo daemon and provides the same RPC and Web UI in a nicer package.",
"description": "Explainer under the IPFS Node section on the Preferences screen (option_ipfsNodeType_external_explainer)"
},
"option_ipfsNodeType_external_installKubo": {
"message": "Install Kubo (command line)",
"description": "Link under the IPFS Node section on the Preferences screen (option_ipfsNodeType_external_installKubo)"
},
"option_ipfsNodeType_external_installDesktop": {
"message": "Install IPFS Desktop",
"description": "Link under the IPFS Node section on the Preferences screen (option_ipfsNodeType_external_installDesktop)"
},
"option_ipfsNodeType_external_rpcDocs": {
"message": "Kubo RPC API docs",
"description": "Link under the IPFS Node section on the Preferences screen (option_ipfsNodeType_external_rpcDocs)"
},
"option_ipfsNodeType_brave": {
"message": "Provided by Brave",
"description": "An option on the Preferences screen (option_ipfsNodeType_brave)"
Expand Down Expand Up @@ -300,15 +316,15 @@
"description": "An option title on the Preferences screen (option_useSubdomains_title)"
},
"option_useSubdomains_description": {
"message": "Isolate content roots from each other by loading them as subdomains (at *.localhost) and creating a unique origin for each CID, IPNS, or DNSLink record.",
"message": "Isolate content roots from each other by loading them as subdomains (at *.localhost) and creating a unique origin for each CID, IPNS name, or DNSLink site.",
"description": "An option description on the Preferences screen (option_useSubdomains_description)"
},
"option_dnslinkRedirect_title": {
"message": "Load DNSLink Sites from Custom Gateway",
"message": "Load DNSLink Sites from Local Gateway",
"description": "An option title on the Preferences screen (option_dnslinkRedirect_title)"
},
"option_dnslinkRedirect_description": {
"message": "When global gateway redirect is enabled, include DNSLink websites by redirecting them to their respective /ipns/{fqdn} paths at your custom gateway.",
"message": "When redirects to your local gateway are enabled, include DNSLink websites by redirecting them to their respective /ipns/{fqdn} paths.",
"description": "An option description on the Preferences screen (option_dnslinkRedirect_description)"
},
"option_dnslinkDataPreload_title": {
Expand Down Expand Up @@ -340,21 +356,29 @@
"description": "An option description on the Preferences screen (option_enabledOn_description)"
},
"option_publicGatewayUrl_title": {
"message": "Default Public Gateway",
"message": "Public Path Gateway",
"description": "An option title on the Preferences screen (option_publicGatewayUrl_title)"
},
"option_publicGatewayUrl_description": {
"message": "Set the URL of a default public gateway to use in publicly shareable links and as a fallback if your local gateway is unavailable.",
"message": "A public gateway serving links like https://example.com/ipfs/CID. All content shares that one origin, which works everywhere but does not keep sites isolated from each other. Used as a fallback when your local gateway is unavailable, and for shareable links when the subdomain gateway below is empty or cannot serve the address.",
"description": "An option description on the Preferences screen (option_publicGatewayUrl_description)"
},
"option_publicSubdomainGatewayUrl_title": {
"message": "Default Public Subdomain Gateway",
"message": "Public Subdomain Gateway",
"description": "An option title on the Preferences screen (option_publicSubdomainGatewayUrl_title)"
},
"option_publicSubdomainGatewayUrl_description": {
"message": "Set the URL of a default public subdomain gateway for the recovery of broken subdomain gateways.",
"message": "A public gateway serving links like https://CID.ipfs.example.com. Each CID, IPNS name, or DNSLink site gets its own origin, keeping sites isolated the way browsers expect. Preferred for shareable links, and used to repair broken links to other subdomain gateways.",
"description": "An option description on the Preferences screen (option_publicSubdomainGatewayUrl_description)"
},
"option_usePublicGatewaysForShare_title": {
"message": "Use Public Gateways for Shareable Links",
"description": "An option title on the Preferences screen (option_usePublicGatewaysForShare_title)"
},
"option_usePublicGatewaysForShare_description": {
"message": "When on, copied links such as \"Copy Shareable Link\" and \"Copy Snapshot Link\" point at one of the public gateways above and open for everyone, including people who don't run IPFS. When off, or when both gateway URLs above are empty, a native ipfs:// or ipns:// address is copied instead.",
"description": "An option description on the Preferences screen (option_usePublicGatewaysForShare_description)"
},
"option_header_api": {
"message": "Kubo RPC API",
"description": "A section header on the Preferences screen (option_header_api)"
Expand All @@ -380,11 +404,11 @@
"description": "An option title on the Preferences screen (option_automaticMode_title)"
},
"option_automaticMode_description": {
"message": "Automatically enables automatic redirection to your local gateway when the Kubo RPC endpoint is accessible, and disable it when the endpoint is unavailable.",
"message": "Automatically enable redirection to your local gateway when the Kubo RPC endpoint is accessible, and disable it when the endpoint is unavailable.",
"description": "An option description on the Preferences screen (option_automaticMode_description)"
},
"option_automaticMode_description_subtext": {
"message": "Note: disabling this feature will result in static redirects, independent of the Kubo RPC endpoint's availability, and may produce error when your node is offline.",
"message": "Note: disabling this feature will result in static redirects, independent of the Kubo RPC endpoint's availability, and may produce errors when your node is offline.",
"description": "An automatic mode option description on the Preferences screen (option_automaticMode_description_subtext)"
},
"option_header_dnslink": {
Expand Down Expand Up @@ -428,7 +452,7 @@
"description": "An option title on the Preferences screen (option_displayNotifications_title)"
},
"option_displayNotifications_description": {
"message": "Display OS-level notifications when a link is copied to the clipboard,the API state changes, etc.",
"message": "Display OS-level notifications when a link is copied to the clipboard, the API state changes, etc.",
"description": "An option description on the Preferences screen (option_displayNotifications_description)"
},
"option_displayReleaseNotes_title": {
Expand Down Expand Up @@ -480,7 +504,7 @@
"description": "An option title on the Preferences screen (option_recoverFailedHttpRequests_title)"
},
"option_recoverFailedHttpRequests_description": {
"message": "Redirect failed HTTP requests for IPFS resources to the public gateway.",
"message": "Redirect failed requests for IPFS resources to another gateway: a public one when set, otherwise your local one.",
"description": "An option description on the Preferences screen (option_recoverFailedHttpRequests_description)"
},
"option_detectIpfsPathHeader_title": {
Expand Down Expand Up @@ -751,6 +775,22 @@
"message": "Update your IPFS Companion preferences",
"description": "Learn more link on the recovery screen (recovery_page_learn_more)"
},
"recovery_page_publicUrl": {
"message": "Public URL:",
"description": "Label before the public gateway URL on the recovery screen (recovery_page_publicUrl)"
},
"recovery_page_nativeUrl": {
"message": "Native address:",
"description": "Label before the native ipfs:// address on the recovery screen, shown when no public gateway is set (recovery_page_nativeUrl)"
},
"recovery_page_message_p2_native": {
"message": "The link below is a native IPFS address. To open it, run a local IPFS node, or set a public gateway in your preferences.",
"description": "Message on the recovery screen shown when no public gateway is set (recovery_page_message_p2_native)"
},
"recovery_page_button_installDesktop": {
"message": "Install IPFS Desktop",
"description": "Button on the recovery screen shown when no public gateway is set (recovery_page_button_installDesktop)"
},
"request_permissions_page_title": {
"message": "Grant Host Permissions | IPFS Companion",
"description": "Title of the recovery page (recovery_page_title)"
Expand Down
4 changes: 2 additions & 2 deletions add-on/src/lib/copier.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ export default function createCopier (notify, ipfsPathValidator) {

async copyAddressAtPublicGw (context, contextType) {
const url = await findValueForContext(context, contextType)
const publicUrl = await ipfsPathValidator.resolveToPublicUrl(url)
await copyTextToClipboard(publicUrl, notify)
const shareableUrl = await ipfsPathValidator.resolveToShareableUrl(url)
await copyTextToClipboard(shareableUrl, notify)
},

async copyPermalink (context, contextType) {
Expand Down
6 changes: 5 additions & 1 deletion add-on/src/lib/dnslink.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ export default function createDnslinkResolver (getState) {
// to load the correct path from IPFS
// - https://github.com/ipfs/ipfs-companion/issues/298
const ipnsPath = dnslinkResolver.convertToIpnsPath(url)
const gateway = state.redirect && state.localGwAvailable ? state.gwURLString : state.pubGwURLString
// redirect to the local gateway when enabled, and fall back to it when
// no public gateway is configured
const gateway = (state.redirect && state.localGwAvailable) || !state.pubGwURLString
? state.gwURLString
: state.pubGwURLString
return pathAtHttpGateway(ipnsPath, gateway)
}
},
Expand Down
6 changes: 4 additions & 2 deletions add-on/src/lib/inspector.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ export default function createInspector (notify, ipfsPathValidator, getState) {
async viewOnGateway (context, contextType) {
const url = await findValueForContext(context, contextType)
const ipfsPath = ipfsPathValidator.resolveToIpfsPath(url)
const gateway = getState().pubGwURLString
const gatewayUrl = pathAtHttpGateway(ipfsPath, gateway)
const { pubGwURLString, gwURLString } = getState()
// fall back to the local gateway when no public gateway is configured
// (a browser tab can't open a native ipfs:// URI)
const gatewayUrl = pathAtHttpGateway(ipfsPath, pubGwURLString || gwURLString)
await browser.tabs.create({ url: gatewayUrl })
}
// TODO: view in WebUI's Files
Expand Down
27 changes: 17 additions & 10 deletions add-on/src/lib/ipfs-companion.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,14 @@ export default async function init (inQuickImport = false) {
// console.log((sender.tab ? 'Message from a content script:' + sender.tab.url : 'Message from the extension'), request)
if (request.pubGwUrlForIpfsOrIpnsPath) {
const path = request.pubGwUrlForIpfsOrIpnsPath
const { validIpfsOrIpns, resolveToPublicUrl } = ipfsPathValidator
const result = await validIpfsOrIpns(path) ? await resolveToPublicUrl(path) : null
const { validIpfsOrIpns, resolveToPublicUrl, resolveToLocalUrl } = ipfsPathValidator
let result = null
if (await validIpfsOrIpns(path)) {
// linkified hrefs must be loadable http(s) URLs; with no public gateway
// configured the resolver returns a native URI, use the local gateway then
const publicUrl = await resolveToPublicUrl(path)
result = publicUrl && publicUrl.startsWith('http') ? publicUrl : resolveToLocalUrl(path)
}
return { pubGwUrlForIpfsOrIpnsPath: result }
}
}
Expand Down Expand Up @@ -277,19 +283,20 @@ export default async function init (inQuickImport = false) {
const url = info.currentTab.url
info.isIpfsContext = ipfsPathValidator.isIpfsPageActionsContext(url)
if (info.isIpfsContext) {
info.currentTabPublicUrl = await ipfsPathValidator.resolveToPublicUrl(url)
info.currentTabPublicUrl = await ipfsPathValidator.resolveToShareableUrl(url)
info.currentTabContentPath = ipfsPathValidator.resolveToIpfsPath(url)
if (resolveCache.has(url)) {
const [immutableIpfsPath, permalink, cid] = resolveCache.get(url)
const [immutableIpfsPath, cid] = resolveCache.get(url)
info.currentTabImmutablePath = immutableIpfsPath
info.currentTabPermalink = permalink
// resolve the permalink fresh so it tracks the share toggle; the costly
// DAG lookup underneath is memoized, so this stays cheap
info.currentTabPermalink = await ipfsPathValidator.resolveToPermalink(url)
info.currentTabCid = cid
} else {
// run async resolution in the next event loop so it does not block the UI
setTimeout(async () => {
resolveCache.set(url, [
await ipfsPathValidator.resolveToImmutableIpfsPath(url),
await ipfsPathValidator.resolveToPermalink(url),
await ipfsPathValidator.resolveToCid(url)
])
await sendStatusUpdateToBrowserAction()
Expand Down Expand Up @@ -665,12 +672,12 @@ export default async function init (inQuickImport = false) {
state.gwURLString = state.gwURL.toString()
break
case 'publicGatewayUrl':
state.pubGwURL = new URL(change.newValue)
state.pubGwURLString = state.pubGwURL.toString()
state.pubGwURL = change.newValue ? new URL(change.newValue) : undefined
state.pubGwURLString = state.pubGwURL?.toString()
break
case 'publicSubdomainGatewayUrl':
state.pubSubdomainGwURL = new URL(change.newValue)
state.pubSubdomainGwURLString = state.pubSubdomainGwURL.toString()
state.pubSubdomainGwURL = change.newValue ? new URL(change.newValue) : undefined
state.pubSubdomainGwURLString = state.pubSubdomainGwURL?.toString()
break
case 'useCustomGateway':
state.redirect = change.newValue
Expand Down
22 changes: 16 additions & 6 deletions add-on/src/lib/ipfs-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import debug from 'debug'
import browser from 'webextension-polyfill'

import { redirectOptOutHint } from './ipfs-request.js'
import { ipfsContentPath } from './ipfs-path.js'
import { contentPathToSubdomainUrl, ipfsContentPath, pathAtHttpGateway } from './ipfs-path.js'
const log = debug('ipfs-companion:import')
log.error = debug('ipfs-companion:import:error')

export const browserActionFilesCpImportCurrentTab = 'browserActionFilesCpImportCurrentTab'

export function createIpfsImportHandler (getState, getIpfs, ipfsPathValidator, runtime, copier) {
const {
resolveToPublicUrl,
resolveToShareableUrl,
resolveToCid
} = ipfsPathValidator
const ipfsImportHandler = {
Expand All @@ -23,8 +23,9 @@ export function createIpfsImportHandler (getState, getIpfs, ipfsPathValidator, r
if (runtime.hasNativeProtocolHandler) {
return { path, url: `ipfs://${cid}` }
} else {
// open at public GW (will be redirected to local elsewhere, if enabled)
const url = new URL(path, state.pubGwURLString).toString()
// open at public GW (will be redirected to local elsewhere, if enabled);
// fall back to the local gateway when no public gateway is configured
const url = new URL(path, state.pubGwURLString || state.gwURLString).toString()
return { path, url }
}
},
Expand Down Expand Up @@ -67,17 +68,26 @@ export function createIpfsImportHandler (getState, getIpfs, ipfsPathValidator, r
// share wrapping dir
path = `/ipfs/${root.cid}/`
}
const url = await resolveToPublicUrl(path)
const url = await resolveToShareableUrl(path)
await copier.copyTextToClipboard(url)
},

async preloadFilesAtPublicGateway (files) {
const state = getState()
if (!state.preloadAtPublicGateway) return
// preload needs a public gateway; noop when none is configured
if (!state.pubGwURLString && !state.pubSubdomainGwURL) return
for (const file of files) {
if (file && file.cid) {
const { path } = ipfsImportHandler.getIpfsPathAndNativeAddress(file.cid)
const preloadUrl = await resolveToPublicUrl(`${path}#${redirectOptOutHint}`)
const contentPath = `${path}#${redirectOptOutHint}`
// spread the load over a random configured gateway when both are set
const candidates = [
state.pubGwURLString && pathAtHttpGateway(contentPath, state.pubGwURLString),
state.pubSubdomainGwURL && contentPathToSubdomainUrl(contentPath, state.pubSubdomainGwURL)
].filter(Boolean)
if (!candidates.length) continue
const preloadUrl = candidates[Math.floor(Math.random() * candidates.length)]
try {
await fetch(preloadUrl, { method: 'HEAD' })
log('successfully preloaded', file)
Expand Down
Loading
Loading