Skip to content

Commit a7793c7

Browse files
committed
refactor: separate 'api down' from 'offline' state
This changes the meaning of offlinePeerCount to be the state when peerCount is 0, and adds explicit apiDownPeerCount equal -1 to indicate state where extension is unable to read peerCount and has to assume API is down.
1 parent 0f51c6b commit a7793c7

File tree

14 files changed

+60
-53
lines changed

14 files changed

+60
-53
lines changed

add-on/src/landing-pages/welcome/page.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const colorYellow = '#f39021'
1616

1717
function createWelcomePage (i18n) {
1818
return function welcomePage (state, emit) {
19-
const { isIpfsOnline, peerCount } = state
19+
const { apiAvailable, peerCount } = state
2020
const openWebUi = (page) => () => emit('openWebUi', page)
2121

2222
// Set translated title
@@ -25,8 +25,8 @@ function createWelcomePage (i18n) {
2525
return html`
2626
<div class="flex flex-column flex-row-l">
2727
<div id="left-col" class="min-vh-100 flex flex-column justify-center items-center bg-navy white">
28-
${renderCompanionLogo(i18n, isIpfsOnline)}
29-
${isIpfsOnline ? renderWelcome(i18n, peerCount, openWebUi) : renderInstallSteps(i18n, isIpfsOnline)}
28+
${renderCompanionLogo(i18n, apiAvailable)}
29+
${apiAvailable ? renderWelcome(i18n, peerCount, openWebUi) : renderInstallSteps(i18n, apiAvailable)}
3030
</div>
3131
3232
<div id="right-col" class="min-vh-100 w-100 flex flex-column justify-around items-center">
@@ -43,14 +43,14 @@ function createWelcomePage (i18n) {
4343
Render functions for the left side
4444
======================================================== */
4545

46-
const renderCompanionLogo = (i18n, isIpfsOnline) => {
46+
const renderCompanionLogo = (i18n, apiAvailable) => {
4747
const logoPath = '../../../icons'
4848
const logoSize = 128
49-
const stateUnknown = isIpfsOnline === null
49+
const stateUnknown = apiAvailable === null
5050

5151
return html`
5252
<div class="mt4 mb2 flex flex-column justify-center items-center transition-all ${stateUnknown && 'state-unknown'}">
53-
${logo({ path: logoPath, size: logoSize, isIpfsOnline: isIpfsOnline })}
53+
${logo({ path: logoPath, size: logoSize, apiAvailable: apiAvailable })}
5454
<p class="montserrat mt3 mb0 f2">${i18n.getMessage('page_landingWelcome_logo_title')}</p>
5555
</div>
5656
`
@@ -83,10 +83,10 @@ const renderWelcome = (i18n, peerCount, openWebUi) => {
8383
`
8484
}
8585

86-
const renderInstallSteps = (i18n, isIpfsOnline) => {
86+
const renderInstallSteps = (i18n, apiAvailable) => {
8787
const copyClass = 'mv0 white f5 lh-copy'
8888
const anchorClass = 'aqua hover-white'
89-
const stateUnknown = isIpfsOnline === null
89+
const stateUnknown = apiAvailable == null
9090
const svgWidth = 130
9191

9292
const nodeOffSvg = () => html`

add-on/src/landing-pages/welcome/store.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const browser = require('webextension-polyfill')
44

55
function createWelcomePageStore (i18n, runtime) {
66
return function welcomePageStore (state, emitter) {
7-
state.isIpfsOnline = null
7+
state.apiAvailable = null
88
state.peerCount = null
99
state.webuiRootUrl = null
1010
let port
@@ -13,12 +13,10 @@ function createWelcomePageStore (i18n, runtime) {
1313
port = runtime.connect({ name: 'browser-action-port' })
1414
port.onMessage.addListener(async (message) => {
1515
if (message.statusUpdate) {
16-
const webuiRootUrl = message.statusUpdate.webuiRootUrl
17-
const peerCount = message.statusUpdate.peerCount
18-
const isIpfsOnline = peerCount > -1
19-
if (isIpfsOnline !== state.isIpfsOnline || peerCount !== state.peerCount || webuiRootUrl !== state.webuiRootUrl) {
16+
const { webuiRootUrl, peerCount, apiAvailable } = message.statusUpdate
17+
if (apiAvailable !== state.apiAvailable || peerCount !== state.peerCount || webuiRootUrl !== state.webuiRootUrl) {
2018
state.webuiRootUrl = webuiRootUrl
21-
state.isIpfsOnline = isIpfsOnline
19+
state.apiAvailable = apiAvailable
2220
state.peerCount = peerCount
2321
emitter.emit('render')
2422
}

add-on/src/lib/context-menus.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,15 +163,15 @@ function createContextMenus (getState, runtime, ipfsPathValidator, { onAddFromCo
163163
ipfsContext = ipfsPathValidator.isIpfsPageActionsContext(currentTab.url)
164164
}
165165
}
166-
const ifApi = getState().peerCount > -1
166+
const { apiAvailable } = getState()
167167
for (const item of apiMenuItems) {
168-
await browser.contextMenus.update(item, { enabled: ifApi })
168+
await browser.contextMenus.update(item, { enabled: apiAvailable })
169169
}
170170
for (const item of ipfsContextItems) {
171171
await browser.contextMenus.update(item, { enabled: ipfsContext })
172172
}
173173
for (const item of apiAndIpfsContextItems) {
174-
await browser.contextMenus.update(item, { enabled: (ifApi && ipfsContext) })
174+
await browser.contextMenus.update(item, { enabled: (apiAvailable && ipfsContext) })
175175
}
176176
} catch (err) {
177177
log.error('Error updating context menus', err)

add-on/src/lib/dnslink.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ log.error = debug('ipfs-companion:dnslink:error')
88
const IsIpfs = require('is-ipfs')
99
const LRU = require('lru-cache')
1010
const { default: PQueue } = require('p-queue')
11-
const { offlinePeerCount } = require('./state')
1211
const { ipfsContentPath, sameGateway, pathAtHttpGateway } = require('./ipfs-path')
1312

1413
module.exports = function createDnslinkResolver (getState) {
@@ -123,7 +122,7 @@ module.exports = function createDnslinkResolver (getState) {
123122
const state = getState()
124123
let apiProvider
125124
// TODO: fix DNS resolver for ipfsNodeType='embedded:chromesockets', for now use ipfs.io
126-
if (!state.ipfsNodeType.startsWith('embedded') && state.peerCount !== offlinePeerCount) {
125+
if (state.localGwAvailable && state.apiAvailable) {
127126
// Use gw port so it can be a GET:
128127
// Chromium does not execute onBeforeSendHeaders for synchronous calls
129128
// made from the same extension context as onBeforeSendHeaders

add-on/src/lib/ipfs-companion.js

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const pMemoize = require('p-memoize')
1111
const LRU = require('lru-cache')
1212
const all = require('it-all')
1313
const { optionDefaults, storeMissingOptions, migrateOptions, guiURLString, safeURL } = require('./options')
14-
const { initState, offlinePeerCount } = require('./state')
14+
const { initState, offlinePeerCount, apiDownPeerCount } = require('./state')
1515
const { createIpfsPathValidator, sameGateway } = require('./ipfs-path')
1616
const createDnslinkResolver = require('./dnslink')
1717
const { createRequestModifier } = require('./ipfs-request')
@@ -257,6 +257,7 @@ module.exports = async function init () {
257257
importDir: state.importDir,
258258
openViaWebUI: state.openViaWebUI,
259259
apiURLString: dropSlash(state.apiURLString),
260+
apiAvailable: state.apiAvailable,
260261
redirect: state.redirect,
261262
enabledOn: state.enabledOn,
262263
disabledOn: state.disabledOn,
@@ -462,13 +463,18 @@ module.exports = async function init () {
462463
}
463464

464465
async function getSwarmPeerCount () {
465-
if (!ipfs) return offlinePeerCount
466+
if (!ipfs) return apiDownPeerCount
466467
try {
467468
const peerInfos = await ipfs.swarm.peers({ timeout: 2500 })
468469
return peerInfos.length
469470
} catch (error) {
471+
if (error.message.includes('action must be run in online mode')) {
472+
// node is running in offline mode (ipfs daemon --offline)
473+
// https://github.com/ipfs-shipyard/ipfs-companion/issues/790
474+
return offlinePeerCount // ipfs daemon --offline
475+
}
470476
console.error(`Error while ipfs.swarm.peers: ${error}`)
471-
return offlinePeerCount
477+
return apiDownPeerCount
472478
}
473479
}
474480

@@ -495,11 +501,11 @@ module.exports = async function init () {
495501

496502
let badgeText, badgeColor, badgeIcon
497503
badgeText = state.peerCount.toString()
498-
if (state.peerCount > 0) {
504+
if (state.peerCount > offlinePeerCount) {
499505
// All is good (online with peers)
500506
badgeColor = '#418B8E'
501507
badgeIcon = '/icons/ipfs-logo-on.svg'
502-
} else if (state.peerCount === 0) {
508+
} else if (state.peerCount === offlinePeerCount) {
503509
// API is online but no peers
504510
badgeColor = 'red'
505511
badgeIcon = '/icons/ipfs-logo-on.svg'
@@ -578,13 +584,11 @@ module.exports = async function init () {
578584

579585
function updateAutomaticModeRedirectState (oldPeerCount, newPeerCount) {
580586
// enable/disable gw redirect based on API going online or offline
581-
// newPeerCount === -1 currently implies node is offline.
582-
// TODO: use `node.isOnline()` if available (js-ipfs)
583587
if (state.automaticMode && state.localGwAvailable) {
584-
if (oldPeerCount === offlinePeerCount && newPeerCount > offlinePeerCount && !state.redirect) {
588+
if (oldPeerCount === apiDownPeerCount && newPeerCount > apiDownPeerCount && !state.redirect) {
585589
browser.storage.local.set({ useCustomGateway: true })
586590
.then(() => notify('notify_apiOnlineTitle', 'notify_apiOnlineAutomaticModeMsg'))
587-
} else if (newPeerCount === offlinePeerCount && state.redirect) {
591+
} else if (newPeerCount === apiDownPeerCount && state.redirect) {
588592
browser.storage.local.set({ useCustomGateway: false })
589593
.then(() => notify('notify_apiOfflineTitle', 'notify_apiOfflineAutomaticModeMsg'))
590594
}

add-on/src/lib/state.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
const isFQDN = require('is-fqdn')
55
const { safeURL } = require('./options')
66
const { braveJsIpfsWebuiCid } = require('./precache')
7-
const offlinePeerCount = -1
7+
const offlinePeerCount = 0 // always the case when running: ipfs daemon --offline
8+
const apiDownPeerCount = -1 // unable to read, most likely API is down
89

910
function initState (options, overrides) {
1011
// we store options and some pregenerated values to avoid async storage
@@ -48,6 +49,12 @@ function initState (options, overrides) {
4849
// TODO: make quick fetch to confirm it works?
4950
get: function () { return this.ipfsNodeType !== 'embedded' }
5051
})
52+
Object.defineProperty(state, 'apiAvailable', {
53+
// TODO: when we move away from constantly polling in the backgroun,
54+
// this can be replaced with ipfs.id check to confirm it works + memoize for ipfsApiPollMs
55+
// that way there is no need for polling, api check would execute only when user actualy needs it
56+
get: function () { return this.peerCount > apiDownPeerCount }
57+
})
5158
Object.defineProperty(state, 'webuiRootUrl', {
5259
get: function () {
5360
// Did user opt-in for rolling release published on DNSLink?
@@ -67,3 +74,4 @@ function initState (options, overrides) {
6774

6875
exports.initState = initState
6976
exports.offlinePeerCount = offlinePeerCount
77+
exports.apiDownPeerCount = apiDownPeerCount

add-on/src/popup/browser-action/context-actions.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,15 @@ function contextActions ({
3232
isPinning,
3333
isUnPinning,
3434
isPinned,
35-
isIpfsOnline,
36-
isApiAvailable,
35+
apiAvailable,
3736
onToggleSiteIntegrations,
3837
onViewOnGateway,
3938
onCopy,
4039
onPin,
4140
onUnPin
4241
}) {
43-
const activeCidResolver = active && isIpfsOnline && isApiAvailable && currentTabCid
44-
const activePinControls = active && isApiAvailable
42+
const activeCidResolver = active && apiAvailable && currentTabCid
43+
const activePinControls = active && apiAvailable
4544
const activeViewOnGateway = (currentTab) => {
4645
if (!currentTab) return false
4746
const { url } = currentTab

add-on/src/popup/browser-action/gateway-status.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ function statusEntry ({ label, labelLegend, value, check, itemClass = '', valueC
88
const offline = browser.i18n.getMessage('panel_statusOffline')
99
label = label ? browser.i18n.getMessage(label) : null
1010
labelLegend = labelLegend ? browser.i18n.getMessage(labelLegend) : label
11-
value = value || value === 0 ? value : offline
11+
value = value || offline
1212
return html`
1313
<div class="flex mb1 ${check ? '' : 'o-60'} ${itemClass}" title="${labelLegend}">
1414
<span class="w-40 f7 ttu no-user-select">${label}</span>

add-on/src/popup/browser-action/header.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const ipfsVersion = require('./ipfs-version')
99
const gatewayStatus = require('./gateway-status')
1010

1111
module.exports = function header (props) {
12-
const { ipfsNodeType, active, onToggleActive, onOpenPrefs, isIpfsOnline, onOpenWelcomePage } = props
12+
const { ipfsNodeType, active, onToggleActive, onOpenPrefs, apiAvailable, onOpenWelcomePage } = props
1313
return html`
1414
<div class="br2 br--top ba bw1 b--white ipfs-gradient-0">
1515
<div class="pt3 pr3 pb2 pl3 no-user-select flex justify-between items-center">
@@ -22,7 +22,7 @@ module.exports = function header (props) {
2222
size: 54,
2323
path: '../../../icons',
2424
ipfsNodeType,
25-
isIpfsOnline: (active && isIpfsOnline)
25+
apiAvailable
2626
})}
2727
</div>
2828
<div class="flex flex-column ml2 white ${active ? '' : 'o-40'}">

add-on/src/popup/browser-action/store.js

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,13 @@ module.exports = (state, emitter) => {
2222
isPinned: false,
2323
// IPFS details
2424
ipfsNodeType: 'external',
25-
isIpfsOnline: false,
25+
apiAvailable: false,
2626
ipfsApiUrl: null,
2727
publicGatewayUrl: null,
2828
publicSubdomainGatewayUrl: null,
2929
gatewayAddress: null,
3030
swarmPeers: null,
3131
gatewayVersion: null,
32-
isApiAvailable: false,
3332
// isRedirectContext
3433
currentTab: null,
3534
currentFqdn: null,
@@ -229,7 +228,7 @@ module.exports = (state, emitter) => {
229228
state.ipfsApiUrl = null
230229
state.gatewayVersion = null
231230
state.swarmPeers = null
232-
state.isIpfsOnline = false
231+
state.apiAvailable = false
233232
}
234233
try {
235234
await browser.storage.local.set({ active: state.active })
@@ -257,7 +256,7 @@ module.exports = (state, emitter) => {
257256
// Note: access to background page will be denied in Private Browsing mode
258257
const ipfs = await getIpfsApi()
259258
// There is no point in displaying actions that require API interaction if API is down
260-
const apiIsUp = ipfs && status && status.peerCount >= 0
259+
const apiIsUp = ipfs && status && status.apiAvailable
261260
if (apiIsUp) await updatePinnedState(ipfs, status)
262261
}
263262
}
@@ -273,15 +272,14 @@ module.exports = (state, emitter) => {
273272
state.gatewayAddress = status.pubGwURLString
274273
}
275274
// Import requires access to the background page (https://github.com/ipfs-shipyard/ipfs-companion/issues/477)
276-
state.isApiAvailable = state.active && !!(await getBackgroundPage()) && !browser.extension.inIncognitoContext // https://github.com/ipfs-shipyard/ipfs-companion/issues/243
277-
state.swarmPeers = !state.active || status.peerCount === -1 ? null : status.peerCount
278-
state.isIpfsOnline = state.active && status.peerCount > -1
275+
state.apiAvailable = state.active && status.apiAvailable && !!(await getBackgroundPage()) && !browser.extension.inIncognitoContext // https://github.com/ipfs-shipyard/ipfs-companion/issues/243
276+
state.swarmPeers = state.apiAvailable ? status.peerCount : null
279277
state.gatewayVersion = state.active && status.gatewayVersion ? status.gatewayVersion : null
280278
state.ipfsApiUrl = state.active ? status.apiURLString : null
281279
} else {
282280
state.ipfsNodeType = 'external'
283281
state.swarmPeers = null
284-
state.isIpfsOnline = false
282+
state.apiAvailable = false
285283
state.gatewayVersion = null
286284
state.isIpfsContext = false
287285
state.isRedirectContext = false

add-on/src/popup/browser-action/tools.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ const navItem = require('./nav-item')
88
module.exports = function tools ({
99
active,
1010
ipfsNodeType,
11-
isIpfsOnline,
12-
isApiAvailable,
11+
apiAvailable,
1312
onQuickImport,
1413
onOpenWebUi
1514
}) {
16-
const activeQuickImport = active && isApiAvailable
17-
const activeWebUI = active && isApiAvailable && ipfsNodeType !== 'embedded'
15+
const localGwAvailable = ipfsNodeType !== 'embedded'
16+
const activeQuickImport = active && apiAvailable
17+
const activeWebUI = active && apiAvailable && localGwAvailable
1818

1919
return html`
2020
<div class="fade-in pv1">

add-on/src/popup/logo.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33

44
const html = require('choo/html')
55

6-
function logo ({ path, size = 52, ipfsNodeType = 'external', isIpfsOnline = true, heartbeat = true }) {
6+
function logo ({ path, size = 52, ipfsNodeType = 'external', apiAvailable = true, heartbeat = true }) {
77
const logoTypePrefix = ipfsNodeType.startsWith('embedded') ? 'js-' : ''
8-
const logoFileName = `${logoTypePrefix}ipfs-logo-${isIpfsOnline ? 'on' : 'off'}.svg`
8+
const logoFileName = `${logoTypePrefix}ipfs-logo-${apiAvailable ? 'on' : 'off'}.svg`
99
return html`
1010
<img
1111
alt="IPFS"
1212
src="${path}/${logoFileName}"
13-
class="v-mid ${isIpfsOnline ? '' : 'o-40'} ${isIpfsOnline && heartbeat ? 'heartbeat' : ''}"
13+
class="v-mid ${apiAvailable ? '' : 'o-40'} ${apiAvailable && heartbeat ? 'heartbeat' : ''}"
1414
style="width:${size}px; height:${size}px" />
1515
`
1616
}

add-on/src/popup/page-action/header.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module.exports = function header ({ isIpfsContext, pageActionTitle }) {
1313
size: 20,
1414
path: '../../../icons',
1515
ipfsNodeType: 'external',
16-
isIpfsOnline: true,
16+
apiAvailable: true,
1717
heartbeat: false
1818
})} <span class="pl1 f6 fw4 v-mid">${pageActionTitle || '…'}</span>
1919
</h2>

add-on/src/popup/quick-import.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const choo = require('choo')
88
const html = require('choo/html')
99
const logo = require('./logo')
1010
const externalApiClient = require('../lib/ipfs-client/external')
11+
const { offlinePeerCount } = require('../lib/state')
1112
const all = require('it-all')
1213
const drop = require('drag-and-drop-files')
1314
const filesize = require('filesize')
@@ -22,7 +23,7 @@ app.mount('#root')
2223

2324
function quickImportStore (state, emitter) {
2425
state.message = ''
25-
state.peerCount = ''
26+
state.peerCount = offlinePeerCount
2627
state.ipfsNodeType = 'external'
2728
state.expandOptions = false
2829
state.openViaWebUI = true

0 commit comments

Comments
 (0)