Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: recovery page when local gateway is unreachable #1125

Merged
merged 42 commits into from
Feb 3, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b9232a1
Adding recovery options
whizzzkid Dec 23, 2022
3ac7e12
fixing packages
whizzzkid Dec 23, 2022
5103b60
hooking components
whizzzkid Dec 23, 2022
3a53c02
reverting changes
whizzzkid Dec 29, 2022
3256407
adding additional state prop getters
whizzzkid Dec 29, 2022
fb301e0
Adding hookup for local gateway down.
whizzzkid Dec 29, 2022
df31834
Refactor
whizzzkid Dec 29, 2022
a3feda0
Marking recovery page as accessible
whizzzkid Dec 29, 2022
e389d45
unnecessary logging
whizzzkid Dec 29, 2022
133ca93
lint
whizzzkid Dec 29, 2022
cd5f4bf
Fixing UI + encdec URIs
whizzzkid Dec 29, 2022
03c14c6
Making page recover if node is online.
whizzzkid Dec 30, 2022
23412e4
Merge branch 'main' into feat/recover-client
whizzzkid Jan 3, 2023
a438c19
Refactor + Implement Design Change
whizzzkid Jan 5, 2023
0561ea3
Merge branch 'main' into feat/recover-client
whizzzkid Jan 13, 2023
e31cbbf
Update add-on/src/lib/ipfs-request.js
whizzzkid Jan 13, 2023
0561045
Update add-on/_locales/en/messages.json
whizzzkid Jan 13, 2023
4a139d8
Update add-on/src/recovery/recovery.js
whizzzkid Jan 13, 2023
5ce8b18
Update add-on/_locales/en/messages.json
whizzzkid Jan 13, 2023
baad760
Update add-on/_locales/en/messages.json
whizzzkid Jan 13, 2023
4ff97b6
Update add-on/src/recovery/recovery.html
whizzzkid Jan 13, 2023
d3018f4
Update add-on/src/recovery/recovery.js
whizzzkid Jan 13, 2023
41881ec
Update add-on/src/recovery/recovery.js
whizzzkid Jan 13, 2023
8a3db25
moving function to ipfs-path
whizzzkid Jan 13, 2023
ab2d990
unnecessary logging statement
whizzzkid Jan 13, 2023
956863a
fixed logo title not rendering
whizzzkid Jan 13, 2023
885d63f
fix: recovery logic
whizzzkid Jan 13, 2023
7454d62
Refactoring
whizzzkid Jan 13, 2023
5b617fd
fix: styling in firefox
whizzzkid Jan 13, 2023
66485d0
Implementing new UI and copy
whizzzkid Jan 13, 2023
2ea33c2
lint
whizzzkid Jan 13, 2023
4c5642b
feat: Adding regression test
whizzzkid Jan 13, 2023
20f71b6
Merge branch 'main' into feat/recover-client
whizzzkid Jan 18, 2023
414df96
Merge branch 'main' into feat/recover-client
whizzzkid Jan 24, 2023
c6e6dc5
Merge branch 'main' into feat/recover-client
whizzzkid Jan 24, 2023
c7e6956
Merge branch 'main' into feat/recover-client
whizzzkid Jan 31, 2023
ba6e573
feat: :sparkles: Add telemetry for recovery mechanism
whizzzkid Jan 31, 2023
0ca8d4a
Merge branch 'main' into feat/recover-client
whizzzkid Feb 3, 2023
2d8f803
fix(ux): :wastebasket: getting rid of header, companion logo and addi…
whizzzkid Feb 3, 2023
97be393
fix(ux): :lipstick: Fixing CSS
whizzzkid Feb 3, 2023
3e76bf8
fix: :rotating_light: fix lint
whizzzkid Feb 3, 2023
6f53132
style: adjust text and css
lidel Feb 3, 2023
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
16 changes: 16 additions & 0 deletions add-on/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -726,5 +726,21 @@
"page_landingWelcome_projects_title": {
"message": "Related Projects",
"description": "Projects section title (page_landingWelcome_projects_title)"
},
"recovery_page_title" : {
"message": "Node Offline | IPFS Companion",
lidel marked this conversation as resolved.
Show resolved Hide resolved
"description": "Title of the recovery page (recovery_page_title)"
},
"recovery_page_header" : {
"message": "Node is Offline",
"description": "Main header on the recovery screen (recovery_page_header)"
},
"recovery_page_message": {
"message": "IPFS Companion is unable to connect to the IPFS node. Would you like to connect to a public gateway instead?",
"description": "Message on the recovery screen (recovery_page_message)"
},
"recovery_page_button": {
"message": "Continue to a public gateway",
whizzzkid marked this conversation as resolved.
Show resolved Hide resolved
"description": "Button on the recovery screen (recovery_page_button)"
}
}
5 changes: 4 additions & 1 deletion add-on/manifest.common.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@
"icons/png/ipfs-logo-off_38.png",
"icons/png/ipfs-logo-off_128.png",
"icons/ipfs-logo-on.svg",
"icons/ipfs-logo-off.svg"
"icons/ipfs-logo-off.svg",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure how this is persisted on CI, locally this gets overwritten.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@whizzzkid This file is a template used for generating final add-on/manifest.json for specific runtime: firefox, chromium, or brave (production brave used chromium, but we need this for local testing).

Generation happens in ci/update-manifest.sh (triggered during build with some env vars). It was way more complex in the past when we had beta channels, we could simplify build pipeline these days, but did not bother since it works fine.

"dist/recovery/recovery.css",
"dist/recovery/recovery.html",
"dist/recovery/recovery.js"
lidel marked this conversation as resolved.
Show resolved Hide resolved
],
"content_security_policy": "script-src 'self'; object-src 'self'; frame-src 'self';",
"default_locale": "en"
Expand Down
2 changes: 1 addition & 1 deletion add-on/src/landing-pages/welcome/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default function createWelcomePage (i18n) {
Render functions for the left side
======================================================== */

const renderCompanionLogo = (i18n, isIpfsOnline) => {
export const renderCompanionLogo = (i18n, isIpfsOnline) => {
const logoPath = '../../../icons'
const logoSize = 128
const stateUnknown = isIpfsOnline === null
Expand Down
1 change: 1 addition & 0 deletions add-on/src/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@

export const welcomePage = '/dist/landing-pages/welcome/index.html'
export const optionsPage = '/dist/options/options.html'
export const recoveryPagePath = '/dist/recovery/recovery.html'
export const tickMs = 250 // no CPU spike, but still responsive enough
1 change: 1 addition & 0 deletions add-on/src/lib/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const dropSlash = url => url.replace(/\/$/, '')
whizzzkid marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion add-on/src/lib/ipfs-companion.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import createRuntimeChecks from './runtime-checks.js'
import { createContextMenus, findValueForContext, contextMenuCopyAddressAtPublicGw, contextMenuCopyRawCid, contextMenuCopyCanonicalAddress, contextMenuViewOnGateway, contextMenuCopyPermalink, contextMenuCopyCidAddress } from './context-menus.js'
import { registerSubdomainProxy } from './http-proxy.js'
import { runPendingOnInstallTasks } from './on-installed.js'
import { dropSlash } from './helpers.js'
const log = debug('ipfs-companion:main')
log.error = debug('ipfs-companion:main:error')

Expand Down Expand Up @@ -209,7 +210,6 @@ export default async function init () {

async function sendStatusUpdateToBrowserAction () {
if (!browserActionPort) return
const dropSlash = url => url.replace(/\/$/, '')
const currentTab = await browser.tabs.query({ active: true, currentWindow: true }).then(tabs => tabs[0])
const { version } = browser.runtime.getManifest()
const info = {
Expand Down
14 changes: 13 additions & 1 deletion add-on/src/lib/ipfs-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import isFQDN from 'is-fqdn'
import { pathAtHttpGateway, sameGateway, ipfsUri } from './ipfs-path.js'
import { safeURL } from './options.js'
import { braveNodeType } from './ipfs-client/brave.js'
import { recoveryPagePath } from './constants.js'
import { dropSlash } from './helpers.js'

const log = debug('ipfs-companion:request')
log.error = debug('ipfs-companion:request:error')

Expand Down Expand Up @@ -140,6 +143,13 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida
const state = getState()
if (!state.active) return

// When local IPFS node is disabled, show recovery page where user can redirect
whizzzkid marked this conversation as resolved.
Show resolved Hide resolved
// to public gateway.
if (!state.connected && request.type === 'main_frame' && sameGateway(request.url, state.gwURL)) {
const publicUri = ipfsPathValidator.resolveToPublicUrl(request.url, state.pubGwURLString)
return { redirectUrl: `${dropSlash(runtimeRoot)}${recoveryPagePath}#${publicUri}` }
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once we know we're not connected we can load the recovery page and append the public URI as a hash


// When Subdomain Proxy is enabled we normalize address bar requests made
// to the local gateway and replace raw IP with 'localhost' hostname to
// take advantage of subdomain redirect provided by go-ipfs >= 0.5
Expand Down Expand Up @@ -451,7 +461,8 @@ export function createRequestModifier (getState, dnslinkResolver, ipfsPathValida
}

// Returns a string with URL at the active gateway (local or public)
function redirectToGateway (request, url, state, ipfsPathValidator, runtime) {
function redirectToGateway(request, url, state, ipfsPathValidator, runtime) {
log({ request, url, state, ipfsPathValidator, runtime });
const { resolveToPublicUrl, resolveToLocalUrl } = ipfsPathValidator
let redirectUrl = state.localGwAvailable ? resolveToLocalUrl(url) : resolveToPublicUrl(url)

Expand Down Expand Up @@ -604,6 +615,7 @@ function unhandledIpfsPath (requestUrl) {
function normalizedUnhandledIpfsProtocol (request, pubGwUrl) {
let path = unhandledIpfsPath(request.url)
path = fixupDnslinkPath(path) // /ipfs/example.com → /ipns/example.com
console.log('^^^^^$', path, request, pubGwUrl)
if (isIPFS.path(path)) {
// replace search query with a request to a public gateway
// (will be redirected later, if needed)
Expand Down
12 changes: 10 additions & 2 deletions add-on/src/lib/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { safeURL, isHostname } from './options.js'

export const offlinePeerCount = -1

export function initState (options, overrides) {
// we store options and some pregenerated values to avoid async storage
// reads and minimize performance impact on overall browsing experience
Expand Down Expand Up @@ -40,8 +41,15 @@ export function initState (options, overrides) {
return false
}
}
// TODO state.connected ~= state.peerCount > 0
// TODO state.nodeActive ~= API is online,eg. state.peerCount > offlinePeerCount
// TODO refactor this into a class. It's getting too big and messy.
Object.defineProperty(state, 'connected', {
// TODO: make quick fetch to confirm it works?
get: function () { return this.peerCount > offlinePeerCount + 1 }
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This + 1 will cause bugs when your local node is running, but your internet connection goes down. Imagine you opened something, it got cached on your IPFS node, then you shut down your laptop, go to a place without iternet and you opened laptop and restarted browser and there is a tab with localhost gateway URL.

User should be able to browse IPFS resources cached on their IPFS node when WAN is down, but due to peerCount > 0 requirement here Companion will show recovery page, forbidding them from leveraging IPFS cache.

Mind removing this and only triggering recovery when peerCount === offlinePeerCount?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right I did not think about that.

Object.defineProperty(state, 'nodeActive', {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if things defined via Object.defineProperty will survive postMessage serialization present in places where we use browser.runtime.connect ?

I vaguely remember that we did not do getters like this in the past because it did not survive serialization (maybe browser fixed it these days?). That is why we read peerCount field directly (without getters) and compare it to offlinePeerCount const.

By adding these getters here, we are having two ways of doing online check in the codebase, which over time leads to errors.

My suggestion is to remove these Object.defineProperty to keep this PR scope small, and if you want to add them, do that in separate PR that also refactors all places which act on peerCount (if possible, but I feel it may not be worth the work).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like it does: https://github.com/ipfs/ipfs-companion/pull/1125/files#diff-cf4a768af4d2baab2a1c0a5862f50af6239b155754f4a9ea90b43e465f115d88R148

I need this to validate if we need to recover the client. However I didn't want to touch too much of this, I'm just trying to follow the adjacent code pattern. I created #1129 to tackle this again in the future.

// TODO: make quick fetch to confirm it works?
get: function () { return this.peerCount !== offlinePeerCount }
})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented the missing TODOs, will move this to a proper class later.

Object.defineProperty(state, 'localGwAvailable', {
// TODO: make quick fetch to confirm it works?
get: function () { return this.ipfsNodeType !== 'embedded' }
Expand Down
16 changes: 16 additions & 0 deletions add-on/src/recovery/recovery.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@import url('~tachyons/css/tachyons.css');
@import url('~ipfs-css/ipfs.css');

header {
border-color: #69c4cd;
text-transform: uppercase;
}

#header-logo {
height: 50px;
width: 117.5px;
}

.recovery-root {
text-align: center;
}
25 changes: 25 additions & 0 deletions add-on/src/recovery/recovery.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
lidel marked this conversation as resolved.
Show resolved Hide resolved
<html>
<head>
<title>IPFS Node is Offline</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlo89/56ZQ/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUjDu1lo89/6mhTP+zrVP/nplD/5+aRK8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHNiIS6Wjz3/ubFY/761W/+vp1D/urRZ/8vDZf/GvmH/nplD/1BNIm8AAAAAAAAAAAAAAAAAAAAAAAAAAJaPPf+knEj/vrVb/761W/++tVv/r6dQ/7q0Wf/Lw2X/y8Nl/8vDZf+tpk7/nplD/wAAAAAAAAAAAAAAAJaPPf+2rVX/vrVb/761W/++tVv/vrVb/6+nUP+6tFn/y8Nl/8vDZf/Lw2X/y8Nl/8G6Xv+emUP/AAAAAAAAAACWjz3/vrVb/761W/++tVv/vrVb/761W/+vp1D/urRZ/8vDZf/Lw2X/y8Nl/8vDZf/Lw2X/nplD/wAAAAAAAAAAlo89/761W/++tVv/vrVb/761W/++tVv/r6dQ/7q0Wf/Lw2X/y8Nl/8vDZf/Lw2X/y8Nl/56ZQ/8AAAAAAAAAAJaPPf++tVv/vrVb/761W/++tVv/vbRa/5aPPf+emUP/y8Nl/8vDZf/Lw2X/y8Nl/8vDZf+emUP/AAAAAAAAAACWjz3/vrVb/761W/++tVv/vrVb/5qTQP+inkb/op5G/6KdRv/Lw2X/y8Nl/8vDZf/Lw2X/nplD/wAAAAAAAAAAlo89/761W/++tVv/sqlS/56ZQ//LxWb/0Mlp/9DJaf/Kw2X/oJtE/7+3XP/Lw2X/y8Nl/56ZQ/8AAAAAAAAAAJaPPf+9tFr/mJE+/7GsUv/Rymr/0cpq/9HKav/Rymr/0cpq/9HKav+xrFL/nplD/8vDZf+emUP/AAAAAAAAAACWjz3/op5G/9HKav/Rymr/0cpq/9HKav/Rymr/0cpq/9HKav/Rymr/0cpq/9HKav+inkb/nplD/wAAAAAAAAAAAAAAAKKeRv+3slb/0cpq/9HKav/Rymr/0cpq/9HKav/Rymr/0cpq/9HKav+1sFX/op5G/wAAAAAAAAAAAAAAAAAAAAAAAAAAop5GUKKeRv/Nxmf/0cpq/9HKav/Rymr/0cpq/83GZ/+inkb/op5GSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAop5G16KeRv/LxWb/y8Vm/6KeRv+inkaPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAop5G/6KeRtcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/n8AAPgfAADwDwAAwAMAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAwAMAAPAPAAD4HwAA/n8AAA==" />
<link rel="stylesheet" href="/dist/bundles/uiCommons.css">
<link rel="stylesheet" href="/dist/bundles/recoveryPage.css">
</head>
<body>
<header class="flex-l items-center pa3 bg-navy bb bw3 border-aqua tc tl-l justify-between">
<div class="flex-none v-mid">
<svg alt="IPFS" id="header-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 553 235.3"><path d="M239 63h17.8v105H239V63zm35.6 0h36.3c7.9 0 14.5.9 19.6 2.6s9.2 4.1 12.1 7.1a24.45 24.45 0 016.2 10.2 40.75 40.75 0 011.8 12.1 45.69 45.69 0 01-1.8 12.9 26.58 26.58 0 01-6.2 10.8 30.59 30.59 0 01-12.1 7.3c-5.1 1.8-11.5 2.7-19.3 2.7h-19.1V168h-17.5V63zm36.2 51a38.37 38.37 0 0011.1-1.3 16.3 16.3 0 006.8-3.7 13.34 13.34 0 003.5-5.8 29.75 29.75 0 001-7.6 25.68 25.68 0 00-1-7.7 12 12 0 00-3.6-5.5 17.15 17.15 0 00-6.9-3.4 41.58 41.58 0 00-10.9-1.2h-18.5V114h18.5zm119.9-51v15.3h-49.2V108h46.3v15.4h-46.3V168h-17.8V63h67zm26.2 72.9c.8 6.9 3.3 11.9 7.4 15s10.4 4.7 18.6 4.7a32.61 32.61 0 0010.1-1.3 20.52 20.52 0 006.6-3.5 12 12 0 003.5-5.2 19.08 19.08 0 001-6.4 16.14 16.14 0 00-.7-4.9 12.87 12.87 0 00-2.6-4.5 16.59 16.59 0 00-5.1-3.6 35 35 0 00-8.2-2.4l-13.4-2.5a89.76 89.76 0 01-14.1-3.7 33.51 33.51 0 01-10.4-5.8 22.28 22.28 0 01-6.3-8.8 34.1 34.1 0 01-2.1-12.7 26 26 0 0111.3-22.4 36.35 36.35 0 0112.6-5.6 65.89 65.89 0 0115.8-1.8c7.2 0 13.3.8 18.2 2.5a34.46 34.46 0 0111.9 6.5 28.21 28.21 0 016.9 9.3 42.1 42.1 0 013.2 11l-16.8 2.6c-1.4-5.9-3.7-10.2-7.1-13.1s-8.7-4.3-16.1-4.3a43.9 43.9 0 00-10.5 1.1 19.47 19.47 0 00-6.8 3.1 11.63 11.63 0 00-3.7 4.6 14.08 14.08 0 00-1.1 5.4c0 4.6 1.2 8 3.7 10.3s6.9 4 13.2 5.3l14.5 2.8c11.1 2.1 19.2 5.6 24.4 10.5s7.8 12.1 7.8 21.4a31.37 31.37 0 01-2.4 12.3 25.27 25.27 0 01-7.4 9.8 36.58 36.58 0 01-12.4 6.6 56 56 0 01-17.3 2.4c-13.4 0-24-2.8-31.6-8.5s-11.9-14.4-12.6-26.2h18z" fill="#fff"/><path fill="#469ea2" d="M30.3 164l84 48.5 84-48.5V67l-84-48.5-84 48.5v97z"/><path d="M105.7 30.1l-61 35.2a18.19 18.19 0 010 3.3l60.9 35.2a14.55 14.55 0 0117.3 0l60.9-35.2a18.19 18.19 0 010-3.3L123 30.1a14.55 14.55 0 01-17.3 0zm84 48.2l-61 35.6a14.73 14.73 0 01-8.6 15l.1 70a15.57 15.57 0 012.8 1.6l60.9-35.2a14.73 14.73 0 018.6-15V79.9a20 20 0 01-2.8-1.6zm-150.8.4a15.57 15.57 0 01-2.8 1.6v70.4a14.38 14.38 0 018.6 15l60.9 35.2a15.57 15.57 0 012.8-1.6v-70.4a14.38 14.38 0 01-8.6-15L38.9 78.7z" fill="#6acad1"/><path fill="#469ea2" d="M114.3 29l75.1 43.4v86.7l-75.1 43.4-75.1-43.4V72.3L114.3 29m0-10.3l-84 48.5v97l84 48.5 84-48.5v-97l-84-48.5z"/><path fill="#469ea2" d="M114.9 132h-1.2A15.66 15.66 0 0198 116.3v-1.2a15.66 15.66 0 0115.7-15.7h1.2a15.66 15.66 0 0115.7 15.7v1.2a15.66 15.66 0 01-15.7 15.7zm0 64.5h-1.2a15.65 15.65 0 00-13.7 8l14.3 8.2 14.3-8.2a15.65 15.65 0 00-13.7-8zm83.5-48.5h-.6a15.66 15.66 0 00-15.7 15.7v1.2a15.13 15.13 0 002 7.6l14.3-8.3V148zm-14.3-89a15.4 15.4 0 00-2 7.6v1.2a15.66 15.66 0 0015.7 15.7h.6V67.2L184.1 59zm-69.8-40.3L100 26.9a15.73 15.73 0 0013.7 8.1h1.2a15.65 15.65 0 0013.7-8l-14.3-8.3zM44.6 58.9l-14.3 8.3v16.3h.6a15.66 15.66 0 0015.7-15.7v-1.2a16.63 16.63 0 00-2-7.7zM30.9 148h-.6v16.2l14.3 8.3a15.4 15.4 0 002-7.6v-1.2A15.66 15.66 0 0030.9 148z"/><path d="M114.3 213.2v-97.1l-84-48.5v97.1z" fill-opacity=".15" fill="#083b54"/><path d="M198.4 163.8v-97l-84 48.5v97.1z" fill-opacity=".05" fill="#083b54"/></svg>
</div>
<h1 id="header-text" class="pt2 pt0-l ma0 f3 fw2 montserrat aqua ttu flex-none no-select"></h1>
</header>
<main class="bg-white pv3 pa3 pa4-l">
<div id="root"></div>
<script src="/dist/bundles/uiCommons.bundle.js"></script>
<script src="/dist/bundles/recoveryPage.bundle.js"></script>
</div>
</main>
</body>
</html>
43 changes: 43 additions & 0 deletions add-on/src/recovery/recovery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict'
/* eslint-env browser, webextensions */

import choo from 'choo'
import html from 'choo/html/index.js'
import { renderCompanionLogo } from '../landing-pages/welcome/page.js'
import createWelcomePageStore from '../landing-pages/welcome/store.js'
import { i18n, runtime } from 'webextension-polyfill'
import './recovery.css'

const app = choo()

// TODO (whizzzkid): refactor base store to be more generic.
app.use(createWelcomePageStore(i18n, runtime));
// Register our single route
app.route('*', (state) => {
console.log(state)
const openURLFromHash = () => {
const { hash } = window.location;
try {
const url = new URL(decodeURI(hash.slice(1)));
console.log('Opening URL from hash:', url.href);
window.location.href = url.href;
} catch (err) {
console.error('Failed to open URL from hash:', err);
}
}

return html`<div class="recovery-root">
${renderCompanionLogo(i18n, false)}
<p class="f6 fw4">${i18n.getMessage('recovery_page_message')}</p>
<button class="fade-in w-40 ba bw1 b--navy bg-navy snow f7 ph2 pv3 br4 ma1 pointer" onclick=${openURLFromHash}>
<span class="f6 fw4">${i18n.getMessage('recovery_page_button')}</span>
</button>
</div>`
})

// Start the application and render it to the given querySelector
app.mount('#root')

// Set page title and header translation
document.getElementById('header-text').innerText = i18n.getMessage('recovery_page_header')
document.title = i18n.getMessage('recovery_page_title')
1 change: 1 addition & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ const uiConfig = merge(commonConfig, {
browserAction: './add-on/src/popup/browser-action/index.js',
importPage: './add-on/src/popup/quick-import.js',
optionsPage: './add-on/src/options/options.js',
recoveryPage: './add-on/src/recovery/recovery.js',
welcomePage: './add-on/src/landing-pages/welcome/index.js'
},
optimization: {
Expand Down