Skip to content

Commit

Permalink
Add onboarding flow. (#10)
Browse files Browse the repository at this point in the history
* Implement onboarding page to prompt install of helper.

* Reorganise extension pages.

* Update readme.

* Minor fixes.
  • Loading branch information
sammacbeth committed May 16, 2018
1 parent 3230dbd commit 386a1dc
Show file tree
Hide file tree
Showing 14 changed files with 206 additions and 45 deletions.
18 changes: 3 additions & 15 deletions README.md
Expand Up @@ -10,14 +10,7 @@ It aims to implement native-like dat support possible in Firefox. This means:

## Usage

1. Grab [this fork of dat-gateway](https://github.com/sammacbeth/dat-gateway) and run it:

```bash
git clone https://github.com/sammacbeth/dat-gateway.git
cd dat-gateway
npm install
./bin.js
```
1. Install [dat-fox-helper](https://github.com/sammacbeth/dat-fox-helper)

2. Install the extension from the [Mozilla Addon Store](https://addons.mozilla.org/en-US/firefox/addon/dat-p2p-protocol/)

Expand Down Expand Up @@ -50,16 +43,11 @@ You can now load the `addon` folder as a temporary addon in Firefox:
* `http://{hash}`
* `dat://{hostname}` (using [Dat Discovery](https://github.com/beakerbrowser/beaker/wiki/Authenticated-Dat-URLs-and-HTTPS-to-Dat-Discovery))
* Toggle between `https` to `dat` protocol for Dat-enabled sites.

## What does not work

Due to limitations in the WebExtensions protocol handler API, non main-frame `dat://` urls do not load. For static resources we can circumvent this by rewriting `dat://` to `http://` in HTML files using a [StreamFilter](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/StreamFilter). However, dynamically generated requests will fail.

Any Dat site which relies on the [DatArchive](https://beakerbrowser.com/docs/apis/dat.html) will not work.
* Create and fork archives.
* `DatArchive` [API](https://beakerbrowser.com/docs/apis/dat.html)

## How it works

1. The [protocol handler](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/protocol_handlers) redirects `dat://` urls to a special handler domain (`dat.redirect`), passing the full url.
2. A [webRequest](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest) listener intercepts requests to this domain and redirects to a `http://` URL with the dat key or hostname as the origin.
3. A [proxy PAC](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/proxy) file intercepts hostnames matching a dat key pattern, or hostnames the user has explicitly ask to load over dat. Requests for these URLs are proxied via the dat-gateway (acting as a HTTP proxy). This allows us to make 'fake' hostnames work, and create the origins we need for dat sites.

4 changes: 2 additions & 2 deletions addon/manifest.json
Expand Up @@ -36,7 +36,7 @@
"default_icon": "assets/dat-hexagon.svg"
},
"options_ui": {
"page": "options_page/options.html",
"browser_style": true
"page": "pages/options.html",
"browser_style": false
}
}
16 changes: 0 additions & 16 deletions addon/options_page/options.html

This file was deleted.

4 changes: 2 additions & 2 deletions addon/dialog.html → addon/pages/dialog.html
Expand Up @@ -4,7 +4,7 @@
<head>
<meta charset="utf-8">
<title>Create Dat</title>
<link rel="stylesheet" href="assets/bootstrap.css" />
<link rel="stylesheet" href="../assets/bootstrap.css" />
</head>
<body>
<div class="container-fluid">
Expand Down Expand Up @@ -48,5 +48,5 @@ <h2>Select an archive from your library</h2>
</div>
</div>
</body>
<script src="./dialog.js"></script>
<script src="../dialog.js"></script>
</html>
38 changes: 38 additions & 0 deletions addon/pages/options.html
@@ -0,0 +1,38 @@
<!DOCTYPE html>

<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="../assets/bootstrap.css" />
</head>

<body>
<div class="container">
<div class="row" id="manual address">
<div class="col">
<h3>Actions:</h3>
<ul>
<li><a href="./setup.html" target="_blank">Open setup page</a></li>
</ul>
</div>
</div>
<div class="row" id="manual address">
<div class="col">
<h3>Settings:</h3>
<form id="options">
<div class="form-group">
<label for="address">Dat gateway Address</label>
<input class="form-control"
type="URI"
id="address"
value="http://localhost:3000"
required pattern="https?:\/\/[^/\?:]+(:[0-9]+)?" />
</div>
<button class="btn btn-primary" type="submit" >Save</button>
</form>
</div>
</div>
</div>
</body>
<script src="./options.js"></script>
</html>
File renamed without changes.
File renamed without changes.
65 changes: 65 additions & 0 deletions addon/pages/setup.html
@@ -0,0 +1,65 @@
<!DOCTYPE html>

<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="../assets/bootstrap.css" />
<title>Dat-fox Setup</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col">
<h1>Dat-fox: Dat + Firefox</h1>
<p>
You're almost ready to browse Dat, but first we need to make sure
everything is setup.
</p>
<p>If you didn't already, you need to install the dat-fox helper:</p>
<ul>
<li>Mac/Linux: <code><kdb>curl -o- https://raw.githubusercontent.com/sammacbeth/dat-fox-helper/master/installer.sh | bash</kdb></code></li>
<li>Windows: <a href="https://datfox-sammacbeth.hashbase.io/how-to-install-dat-fox-helper">Manual install instructions.</a></li>
</ul>
<p>
Once dat-fox-helper is installed, click below to test that your browser
can communicate with the helper process.
</p>
<button class="btn btn-primary" id="test">Test</button>
</div>
</div>
<div class="row">
<div class="col" id="test-results">
</div>
</div>
<div class="row">
<div class="col invisible" id="ready-to-go">
<h3>Check Dat out!</h3>
<p>
Now you're ready to browse the P2P web! Here's some sites to get you
started:
</p>
<ul>
<li>
<a href="dat://datfox-sammacbeth.hashbase.io/">The dat-fox blog</a> published
with orkl.
</li>
<li>
Publish your own p2p blog with <a href="dat://orkl-kodedninja.hashbase.io/">orkl</a>,
<a href="dat://solo-kodedninja.hashbase.io">solo</a> or
<a href="dat://pipette-dev-blog-jimpick.hashbase.io/post/introducing-pipette/">Pipette</a>.
Hit the fork button via the green Dat icon in the url bar.
</li>
<li>
Edit any Dats you create in the browser, with <a href="dat://editor-cryptic.hashbase.io/">this editor</a>.
</li>
<li>
And more... A more comprehensive list of dat sites is available from
the <a href="dat://beakerbrowser.com">Beaker Browser</a> team on <a href="https://github.com/beakerbrowser/explore">Github</a>
</li>
</ul>
</div>
</div>
</div>
<script src="./setup.js"></script>
</body>
</html>
70 changes: 70 additions & 0 deletions addon/pages/setup.js
@@ -0,0 +1,70 @@

const { bridge, resetBridge } = browser.extension.getBackgroundPage();

const button = document.getElementById('test');
const testResults = document.getElementById('test-results');

function createCheckAlert() {
const elem = document.createElement('div');
elem.setAttribute('class', 'alert alert-primary');
elem.setAttribute('role', 'alert');
return elem;
}

async function testGateway(setLabel) {
setLabel('Attempting to start gateway...');
const port = await resetBridge();
setLabel(`Successfully started dat gateway at localhost:${port}.`);
return true;
}

async function testVersion(setLabel) {
setLabel('Checking helper version...');
const version = await bridge.postMessage({ action: 'getVersion' });
const wantedVersion = '0.0.5';
if (version < wantedVersion) {
setLabel(`Helper version ${version} not up-to-date. Latest is ${wantedVersion}`);
return false;
}
setLabel(`Helper version ${version} up-to-date`);
return true;
}

button.onclick = async () => {
button.innerText = 'Testing...';
button.setAttribute('disabled', true);
testResults.innerHTML = '';
const bridgeCheck = createCheckAlert();
bridgeCheck.innerHTML = '<p>Checking helper connection</p><ul id="results"></ul>';
testResults.appendChild(bridgeCheck);
const resultList = document.getElementById('results');

const tests = [testGateway, testVersion];
let failure = false;
for (let i = 0; i < tests.length; i++) {
const test = tests[i];
const testElem = document.createElement('li');
resultList.appendChild(testElem);
try {
const res = await test((label) => testElem.innerText = label);
if (!res) {
failure = true;
break;
}
} catch(e) {
testElem.innerText = `${testElem.innerText} ${e}`;
failure = true;
break;
}
}

if (failure) {
bridgeCheck.setAttribute('class', 'alert alert-danger');
} else {
bridgeCheck.setAttribute('class', 'alert alert-success');
document.getElementById('ready-to-go').setAttribute('class', 'col visible');
}

button.removeAttribute('disabled');
button.innerText = 'Test';
};
21 changes: 15 additions & 6 deletions background/background.js
Expand Up @@ -18,16 +18,19 @@ proxyReady.then(() => {

const bridge = new NativeBridge();
global.bridge = bridge;
bridge.connect().then(() => {
global.resetBridge = async () => {
if (bridge.connected) {
bridge.disconnect();
}
await bridge.connect();
console.log('bridge is ready');
useNativeBridge(bridge);
const port = 3000 + Math.floor(Math.random() * 500);
bridge.postMessage({
await bridge.postMessage({
action: 'startGateway',
port: port,
}).then(() => {
setGatewayAddress(`http://localhost:${port}`);
}, (e) => console.error('error starting gateway', e));
});
setGatewayAddress(`http://localhost:${port}`);
// add actions which the helper API supports
['resolveName', 'getInfo', 'stat', 'readdir', 'history', 'readFile', 'writeFile', 'mkdir',
'unlink', 'rmdir', 'diff', 'commit', 'revert', 'download', 'createFileActivityStream',
Expand All @@ -47,10 +50,16 @@ bridge.connect().then(() => {
await closeArchives.map(({ url }) => bridge.postMessage({ action: 'closeArchive', url }));
}
}, 60000);
}, (e) => {
return port;
};

global.resetBridge().catch((e) => {
console.log('bridge loading failed, using local Dat API implementation', e);
console.log('opening setup page');
browser.tabs.create({ url: browser.extension.getURL('setup.html')});
});


// actions in this set are forwarded to the native bridge
const passthroughActions = new Set();

Expand Down
2 changes: 1 addition & 1 deletion background/dialog.js
Expand Up @@ -6,7 +6,7 @@ export default {
const win = await browser.windows.create({
allowScriptsToClose: true,
type: 'popup',
url: `/dialog.html#${JSON.stringify(message)}`,
url: `/pages/dialog.html#${JSON.stringify(message)}`,
width: 500,
height: 400,
});
Expand Down
7 changes: 7 additions & 0 deletions background/native-bridge.js
Expand Up @@ -4,6 +4,7 @@ export default class {
constructor() {
this.messageIdx = 0;
this.waitingForResponse = new Map();
this.connected = false;
}

connect() {
Expand All @@ -18,6 +19,7 @@ export default class {
};
const timer = setTimeout(() => {
this.port.onDisconnect.removeListener(disconnectListener);
this.connected = true;
resolve();
}, 2000);
this.port.onDisconnect.addListener(disconnectListener);
Expand Down Expand Up @@ -46,4 +48,9 @@ export default class {
this.port.postMessage(message);
});
}

disconnect() {
this.connected = false;
this.port.disconnect();
}
}
2 changes: 1 addition & 1 deletion background/page-action.js
Expand Up @@ -27,7 +27,7 @@ export function showDatSecureIcon(tabId) {
tabId,
title: 'Secure Dat Site',
});
browser.pageAction.setPopup({ tabId, popup: 'popup/popup.html' });
browser.pageAction.setPopup({ tabId, popup: 'pages/popup.html' });
browser.pageAction.show(tabId);
}

Expand Down
4 changes: 2 additions & 2 deletions dialog/dialog.js
Expand Up @@ -55,8 +55,8 @@ async function setupForm() {
desc.setAttribute('value', opts.description || '');
onSubmit(() =>
DatArchive.create({
title: title.getAttribute('value'),
desc: desc.getAttribute('value'),
title: title.value,
desc: desc.value,
}).then((archive) => {
port.postMessage({
action: 'dialogResponse',
Expand Down

0 comments on commit 386a1dc

Please sign in to comment.