From 0bdd2ede0a2a6735875f7e6178a2b6e5ba41705d Mon Sep 17 00:00:00 2001 From: haad Date: Mon, 16 Jan 2017 17:28:08 +0100 Subject: [PATCH 01/10] docs(example): create an example of cat'ing files added from ipfs CLI using only webrtc-star as a transport --- .../cat-a-file/.gitignore | 2 + .../access-go-ipfs-files/cat-a-file/README.md | 90 ++++++ .../cat-a-file/package.json | 14 + .../cat-a-file/public/index.html | 281 ++++++++++++++++++ .../cat-a-file/public/start-ipfs.js | 47 +++ .../cat-a-file/public/styles.css | 72 +++++ package.json | 1 + src/core/index.js | 7 + 8 files changed, 514 insertions(+) create mode 100644 examples/access-go-ipfs-files/cat-a-file/.gitignore create mode 100644 examples/access-go-ipfs-files/cat-a-file/README.md create mode 100644 examples/access-go-ipfs-files/cat-a-file/package.json create mode 100644 examples/access-go-ipfs-files/cat-a-file/public/index.html create mode 100644 examples/access-go-ipfs-files/cat-a-file/public/start-ipfs.js create mode 100644 examples/access-go-ipfs-files/cat-a-file/public/styles.css diff --git a/examples/access-go-ipfs-files/cat-a-file/.gitignore b/examples/access-go-ipfs-files/cat-a-file/.gitignore new file mode 100644 index 0000000000..f96c726227 --- /dev/null +++ b/examples/access-go-ipfs-files/cat-a-file/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +public/ipfs.js diff --git a/examples/access-go-ipfs-files/cat-a-file/README.md b/examples/access-go-ipfs-files/cat-a-file/README.md new file mode 100644 index 0000000000..3d6b61134d --- /dev/null +++ b/examples/access-go-ipfs-files/cat-a-file/README.md @@ -0,0 +1,90 @@ +# access-go-ipfs-files - cat-a-file + +**WIP** + +## TODO + +- Add "connect to peer" input field and "connect" button under the "Peers" section in index.html +- Add `connectToPeer` function which calls `ipfs.swarm.connect(multiaddr)`. See https://github.com/ipfs/js-ipfs/blob/b0a7cd83cbf146b0f147467dedc686f94a5f751f/examples/ipfm/src/DataStore.js#L82 and https://github.com/ipfs/js-ipfs/blob/b0a7cd83cbf146b0f147467dedc686f94a5f751f/examples/ipfm/README.md#start-an-interplanetary-file-exchange-daemon. The multiaddr to connect to looks like this: `/ip4/127.0.0.1/tcp/9999/ws/ipfs/QmZGH8GeASSkSZoNLPEBu1MqtzLTERNUEwh9yTHLEF5kcW` +- Hook up "connect" button's click event to `connectToPeer` function +- Add instructions to this README on how to add a file in go-ipfs +- Add instructions to this README on how to cat it in the UI +- Make sure the "Start a go-ipfs daemon" instructions are correct +- Make sure go-ipfs daemon and the example connect to each other + +## Step-by-step Instructions + +### Start a go-ipfs daemon + +1. Install go-ipfs from master (TODO: link). + +2. Run `ipfs init` + +3. Edit your IPFS config file, located at `~/.ipfs/config` + +4. Add a Websocket listener address to `Addresses.Swarm`. It should look like this after editing: +``` +"Addresses": { + "API": "/ip4/127.0.0.1/tcp/5001", + "Gateway": "/ip4/0.0.0.0/tcp/8080", + "Swarm": [ + "/ip4/0.0.0.0/tcp/4001", + "/ip4/0.0.0.0/tcp/9999/ws" + ] +}, +``` + +5. Start the go-ipfs daemon with: +``` +ipfs daemon +``` + +6. You should see the Websocket address in the output: +``` +Initializing daemon... +Swarm listening on /ip4/127.0.0.1/tcp/4001 +Swarm listening on /ip4/127.0.0.1/tcp/9999/ws +Swarm listening on /ip4/192.168.10.38/tcp/4001 +Swarm listening on /ip4/192.168.10.38/tcp/9999/ws +API server listening on /ip4/127.0.0.1/tcp/5001 +Gateway (readonly) server listening on /ip4/0.0.0.0/tcp/8080 +Daemon is ready +``` + +If you see address like `Swarm listening on /ip4/127.0.0.1/tcp/9999/ws`, it means all good! + +## Start the example + +**NOTE!** Before running the examples, you need to build `js-ipfs`. You can do this by following the instructions in https://github.com/ipfs/js-ipfs#clone-and-install-dependnecies and then building it as per https://github.com/ipfs/js-ipfs#build-a-dist-version. + +``` +npm install +npm start +``` + +Open http://127.0.0.1:8080 in a browser. + +**TODO: add instructions how to cat a hash in the UI.** + +## Tutorial + +Steps +1. create IPFS instance + +TODO. See `./start-ipfs.js` + +3. add a file in go-ipfs +4. copy file's hash +5. ipfs.files.cat + +TODO. add ipfs.files.cat code examples from index.html + +6. output the buffer to + +``` +... +stream.on('end', () => { + const blob = new Blob(buf) + picture.src = URL.createObjectURL(blob) +}) +``` diff --git a/examples/access-go-ipfs-files/cat-a-file/package.json b/examples/access-go-ipfs-files/cat-a-file/package.json new file mode 100644 index 0000000000..8b03ca9591 --- /dev/null +++ b/examples/access-go-ipfs-files/cat-a-file/package.json @@ -0,0 +1,14 @@ +{ + "name": "cat-a-file", + "version": "1.0.0", + "description": "", + "scripts": { + "postinstall": "cp ../../../dist/index.js ./public/ipfs.js", + "start": "http-server -c-1" + }, + "author": "Haad", + "license": "MIT", + "devDependencies": { + "http-server": "^0.9.0" + } +} diff --git a/examples/access-go-ipfs-files/cat-a-file/public/index.html b/examples/access-go-ipfs-files/cat-a-file/public/index.html new file mode 100644 index 0000000000..fa3b9d2bb1 --- /dev/null +++ b/examples/access-go-ipfs-files/cat-a-file/public/index.html @@ -0,0 +1,281 @@ + + + + + + + + + + + +
+
+

+
+
+ + +
+
+

IPFS Settings

+ + +

+ + +
+
+
+
+
+

Files

+
+
+ + + + +
+

+      
+
+ + + + diff --git a/examples/access-go-ipfs-files/cat-a-file/public/start-ipfs.js b/examples/access-go-ipfs-files/cat-a-file/public/start-ipfs.js new file mode 100644 index 0000000000..284c53a208 --- /dev/null +++ b/examples/access-go-ipfs-files/cat-a-file/public/start-ipfs.js @@ -0,0 +1,47 @@ +'use strict' + +// Start an IPFS instance +window.startIpfs = (options, callback) => { + const repoPath = options.IpfsDataDir || '/tmp/ipfs' + + const node = new window.Ipfs(repoPath) + + node.init({ emptyRepo: true, bits: 2048 }, (err) => { + if (err && err.message !== 'repo already exists') { + return callback(err) + } + + node.config.get((err, config) => { + if (err) { + return callback(err) + } + + const host = options.SignalServer.split(':')[0] || '127.0.0.1' + const port = options.SignalServer.split(':')[1] || 9090 + const signalServer = `/libp2p-webrtc-star/ip4/${host}/tcp/${port}/ws/ipfs/${config.Identity.PeerID}` + + config.Addresses = { + Swarm: [ + signalServer + ], + API: '', + Gateway: '' + } + + config.Discovery.MDNS.Enabled = false + + node.config.replace(config, (err) => { + if (err) { return callback(err) } + + node.load((err) => { + if (err) { return callback(err) } + node.goOnline((err) => { + if (err) { return callback(err) } + console.log('IPFS node is ready') + callback(null, node) + }) + }) + }) + }) + }) +} diff --git a/examples/access-go-ipfs-files/cat-a-file/public/styles.css b/examples/access-go-ipfs-files/cat-a-file/public/styles.css new file mode 100644 index 0000000000..6dd63add24 --- /dev/null +++ b/examples/access-go-ipfs-files/cat-a-file/public/styles.css @@ -0,0 +1,72 @@ +@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,700'); + +body { + font-family: 'Roboto', sans-serif; + font-size: 1.0em; + font-weight: 400; +} + +h1 { + font-size: 2em; + font-weight: 300; + margin: 0em; +} + +h2 { + font-size: 1.25em; + font-weight: 700; +} + +h3 { + font-size: 1.0em; + font-weight: 700; +} + +label { + margin-right: 1em; +} + +input { + margin-right: 1em; +} + +.hidden { + display: none; +} + +.visible { + display: inherit; + font-size: 0.8em; +} + +.error { + font-style: italic; + color: red; +} + +.center { + display: flex; + justify-content: center; +} + +.ipfs { + min-width: 22em; + max-width: 44em; + flex-grow: 1; + flex-basis: 22em; + border: 1px dashed grey; + padding: 1em; + overflow: auto; +} + +.buttons { + margin-top: 1em; +} + +.picture { + margin-top: 1em; + width: 100%; + background-color: rgba(196, 196, 196, 0.1); + /*padding: 0.25em;*/ + /*font-size: 1.2em;*/ +} \ No newline at end of file diff --git a/package.json b/package.json index 1684020bbf..60a92ffbc8 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "async": "^2.1.4", "bl": "^1.2.0", "boom": "^4.2.0", + "buffer": "^5.0.2", "debug": "^2.6.0", "fs-pull-blob-store": "^0.3.0", "glob": "^7.1.1", diff --git a/src/core/index.js b/src/core/index.js index fa4c2e8ae2..6d0df427fd 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -25,6 +25,8 @@ const files = require('./components/files') const bitswap = require('./components/bitswap') const pubsub = require('./components/pubsub') +const Buffer = require('buffer/').Buffer + exports = module.exports = IPFS function IPFS (repoInstance) { @@ -70,4 +72,9 @@ function IPFS (repoInstance) { this.bitswap = bitswap(this) this.ping = ping(this) this.pubsub = pubsub(this) + + // Exposed data types + this.types = { + Buffer: Buffer + } } From cbf7cf07f905e94bdf7e8acc68f3859daea0a854 Mon Sep 17 00:00:00 2001 From: David Dias Date: Wed, 25 Jan 2017 17:03:10 +0000 Subject: [PATCH 02/10] docs(example: transfer-files) - refactor: reorg + fix lint - fix: build step, no longer fail on postinstall - docs: add diagram - docs: add the instructions steps - lint: fix all the things - note about signalling server --- .gitignore | 2 + examples/README.md | 2 + .../access-go-ipfs-files/cat-a-file/README.md | 90 ------ .../cat-a-file/package.json | 14 - .../cat-a-file/public/index.html | 281 ------------------ .../cat-a-file/public/start-ipfs.js | 47 --- .../cat-a-file => transfer-files}/.gitignore | 0 examples/transfer-files/README.md | 142 +++++++++ examples/transfer-files/complete/package.json | 14 + .../complete/public/css/app.css} | 0 .../complete/public/favicon.ico | Bin 0 -> 8076 bytes .../transfer-files/complete/public/index.html | 57 ++++ .../transfer-files/complete/public/js/app.js | 239 +++++++++++++++ .../complete/public/js/create-node.js | 54 ++++ examples/transfer-files/img/diagram.monopic | Bin 0 -> 1571 bytes examples/transfer-files/img/diagram.txt | 19 ++ 16 files changed, 529 insertions(+), 432 deletions(-) delete mode 100644 examples/access-go-ipfs-files/cat-a-file/README.md delete mode 100644 examples/access-go-ipfs-files/cat-a-file/package.json delete mode 100644 examples/access-go-ipfs-files/cat-a-file/public/index.html delete mode 100644 examples/access-go-ipfs-files/cat-a-file/public/start-ipfs.js rename examples/{access-go-ipfs-files/cat-a-file => transfer-files}/.gitignore (100%) create mode 100644 examples/transfer-files/README.md create mode 100644 examples/transfer-files/complete/package.json rename examples/{access-go-ipfs-files/cat-a-file/public/styles.css => transfer-files/complete/public/css/app.css} (100%) create mode 100644 examples/transfer-files/complete/public/favicon.ico create mode 100644 examples/transfer-files/complete/public/index.html create mode 100644 examples/transfer-files/complete/public/js/app.js create mode 100644 examples/transfer-files/complete/public/js/create-node.js create mode 100644 examples/transfer-files/img/diagram.monopic create mode 100644 examples/transfer-files/img/diagram.txt diff --git a/.gitignore b/.gitignore index 254988dc81..d9cb651ee0 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,5 @@ node_modules lib dist + +examples/transfer-files/complete/public/js/ipfs.js diff --git a/examples/README.md b/examples/README.md index 78b7c23347..f9a954b30e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -11,3 +11,5 @@ Let us know if you find any issue or if you want to contribute and add a new tut - [How to bundle js-ipfs with WebPack](/bundle-webpack) ## Tutorials + +- [Transfer files between a Browser node and Desktop node](/transfer-files) diff --git a/examples/access-go-ipfs-files/cat-a-file/README.md b/examples/access-go-ipfs-files/cat-a-file/README.md deleted file mode 100644 index 3d6b61134d..0000000000 --- a/examples/access-go-ipfs-files/cat-a-file/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# access-go-ipfs-files - cat-a-file - -**WIP** - -## TODO - -- Add "connect to peer" input field and "connect" button under the "Peers" section in index.html -- Add `connectToPeer` function which calls `ipfs.swarm.connect(multiaddr)`. See https://github.com/ipfs/js-ipfs/blob/b0a7cd83cbf146b0f147467dedc686f94a5f751f/examples/ipfm/src/DataStore.js#L82 and https://github.com/ipfs/js-ipfs/blob/b0a7cd83cbf146b0f147467dedc686f94a5f751f/examples/ipfm/README.md#start-an-interplanetary-file-exchange-daemon. The multiaddr to connect to looks like this: `/ip4/127.0.0.1/tcp/9999/ws/ipfs/QmZGH8GeASSkSZoNLPEBu1MqtzLTERNUEwh9yTHLEF5kcW` -- Hook up "connect" button's click event to `connectToPeer` function -- Add instructions to this README on how to add a file in go-ipfs -- Add instructions to this README on how to cat it in the UI -- Make sure the "Start a go-ipfs daemon" instructions are correct -- Make sure go-ipfs daemon and the example connect to each other - -## Step-by-step Instructions - -### Start a go-ipfs daemon - -1. Install go-ipfs from master (TODO: link). - -2. Run `ipfs init` - -3. Edit your IPFS config file, located at `~/.ipfs/config` - -4. Add a Websocket listener address to `Addresses.Swarm`. It should look like this after editing: -``` -"Addresses": { - "API": "/ip4/127.0.0.1/tcp/5001", - "Gateway": "/ip4/0.0.0.0/tcp/8080", - "Swarm": [ - "/ip4/0.0.0.0/tcp/4001", - "/ip4/0.0.0.0/tcp/9999/ws" - ] -}, -``` - -5. Start the go-ipfs daemon with: -``` -ipfs daemon -``` - -6. You should see the Websocket address in the output: -``` -Initializing daemon... -Swarm listening on /ip4/127.0.0.1/tcp/4001 -Swarm listening on /ip4/127.0.0.1/tcp/9999/ws -Swarm listening on /ip4/192.168.10.38/tcp/4001 -Swarm listening on /ip4/192.168.10.38/tcp/9999/ws -API server listening on /ip4/127.0.0.1/tcp/5001 -Gateway (readonly) server listening on /ip4/0.0.0.0/tcp/8080 -Daemon is ready -``` - -If you see address like `Swarm listening on /ip4/127.0.0.1/tcp/9999/ws`, it means all good! - -## Start the example - -**NOTE!** Before running the examples, you need to build `js-ipfs`. You can do this by following the instructions in https://github.com/ipfs/js-ipfs#clone-and-install-dependnecies and then building it as per https://github.com/ipfs/js-ipfs#build-a-dist-version. - -``` -npm install -npm start -``` - -Open http://127.0.0.1:8080 in a browser. - -**TODO: add instructions how to cat a hash in the UI.** - -## Tutorial - -Steps -1. create IPFS instance - -TODO. See `./start-ipfs.js` - -3. add a file in go-ipfs -4. copy file's hash -5. ipfs.files.cat - -TODO. add ipfs.files.cat code examples from index.html - -6. output the buffer to - -``` -... -stream.on('end', () => { - const blob = new Blob(buf) - picture.src = URL.createObjectURL(blob) -}) -``` diff --git a/examples/access-go-ipfs-files/cat-a-file/package.json b/examples/access-go-ipfs-files/cat-a-file/package.json deleted file mode 100644 index 8b03ca9591..0000000000 --- a/examples/access-go-ipfs-files/cat-a-file/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "cat-a-file", - "version": "1.0.0", - "description": "", - "scripts": { - "postinstall": "cp ../../../dist/index.js ./public/ipfs.js", - "start": "http-server -c-1" - }, - "author": "Haad", - "license": "MIT", - "devDependencies": { - "http-server": "^0.9.0" - } -} diff --git a/examples/access-go-ipfs-files/cat-a-file/public/index.html b/examples/access-go-ipfs-files/cat-a-file/public/index.html deleted file mode 100644 index fa3b9d2bb1..0000000000 --- a/examples/access-go-ipfs-files/cat-a-file/public/index.html +++ /dev/null @@ -1,281 +0,0 @@ - - - - - - - - - - - -
-
-

-
-
- - -
-
-

IPFS Settings

- - -

- - -
-
-
-
-
-

Files

-
-
- - - - -
-

-      
-
- - - - diff --git a/examples/access-go-ipfs-files/cat-a-file/public/start-ipfs.js b/examples/access-go-ipfs-files/cat-a-file/public/start-ipfs.js deleted file mode 100644 index 284c53a208..0000000000 --- a/examples/access-go-ipfs-files/cat-a-file/public/start-ipfs.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict' - -// Start an IPFS instance -window.startIpfs = (options, callback) => { - const repoPath = options.IpfsDataDir || '/tmp/ipfs' - - const node = new window.Ipfs(repoPath) - - node.init({ emptyRepo: true, bits: 2048 }, (err) => { - if (err && err.message !== 'repo already exists') { - return callback(err) - } - - node.config.get((err, config) => { - if (err) { - return callback(err) - } - - const host = options.SignalServer.split(':')[0] || '127.0.0.1' - const port = options.SignalServer.split(':')[1] || 9090 - const signalServer = `/libp2p-webrtc-star/ip4/${host}/tcp/${port}/ws/ipfs/${config.Identity.PeerID}` - - config.Addresses = { - Swarm: [ - signalServer - ], - API: '', - Gateway: '' - } - - config.Discovery.MDNS.Enabled = false - - node.config.replace(config, (err) => { - if (err) { return callback(err) } - - node.load((err) => { - if (err) { return callback(err) } - node.goOnline((err) => { - if (err) { return callback(err) } - console.log('IPFS node is ready') - callback(null, node) - }) - }) - }) - }) - }) -} diff --git a/examples/access-go-ipfs-files/cat-a-file/.gitignore b/examples/transfer-files/.gitignore similarity index 100% rename from examples/access-go-ipfs-files/cat-a-file/.gitignore rename to examples/transfer-files/.gitignore diff --git a/examples/transfer-files/README.md b/examples/transfer-files/README.md new file mode 100644 index 0000000000..80c60715c4 --- /dev/null +++ b/examples/transfer-files/README.md @@ -0,0 +1,142 @@ +# Tutorial - Transfer files between the browser and other IPFS nodes + +> Welcome! This tutorial will help you a tiny web application where you can fetch and add files to IPFS and transfer these between a go-ipfs node and a js-ipfs node. + +There are a couple of caveats: + +- js-ipfs currently doesn't support DHT peer discovery, the peer from which you are fetching data should be within the reach (local or in public IP) of the browser node. +- We need to use a signalling server to establish the WebRTC connections, this won't be necessary as soon as libp2p-relay gets developed +- [full go-ipfs interop is not complete yet, blocked by an interop stream multiplexer](https://github.com/ipfs/js-ipfs/issues/721) + +That being said, we will explain throughout this tutorial to circunvent the caveats and once they are fixed, we will update the tutorial as well. + +## Application diagram + +The goal of this tutorial is to create a WebApplication with an IPFS node that dials to other instances of it using WebRTC, and at the same time dial and transfer files from a Desktop IPFS node using WebSockets as the transport. + +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Browser β”‚ β”‚ Browser β”‚ +β”‚ β”‚ WebRTC β”‚ β”‚ +β”‚ │◀─────────────────▢│ β”‚ +β”‚ β”‚ β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β–² β–² + β”‚ β”‚ + β”‚ β”‚ + β”‚ β”‚ + β”‚WebSockets WebSocketsβ”‚ + β”‚ β”‚ + β”‚ β”‚ + β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ + β”‚ β”‚ Desktop β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ + └───────▢│ β”‚β—€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + +## Check out the final state + +If you just want to check out what is the final state of how this application will look like, go to the complete folder, install the dependencies and run it. + +```sh +> cd complete +> npm install +> npm start +# open your browser (Chrome or Firefox) in http://localhost:12345 +``` + +You should get something like this: + +TODO: Insert final screenshot here + +## Step-by-step instructions + +**Instructions:** + +- 1. Set up, install a go-ipfs and/or js-ipfs in your machine +- 2. Make your daemons listen on WebSockets +- 3. Start the WebApp project +- 4. Create the frame for your IPFS enabled app +- 5. Add and cat a file +- 6. Use WebRTC to dial between browser nodes +- 7. Dial to a node using WebSockets (your Desktop ones) +- 8. Transfer files between all of your nodes, have fun! + + +------------------------- +> Steps need to be updated once the final thing is finished + +### Start a go-ipfs daemon + +1. Install go-ipfs from master (TODO: link). + +2. Run `ipfs init` + +3. Edit your IPFS config file, located at `~/.ipfs/config` + +4. Add a Websocket listener address to `Addresses.Swarm`. It should look like this after editing: +``` +"Addresses": { + "API": "/ip4/127.0.0.1/tcp/5001", + "Gateway": "/ip4/0.0.0.0/tcp/8080", + "Swarm": [ + "/ip4/0.0.0.0/tcp/4001", + "/ip4/0.0.0.0/tcp/9999/ws" + ] +}, +``` + +5. Start the go-ipfs daemon with: +``` +ipfs daemon +``` + +6. You should see the Websocket address in the output: +``` +Initializing daemon... +Swarm listening on /ip4/127.0.0.1/tcp/4001 +Swarm listening on /ip4/127.0.0.1/tcp/9999/ws +Swarm listening on /ip4/192.168.10.38/tcp/4001 +Swarm listening on /ip4/192.168.10.38/tcp/9999/ws +API server listening on /ip4/127.0.0.1/tcp/5001 +Gateway (readonly) server listening on /ip4/0.0.0.0/tcp/8080 +Daemon is ready +``` + +If you see address like `Swarm listening on /ip4/127.0.0.1/tcp/9999/ws`, it means all good! + +## Start the example + +**NOTE!** Before running the examples, you need to build `js-ipfs`. You can do this by following the instructions in https://github.com/ipfs/js-ipfs#clone-and-install-dependnecies and then building it as per https://github.com/ipfs/js-ipfs#build-a-dist-version. + +``` +npm install +npm start +``` + +Open http://127.0.0.1:8080 in a browser. + +**TODO: add instructions how to cat a hash in the UI.** + +## Tutorial + +Steps +1. create IPFS instance + +TODO. See `./start-ipfs.js` + +3. add a file in go-ipfs +4. copy file's hash +5. ipfs.files.cat + +TODO. add ipfs.files.cat code examples from index.html + +6. output the buffer to + +``` +... +stream.on('end', () => { + const blob = new Blob(buf) + picture.src = URL.createObjectURL(blob) +}) +``` diff --git a/examples/transfer-files/complete/package.json b/examples/transfer-files/complete/package.json new file mode 100644 index 0000000000..959558ba92 --- /dev/null +++ b/examples/transfer-files/complete/package.json @@ -0,0 +1,14 @@ +{ + "name": "transfer-files", + "version": "1.0.0", + "scripts": { + "check-bundle": "test -f ../../../dist/index.js && npm run copy-bundle || echo \"js-ipfs dist file not found, go up two dirs and run 'npm run build' first\"", + "copy-bundle": "cp ../../../dist/index.js ./public/js/ipfs.js", + "serve": "http-server -c-1 -p 12345 public", + "start": "npm run check-bundle && npm run serve" + }, + "license": "MIT", + "devDependencies": { + "http-server": "^0.9.0" + } +} diff --git a/examples/access-go-ipfs-files/cat-a-file/public/styles.css b/examples/transfer-files/complete/public/css/app.css similarity index 100% rename from examples/access-go-ipfs-files/cat-a-file/public/styles.css rename to examples/transfer-files/complete/public/css/app.css diff --git a/examples/transfer-files/complete/public/favicon.ico b/examples/transfer-files/complete/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..801f728c083d96c67b7b636050dda09507edeb3b GIT binary patch literal 8076 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 literal 0 HcmV?d00001 diff --git a/examples/transfer-files/complete/public/index.html b/examples/transfer-files/complete/public/index.html new file mode 100644 index 0000000000..3086e1a963 --- /dev/null +++ b/examples/transfer-files/complete/public/index.html @@ -0,0 +1,57 @@ + + + + + + + + + + +
+
+

+
+
+ + +
+
+

IPFS Settings

+ + +

+ + +
+
+
+
+
+

Files

+
+
+ + + + +
+

+      
+
+ + + + + + + + + + + + diff --git a/examples/transfer-files/complete/public/js/app.js b/examples/transfer-files/complete/public/js/app.js new file mode 100644 index 0000000000..207a0f653b --- /dev/null +++ b/examples/transfer-files/complete/public/js/app.js @@ -0,0 +1,239 @@ +/* global Blob, URL, FileReader */ + +const rootElement = document.getElementById('ipfs') +const startButton = document.getElementById('start') +const stopButton = document.getElementById('stop') +const output = document.getElementById('state') +const details = document.getElementById('details') +const peers = document.getElementById('peers') +const errors = document.getElementById('errors') +const directory = document.getElementById('directory') +const dirInput = document.getElementById('dir') +const signalServerInput = document.getElementById('signalServerInput') +const files = document.getElementById('files') +const filesStatus = document.getElementById('filesStatus') +const picture = document.getElementById('picture') +const multihashInput = document.getElementById('multihash') +const catButton = document.getElementById('cat') + +let ipfs +let peerInfo +let pollPeersTimer + +// Start IPFS instance +function start () { + if (!ipfs) { + // Update the UI with initial settings + updateView('starting', ipfs) + + /* + * path - 'dirname' of where the IPFS repo is stored + * signallAddr - address of the signalling server + */ + const options = { + path: dirInput.value, + signalAddr: signalServerInput.value + } + + // Create an IPFS instance + window.createNode(options, (err, node) => { + if (err) { + return onError(err) + } + + ipfs = node + + // Get our IPFS instance's info: ID and address + ipfs.id().then((id) => { + peerInfo = id + // Update the UI + updateView('ready', ipfs) + + // Poll for peers from IPFS and display them + pollPeersTimer = setInterval(updatePeers, 1000) + peers.innerHTML = '

Peers

Waiting for peers...' + }) + }) + } +} + +// Stop IPFS instance +const stop = () => { + if (ipfs) { + if (pollPeersTimer) { + clearInterval(pollPeersTimer) + } + + ipfs.goOffline() + ipfs = null + updateView('stopped', ipfs) + } +} + +// Fetch file contents from IPFS and display it +const catFile = () => { + // Get the hash to cat from the input field + const multihash = multihashInput.value + + // Update UI + picture.innerHTML = multihash ? 'Loading...' : '' + picture.className = multihash ? 'picture visible' : 'hidden' + errors.className = 'hidden' + + // Get the file from IPFS + if (multihash) { + // IPFS.files.cat() + // https://github.com/ipfs/interface-ipfs-core/tree/master/API/files#javascript---ipfscatmultihash-callback + ipfs.files.cat(multihash) + .then((stream) => { + // Buffer all contents of the file as text + // and once buffered, create a blob for the picture + let buf = [] + stream.on('data', (d) => buf.push(d)) + stream.on('end', () => { + const blob = new Blob(buf) + picture.src = URL.createObjectURL(blob) + }) + }) + .catch(onError) + } +} + +// Display an error +const onError = (e) => { + console.error(e) + errors.innerHTML = "
' + e.stack + '" + errors.className = 'error visible' +} + +// Handle file drop +const onDrop = (event) => { + picture.innerHTML = '' + picture.className = 'hidden' + errors.className = 'hidden' + + event.preventDefault() + var dt = event.dataTransfer + var files = dt.files + + const readFileContents = (file) => { + return new Promise((resolve) => { + const reader = new FileReader() + reader.onload = (event) => resolve(event.target.result) + reader.readAsArrayBuffer(file) + }) + } + + // TODO: Promise reduce? + for (var i = 0; i < files.length; i++) { + const file = files[i] + console.log('Add file', file.name, file.size) + readFileContents(file) + .then((buffer) => { + // IPFS.files.add() + // https://github.com/ipfs/interface-ipfs-core/tree/master/API/files#javascript---ipfsfilesadddata-options-callback + return ipfs.files.add([{ + path: file.name, + content: new ipfs.types.Buffer(buffer) + }]) + }) + .then((files) => { + console.log('Files added', files) + multihashInput.value = files[0].hash + filesStatus.innerHTML = files + .map((e) => `Added ${e.path} as ${e.hash}`) + .join('
') + }) + .catch(onError) + } +} + +// Get peers from IPFS and display them +const updatePeers = () => { + ipfs.swarm.peers((err, res) => { + if (err) { + // TODO ?? + } + // PeerId.toJSON() + // https://github.com/libp2p/js-peer-id/blob/3ef704ba32a97a9da26a1f821702cdd3f09c778f/src/index.js#L106 + // Multiaddr.toString() + // https://multiformats.github.io/js-multiaddr/#multiaddrtostring + const peersAsHtml = res + .map((e, idx) => { + return (idx + 1) + '.' + + e.peer.id.toJSON().id + + '
' + + e.addr.toString() + + '
' + }) + .join('') + + peers.innerHTML = res.length > 0 + ? '

Peers

' + peersAsHtml + : '

Peers

Waiting for peers...' + }) +} + +/* UI functions */ +function initView () { + const initElement = (e, className) => { + e.innerHTML = '' + e.className = className + } + + // Initial view + const elements = [errors, details, peers] + elements.map((e) => initElement(e, 'hidden')) + errors.innerHTML = '' + output.innerHTML = 'πŸ”Œ IPFS stopped' + dirInput.value = '/ipfs/' + new Date().getTime() + directory.className = 'visible' + files.className = 'hidden' + filesStatus.innerHTML = '' + picture.innerHTML = '' + startButton.disabled = false + stopButton.disabled = true + multihashInput.value = null + picture.className = 'hidden' + // Remove old event listeners + rootElement.removeEventListener('drop', onDrop) + startButton.removeEventListener('click', start) + stopButton.removeEventListener('click', stop) + catButton.removeEventListener('click', catFile) + // Setup event listeners for interaction + rootElement.addEventListener('drop', onDrop) + startButton.addEventListener('click', start) + stopButton.addEventListener('click', stop) + catButton.addEventListener('click', catFile) +} + +function updateView (state, ipfs) { + if (state === 'ready') { + // Set the header to display the current state + output.innerHTML = 'πŸš€ IPFS started' + // Display IPFS info + details.innerHTML = '
' + + '

IPFS Node

' + + 'ID
' + + peerInfo.id + '

' + + 'Address
' + + peerInfo.addresses[0] + '

' + + 'IPFS Data Directory
' + + dirInput.value + // Set the file status + filesStatus.innerHTML = 'Drop a picture here to add it to IPFS.' + details.className = 'visible' + peers.className = 'visible' + files.className = 'visible' + stopButton.disabled = false + } else if (state === 'starting') { + output.innerHTML = 'πŸ“‘ IPFS starting' + startButton.disabled = true + directory.className = 'hidden' + } else if (state === 'stopped') { + initView() + } +} + +// Start the app +initView() diff --git a/examples/transfer-files/complete/public/js/create-node.js b/examples/transfer-files/complete/public/js/create-node.js new file mode 100644 index 0000000000..3051edb4b7 --- /dev/null +++ b/examples/transfer-files/complete/public/js/create-node.js @@ -0,0 +1,54 @@ +'use strict' + +/* + * Create an IPFS node helper + */ +window.createNode = (options, callback) => { + const repoPath = options.path || '/tmp/ipfs' + Math.random() + const node = new window.Ipfs(repoPath) + + node.init({ emptyRepo: true, bits: 2048 }, updateConfig) + + function updateConfig (err) { + if (err) { + return callback(err) + } + + node.config.get((err, config) => { + if (err) { + return callback(err) + } + + // TODO change to use wstar in DNS instead + const host = options.signalAddr.split(':')[0] || '127.0.0.1' + const port = options.signalAddr.split(':')[1] || 9090 + + const wstarMultiaddr = `/libp2p-webrtc-star/ip4/${host}/tcp/${port}/ws/ipfs/${config.Identity.PeerID}` + + config.Addresses.Swarm = [ wstarMultiaddr ] + + node.config.replace(config, bootNode) + }) + } + + function bootNode (err) { + if (err) { + return callback(err) + } + + node.load((err) => { + if (err) { + return callback(err) + } + + node.goOnline((err) => { + if (err) { + return callback(err) + } + + // console.log('IPFS node is ready') + callback(null, node) + }) + }) + } +} diff --git a/examples/transfer-files/img/diagram.monopic b/examples/transfer-files/img/diagram.monopic new file mode 100644 index 0000000000000000000000000000000000000000..35f88ab30f3b374694bfc9ee98cffe31dda8cd7a GIT binary patch literal 1571 zcmV+;2Hg4oO;1iwP)S1pABzY8000000t4+@OLL<*5dJGE&Ymjt;*sOb9``i6Rdd)) zQP{{f8w_5+$z)Uh@7od*1`Lh~qu7$c2yd}eL3h$lhGf0ov*ob1{byxkVTJj)}V2a7Dpa_g32*F(N-d-lvK z)?t)A24(z{C$I|Uhh-Tq)*;ZY=?V&q`&Hli9GDB!)h-EhwF~@zW-aq@!-Ftgtg}4G z`BE|DxUhj@Ms69*^J>@D%cQ7YCfVX%9B&yW1@IANiv}$O6V`DQ@wA?>DA#$mTdnIC z^AOmES7{WKPg^(;ZoT8FIA$%sGF(}=a3(P|h~YIp2q;@F3ohhp&xBB`^A+)bK+{6@ zOfXaI<}ewS0*`QjzwYYA^*)+UrN0JId7PFI@}fsFrFxmd_AvUv=YI$pmBrs_&DA2y zQ=tZQ*hnrev=Tg+n&t5-2FY1pfP1%I+c~CQ-=p}p78P6Sys=nS#O0I3!Zw3poTXYW z6%#}-AqG?rm>xKK;97U}Mp0O-4}yHQwo+9)`OM(C?@B6ZiBBxA_(_0;89#Q6s_d(g7|91_($qi)^TiQ9Zc<&#Drv$IOQF2DRw&c zR8Jg`XUDn*z&!5U1Fow!Io8$=R$Lb4lki8R0o)3q+GdqO{0@_Eur&ZW;wxlPTOe!A z!zf6zG87Z#UdJBh1U60>N5NMc@h>>zDF%8xM#Ew^;kLmH#nzBB>a{v4wn~Vk&&_KV zo9s~}xH|Hp-H4#ThU_u12zgf)ix7L4vB;S@Q)7|+no#t;5`MyO(Akj)Io6Ruzi}Kw zNLeJpdY~f{(vb;}Vm=O`>xo0ieN7x{dIp)L9-BxM=TfW6Rs*dOI6Z4X9pQ$umPxn* z#?Zc2%2?9=nWQlqP1-q^YZ;giQ@uV1qICch!EFW#^8%<}h3Ews;pb7n8-BQa2e_1z zw;5nvf||-)l*y+fpRRm*^6AT`Ojz&siaBDN6DfI-^nnf%bYgL0U=v%KJ3L8rzbR1( zk==`#N{riIB09{bFn7d3O&5YCOfMceuiQ(Vg1CZ&FDscHG-3B`Sj{f8YHg3vhe^Cj zHjAJI!(Lo;oYOkX-PR}VZr z@ITl!G@5)3jSS^9p0SY69Aj4&uKgY}v4g$u>u$}fYJKd@SWSZ&%Z?o13??07ELW7c z>BOE(q-Ch>s+^bduy0}d5Ei|e*xfHovpQi;wa0aOqS9^c!*xs1(z$Z=Tl;w3mqAru z=KM_SIBz+bK{dT39g{*adTl8bdXFiTcfkxw)VAm5P7F75CtPj%#<>&KMntt0QEf)d z)&umhawkTn=1$%iuRyNND-iVARK0Lhm9$&~a%Zxk`NqBhYu|viZ@}6&5bYZX`dDpM zLZ-G=k+D=05o3urw#_wz+2r;j#>PrYbT(l2I+1y=ecAR$z;Tl6R3Q`QOUkRQQ%XS z%B|1|6{waC9Xf+I^$f&)T@%zFYWb3>95G_(&hWD@ zH~1u$jp>RpH2n^gnsc_qkd0Bt3|)UrEsUKjtA#Q7tc5RJ3uD@>g$cXJ7sk5q#k%mt z`kP~{ud4wnAF33_Y+@ Date: Fri, 27 Jan 2017 01:21:14 +0100 Subject: [PATCH 03/10] Modifications to transfer-files readme --- examples/transfer-files/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/transfer-files/README.md b/examples/transfer-files/README.md index 80c60715c4..2d966cb54c 100644 --- a/examples/transfer-files/README.md +++ b/examples/transfer-files/README.md @@ -1,6 +1,6 @@ # Tutorial - Transfer files between the browser and other IPFS nodes -> Welcome! This tutorial will help you a tiny web application where you can fetch and add files to IPFS and transfer these between a go-ipfs node and a js-ipfs node. +> Welcome! This tutorial will help you build a tiny web application where you can fetch and add files to IPFS and transfer these between a go-ipfs node and a js-ipfs node. There are a couple of caveats: @@ -12,7 +12,7 @@ That being said, we will explain throughout this tutorial to circunvent the cave ## Application diagram -The goal of this tutorial is to create a WebApplication with an IPFS node that dials to other instances of it using WebRTC, and at the same time dial and transfer files from a Desktop IPFS node using WebSockets as the transport. +The goal of this tutorial is to create a application with a IPFS node that dials to other instances of it using WebRTC, and at the same time dial and transfer files from a Desktop IPFS node using WebSockets as the transport. β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Browser β”‚ β”‚ Browser β”‚ @@ -53,9 +53,9 @@ TODO: Insert final screenshot here **Instructions:** -- 1. Set up, install a go-ipfs and/or js-ipfs in your machine +- 1. Set up, install a go-ipfs node in your machine - 2. Make your daemons listen on WebSockets -- 3. Start the WebApp project +- 3. Initialize the project - 4. Create the frame for your IPFS enabled app - 5. Add and cat a file - 6. Use WebRTC to dial between browser nodes From f3dcfb555addbaeed915fbaf11269697a8216792 Mon Sep 17 00:00:00 2001 From: Victor Bjelkholm Date: Fri, 27 Jan 2017 01:21:37 +0100 Subject: [PATCH 04/10] first pass of improving design and ux of example - nicer colors - include logo - fancier buttons - easier flow to start daemon - less information after starting daemon - use dns signal server - larger font size - removal of unused features - removal of duplicated script references - more explanation - and probably something more I forgot --- .../complete/public/css/app.css | 139 ++++++++++++++---- .../transfer-files/complete/public/index.html | 81 ++++++---- .../complete/public/ipfs-logo.svg | 1 + .../transfer-files/complete/public/js/app.js | 99 ++++++------- .../complete/public/js/create-node.js | 8 +- 5 files changed, 214 insertions(+), 114 deletions(-) create mode 100644 examples/transfer-files/complete/public/ipfs-logo.svg diff --git a/examples/transfer-files/complete/public/css/app.css b/examples/transfer-files/complete/public/css/app.css index 6dd63add24..8fedfc1f27 100644 --- a/examples/transfer-files/complete/public/css/app.css +++ b/examples/transfer-files/complete/public/css/app.css @@ -1,9 +1,23 @@ -@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,700'); - body { - font-family: 'Roboto', sans-serif; - font-size: 1.0em; - font-weight: 400; + height: 100vh; + font-family: sans-serif; + color: white; + background: linear-gradient(to bottom,#041727 0%,#062b3f 100%); +} + +.id { + margin-top: 30px; + font-size: 14px; +} + +.id strong { + font-size: 17px; +} + +#restart { + position: absolute; + top: 20px; + right: 20px; } h1 { @@ -13,7 +27,7 @@ h1 { } h2 { - font-size: 1.25em; + font-size: 25px; font-weight: 700; } @@ -22,6 +36,14 @@ h3 { font-weight: 700; } +p { + font-size: 17px; +} + +.center { + text-align: center; +} + label { margin-right: 1em; } @@ -44,29 +66,94 @@ input { color: red; } -.center { - display: flex; - justify-content: center; +.wrapper { + position: relative; + margin: 50px auto; + padding-top: 50px; + padding-bottom: 50px; + width: 500px; + background-color: rgba(255, 255, 255, 0.05); + border-radius: 1px; } -.ipfs { - min-width: 22em; - max-width: 44em; - flex-grow: 1; - flex-basis: 22em; - border: 1px dashed grey; - padding: 1em; - overflow: auto; +.introduction { + width: 70%; + margin: 0px auto; + text-align: center; } -.buttons { - margin-top: 1em; +.on-off-button { + position: absolute; + top: 10px; + right: 10px; } -.picture { - margin-top: 1em; - width: 100%; - background-color: rgba(196, 196, 196, 0.1); - /*padding: 0.25em;*/ - /*font-size: 1.2em;*/ -} \ No newline at end of file +#directory { + text-align: center; +} + +#directory .group { + margin: 30px; +} + +#directory label { + font-size: 18px; + display: block; +} + +textarea, input, button { + outline: none; +} + +button:focus, input:focus { + outline: 5px solid #6acad1; +} + +#peers { + margin: 10px; + text-align: left; + font-size: 14px; + word-break: break-all; +} + +.peer-item { + margin-bottom: 15px; +} + +#logo-link { + position: absolute; + top: 20px; + left: 20px; + width: 32px; + overflow: hidden; +} + +.button { + background-color: rgba(0,0,0,0.2); + color: #6acad1; + border: 2px solid #6acad1; + font-size: 15px; + padding: 10px 25px 10px 25px; + border-radius: 2px; +} + +button:hover { + color: white; + border: 2px solid white; + cursor: pointer; +} + +#directory input { + width: 70%; + font-size: 16px; + border: 2px solid #444; + color: black; + padding: 10px; + border-radius: 2px; + font-family: monospace; +} + +.group-item { + display: inline; + line-height: 30px; +} diff --git a/examples/transfer-files/complete/public/index.html b/examples/transfer-files/complete/public/index.html index 3086e1a963..f302927138 100644 --- a/examples/transfer-files/complete/public/index.html +++ b/examples/transfer-files/complete/public/index.html @@ -1,42 +1,67 @@ - + + - + - - - + IPFS - Transfer Files Example - + + + +
-
-

+
+

IPFS not running yet

-
- - +
-

IPFS Settings

- - -

- - +

Setup

+
+

+ Here you can configure where to keep your IPFS configuration + and which signaling server to use. +

+

+ When you're ready, click "Go Online" to start the IPFS daemon + in the background +

+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
-
-
-
-

Files

-
-
- - - - + + + -

+        
       
diff --git a/examples/transfer-files/complete/public/ipfs-logo.svg b/examples/transfer-files/complete/public/ipfs-logo.svg new file mode 100644 index 0000000000..5091cce61e --- /dev/null +++ b/examples/transfer-files/complete/public/ipfs-logo.svg @@ -0,0 +1 @@ +IPFS logo (new) \ No newline at end of file diff --git a/examples/transfer-files/complete/public/js/app.js b/examples/transfer-files/complete/public/js/app.js index 207a0f653b..20a53b57ea 100644 --- a/examples/transfer-files/complete/public/js/app.js +++ b/examples/transfer-files/complete/public/js/app.js @@ -1,8 +1,5 @@ -/* global Blob, URL, FileReader */ - const rootElement = document.getElementById('ipfs') const startButton = document.getElementById('start') -const stopButton = document.getElementById('stop') const output = document.getElementById('state') const details = document.getElementById('details') const peers = document.getElementById('peers') @@ -11,14 +8,27 @@ const directory = document.getElementById('directory') const dirInput = document.getElementById('dir') const signalServerInput = document.getElementById('signalServerInput') const files = document.getElementById('files') -const filesStatus = document.getElementById('filesStatus') -const picture = document.getElementById('picture') const multihashInput = document.getElementById('multihash') const catButton = document.getElementById('cat') +const $connectPeer = document.querySelector('input.connect-peer') +const $connectPeerButton = document.querySelector('button.connect-peer') + let ipfs let peerInfo -let pollPeersTimer + +// See signal and data-directory option +// Click start ipfs daemon +// Connect to go-ipfs daemon +// Cat a file from go-ipfs daemon +// Add text in browser and cat with go-ipfs + +$connectPeerButton.addEventListener('click', () => { + console.log('connecting', $connectPeer.value) + ipfs.swarm.connect($connectPeer.value, (err, res) => { + console.log(err, res) + }) +}) // Start IPFS instance function start () { @@ -49,8 +59,7 @@ function start () { // Update the UI updateView('ready', ipfs) - // Poll for peers from IPFS and display them - pollPeersTimer = setInterval(updatePeers, 1000) + setInterval(updatePeers, 1000) peers.innerHTML = '

Peers

Waiting for peers...' }) }) @@ -58,17 +67,16 @@ function start () { } // Stop IPFS instance -const stop = () => { - if (ipfs) { - if (pollPeersTimer) { - clearInterval(pollPeersTimer) - } - - ipfs.goOffline() - ipfs = null - updateView('stopped', ipfs) - } -} +// const stop = () => { +// if (ipfs) { +// if (pollPeersTimer) { +// clearInterval(pollPeersTimer) +// } +// ipfs.goOffline() +// ipfs = null +// updateView('stopped', ipfs) +// } +// } // Fetch file contents from IPFS and display it const catFile = () => { @@ -76,8 +84,6 @@ const catFile = () => { const multihash = multihashInput.value // Update UI - picture.innerHTML = multihash ? 'Loading...' : '' - picture.className = multihash ? 'picture visible' : 'hidden' errors.className = 'hidden' // Get the file from IPFS @@ -91,8 +97,9 @@ const catFile = () => { let buf = [] stream.on('data', (d) => buf.push(d)) stream.on('end', () => { - const blob = new Blob(buf) - picture.src = URL.createObjectURL(blob) + console.log(buf.join('')) + // const blob = new window.Blob(buf) + // picture.src = window.URL.createObjectURL(blob) }) }) .catch(onError) @@ -108,8 +115,6 @@ const onError = (e) => { // Handle file drop const onDrop = (event) => { - picture.innerHTML = '' - picture.className = 'hidden' errors.className = 'hidden' event.preventDefault() @@ -118,7 +123,7 @@ const onDrop = (event) => { const readFileContents = (file) => { return new Promise((resolve) => { - const reader = new FileReader() + const reader = new window.FileReader() reader.onload = (event) => resolve(event.target.result) reader.readAsArrayBuffer(file) }) @@ -140,14 +145,12 @@ const onDrop = (event) => { .then((files) => { console.log('Files added', files) multihashInput.value = files[0].hash - filesStatus.innerHTML = files - .map((e) => `Added ${e.path} as ${e.hash}`) - .join('
') }) .catch(onError) } } +let numberOfPeersLastTime = 0 // Get peers from IPFS and display them const updatePeers = () => { ipfs.swarm.peers((err, res) => { @@ -158,19 +161,21 @@ const updatePeers = () => { // https://github.com/libp2p/js-peer-id/blob/3ef704ba32a97a9da26a1f821702cdd3f09c778f/src/index.js#L106 // Multiaddr.toString() // https://multiformats.github.io/js-multiaddr/#multiaddrtostring + if (res.length === numberOfPeersLastTime) { + return + } + numberOfPeersLastTime = res.length const peersAsHtml = res .map((e, idx) => { - return (idx + 1) + '.' + - e.peer.id.toJSON().id + - '
' + + return '
' + e.addr.toString() + - '
' + '
' }) .join('') peers.innerHTML = res.length > 0 - ? '

Peers

' + peersAsHtml - : '

Peers

Waiting for peers...' + ? '

Connected Peers

' + peersAsHtml + : '

Connected Peers

Waiting for peers...' }) } @@ -185,49 +190,35 @@ function initView () { const elements = [errors, details, peers] elements.map((e) => initElement(e, 'hidden')) errors.innerHTML = '' - output.innerHTML = 'πŸ”Œ IPFS stopped' dirInput.value = '/ipfs/' + new Date().getTime() directory.className = 'visible' files.className = 'hidden' - filesStatus.innerHTML = '' - picture.innerHTML = '' startButton.disabled = false - stopButton.disabled = true multihashInput.value = null - picture.className = 'hidden' // Remove old event listeners rootElement.removeEventListener('drop', onDrop) startButton.removeEventListener('click', start) - stopButton.removeEventListener('click', stop) catButton.removeEventListener('click', catFile) // Setup event listeners for interaction rootElement.addEventListener('drop', onDrop) startButton.addEventListener('click', start) - stopButton.addEventListener('click', stop) catButton.addEventListener('click', catFile) } function updateView (state, ipfs) { if (state === 'ready') { // Set the header to display the current state - output.innerHTML = 'πŸš€ IPFS started' + output.innerHTML = 'IPFS running now' // Display IPFS info - details.innerHTML = '
' + - '

IPFS Node

' + - 'ID
' + - peerInfo.id + '

' + - 'Address
' + - peerInfo.addresses[0] + '

' + - 'IPFS Data Directory
' + - dirInput.value + details.innerHTML = '
' + + 'Your ID:
' + + peerInfo.id + '

' // Set the file status - filesStatus.innerHTML = 'Drop a picture here to add it to IPFS.' details.className = 'visible' peers.className = 'visible' files.className = 'visible' - stopButton.disabled = false } else if (state === 'starting') { - output.innerHTML = 'πŸ“‘ IPFS starting' + output.innerHTML = 'IPFS daemon is starting...' startButton.disabled = true directory.className = 'hidden' } else if (state === 'stopped') { diff --git a/examples/transfer-files/complete/public/js/create-node.js b/examples/transfer-files/complete/public/js/create-node.js index 3051edb4b7..aa8e3356b6 100644 --- a/examples/transfer-files/complete/public/js/create-node.js +++ b/examples/transfer-files/complete/public/js/create-node.js @@ -19,13 +19,9 @@ window.createNode = (options, callback) => { return callback(err) } - // TODO change to use wstar in DNS instead - const host = options.signalAddr.split(':')[0] || '127.0.0.1' - const port = options.signalAddr.split(':')[1] || 9090 + const wstarMultiaddr = `/libp2p-webrtc-star/dns4/${options.signalAddr}/wss/ipfs/${config.Identity.PeerID}` - const wstarMultiaddr = `/libp2p-webrtc-star/ip4/${host}/tcp/${port}/ws/ipfs/${config.Identity.PeerID}` - - config.Addresses.Swarm = [ wstarMultiaddr ] + config.Addresses.Swarm = config.Addresses.Swarm.concat([ wstarMultiaddr ]) node.config.replace(config, bootNode) }) From 3cca07bfaa37e6a06d3e467defede9ada3231eb5 Mon Sep 17 00:00:00 2001 From: Victor Bjelkholm Date: Fri, 27 Jan 2017 16:54:14 +0100 Subject: [PATCH 05/10] Latest changes --- .../complete/public/css/app.css | 72 +++---- .../transfer-files/complete/public/index.html | 133 ++++++++---- .../transfer-files/complete/public/js/app.js | 193 ++++++++++++------ 3 files changed, 263 insertions(+), 135 deletions(-) diff --git a/examples/transfer-files/complete/public/css/app.css b/examples/transfer-files/complete/public/css/app.css index 8fedfc1f27..8b31a469ab 100644 --- a/examples/transfer-files/complete/public/css/app.css +++ b/examples/transfer-files/complete/public/css/app.css @@ -20,34 +20,10 @@ body { right: 20px; } -h1 { - font-size: 2em; - font-weight: 300; - margin: 0em; -} - -h2 { - font-size: 25px; - font-weight: 700; -} - -h3 { - font-size: 1.0em; - font-weight: 700; -} - -p { - font-size: 17px; -} - .center { text-align: center; } -label { - margin-right: 1em; -} - input { margin-right: 1em; } @@ -69,17 +45,23 @@ input { .wrapper { position: relative; margin: 50px auto; - padding-top: 50px; - padding-bottom: 50px; + width: 1020px; +} + +.box { width: 500px; + padding: 50px; background-color: rgba(255, 255, 255, 0.05); border-radius: 1px; + text-align: center; + float: left; + margin: 5px; + box-sizing: border-box; } .introduction { width: 70%; margin: 0px auto; - text-align: center; } .on-off-button { @@ -88,16 +70,12 @@ input { right: 10px; } -#directory { - text-align: center; -} - #directory .group { margin: 30px; } -#directory label { - font-size: 18px; +label { + font-weight: bolder; display: block; } @@ -143,9 +121,7 @@ button:hover { cursor: pointer; } -#directory input { - width: 70%; - font-size: 16px; +input { border: 2px solid #444; color: black; padding: 10px; @@ -153,7 +129,27 @@ button:hover { font-family: monospace; } +#directory input { + width: 70%; + font-size: 16px; +} + +.group { + vertical-align: middle; + margin-top: 15px; + text-align: center; +} + .group-item { - display: inline; - line-height: 30px; + display: block; + text-align: center; + margin-top: 10px; +} + +.state { + display: none; +} + +.state.stopped { + display: block; } diff --git a/examples/transfer-files/complete/public/index.html b/examples/transfer-files/complete/public/index.html index f302927138..556b7b4bf2 100644 --- a/examples/transfer-files/complete/public/index.html +++ b/examples/transfer-files/complete/public/index.html @@ -6,11 +6,88 @@ IPFS - Transfer Files Example - - - - -
+
+ + + + +
+
+
+
+
+

+ Here you can configure where to keep your IPFS configuration + and which signaling server to use. +

+

+ When you're ready, click "Go Online" to start the IPFS daemon + in the background +

+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+
+
+

IPFS daemon is starting

+
+
+

Now the daemon is running and you're connected!

+

Now let's connect to a go-ipfs daemon running on your desktop

+
+
+ +
+
+ +
+
+ +
+
+
+
+

Connecting to your go-ipfs daemon

+
+
+

+ Now you're connected! Let's look at some content that you've + added in go-ipfs +

+ + +
+
+

Loading content

+
+
+

And here is your content!

+

+				
+
+
+ Information about your js-ipfs daemon +
+
+ +