Commit 46ef9e90772ab5e16ae211a9cfab1ea45fcaa806
initial commit
Ev Bogue committed on 7/22/2018, 2:50:13 PMFiles changed
.gitignore | added |
bin.js | added |
config.js | added |
config/index.js | added |
config/inject.js | added |
index.js | added |
keys.js | added |
manifest.json | added |
package.json | added |
readme.md | added |
render.js | added |
scuttlebot.js | added |
style.css | added |
style.css.json | added |
style.js | added |
tools.js | added |
views.js | added |
bin.js | ||
---|---|---|
@@ -1,0 +1,114 @@ | ||
1 … | +var fs = require('fs') | |
2 … | +var path = require('path') | |
3 … | +var ssbKeys = require('ssb-keys') | |
4 … | +var stringify = require('pull-stringify') | |
5 … | +var open = require('opn') | |
6 … | +var home = require('os-homedir')() | |
7 … | +var nonPrivate = require('non-private-ip') | |
8 … | +var muxrpcli = require('muxrpcli') | |
9 … | + | |
10 … | +var SEC = 1e3 | |
11 … | +var MIN = 60*SEC | |
12 … | + | |
13 … | +var network = 'ssb' | |
14 … | +//var network = 'decent' | |
15 … | +//var network = 'testnet' | |
16 … | + | |
17 … | +var config = require('./config/inject')(network) | |
18 … | + | |
19 … | +config.keys = ssbKeys.loadOrCreateSync(path.join(config.path, 'secret')) | |
20 … | + | |
21 … | +var mvdClient = fs.readFileSync(path.join('./build/index.html')) | |
22 … | + | |
23 … | +var manifestFile = path.join(config.path, 'manifest.json') | |
24 … | + | |
25 … | +var argv = process.argv.slice(2) | |
26 … | +var i = argv.indexOf('--') | |
27 … | +var conf = argv.slice(i+1) | |
28 … | +argv = ~i ? argv.slice(0, i) : argv | |
29 … | + | |
30 … | +if (argv[0] == 'server') { | |
31 … | + | |
32 … | + var createSbot = require('scuttlebot') | |
33 … | + .use(require('scuttlebot/plugins/master')) | |
34 … | + .use(require('scuttlebot/plugins/gossip')) | |
35 … | + .use(require('scuttlebot/plugins/replicate')) | |
36 … | + .use(require('ssb-friends')) | |
37 … | + .use(require('ssb-blobs')) | |
38 … | + .use(require('ssb-links')) | |
39 … | + .use(require('ssb-ebt')) | |
40 … | + .use(require('scuttlebot/plugins/invite')) | |
41 … | + .use(require('scuttlebot/plugins/local')) | |
42 … | + .use(require('decent-ws')) | |
43 … | + .use({ | |
44 … | + name: 'serve', | |
45 … | + version: '1.0.0', | |
46 … | + init: function (sbot) { | |
47 … | + sbot.ws.use(function (req, res, next) { | |
48 … | + var send = config | |
49 … | + delete send.keys // very important to keep this, as it removes the server keys from the config before broadcast | |
50 … | + send.address = sbot.ws.getAddress() | |
51 … | + sbot.invite.create({modern: true}, function (err, cb) { | |
52 … | + send.invite = cb | |
53 … | + }) | |
54 … | + if(req.url == '/') | |
55 … | + res.end(mvdClient) | |
56 … | + if(req.url == '/get-config') | |
57 … | + res.end(JSON.stringify(send)) | |
58 … | + else next() | |
59 … | + }) | |
60 … | + } | |
61 … | + }) | |
62 … | + | |
63 … | + open('http://localhost:' + config.ws.port, {wait: false}) | |
64 … | + | |
65 … | + var server = createSbot(config) | |
66 … | + | |
67 … | + fs.writeFileSync(manifestFile, JSON.stringify(server.getManifest(), null, 2)) | |
68 … | +} else { | |
69 … | + | |
70 … | + var manifest | |
71 … | + try { | |
72 … | + manifest = JSON.parse(fs.readFileSync(manifestFile)) | |
73 … | + } catch (err) { | |
74 … | + throw explain(err, | |
75 … | + 'no manifest file' | |
76 … | + + '- should be generated first time server is run' | |
77 … | + ) | |
78 … | + } | |
79 … | + | |
80 … | + // connect | |
81 … | + require('ssb-client')(config.keys, { | |
82 … | + manifest: manifest, | |
83 … | + port: config.port, | |
84 … | + host: config.host||'localhost', | |
85 … | + caps: config.caps, | |
86 … | + key: config.key || config.keys.id | |
87 … | + }, function (err, rpc) { | |
88 … | + if(err) { | |
89 … | + if (/could not connect/.test(err.message)) { | |
90 … | + console.log('Error: Could not connect to the scuttlebot server.') | |
91 … | + console.log('Use the "server" command to start it.') | |
92 … | + if(config.verbose) throw err | |
93 … | + process.exit(1) | |
94 … | + } | |
95 … | + throw err | |
96 … | + } | |
97 … | + | |
98 … | + // add some extra commands | |
99 … | + manifest.version = 'async' | |
100 … | + manifest.config = 'sync' | |
101 … | + rpc.version = function (cb) { | |
102 … | + console.log(require('./package.json').version) | |
103 … | + cb() | |
104 … | + } | |
105 … | + rpc.config = function (cb) { | |
106 … | + console.log(JSON.stringify(config, null, 2)) | |
107 … | + cb() | |
108 … | + } | |
109 … | + | |
110 … | + // run commandline flow | |
111 … | + muxrpcli(argv, manifest, rpc, config.verbose) | |
112 … | + }) | |
113 … | +} | |
114 … | + |
config.js | ||
---|---|---|
@@ -1,0 +1,34 @@ | ||
1 … | +var http = require('http') | |
2 … | + | |
3 … | +module.exports = function () { | |
4 … | + var host = window.location.origin | |
5 … | + | |
6 … | + function getConfig () { | |
7 … | + http.get(host + '/get-config', function (res) { | |
8 … | + res.on('data', function (data, remote) { | |
9 … | + var config = data | |
10 … | + localStorage[host] = config | |
11 … | + }) | |
12 … | + }) | |
13 … | + } | |
14 … | + | |
15 … | + if (localStorage[host]) { | |
16 … | + var config = JSON.parse(localStorage[host]) | |
17 … | + getConfig() | |
18 … | + } else { | |
19 … | + getConfig() | |
20 … | + setTimeout(function () { | |
21 … | + location.reload() | |
22 … | + }, 1000) | |
23 … | + } | |
24 … | + | |
25 … | + config.blobsUrl = host + '/blobs/get/' | |
26 … | + config.emojiUrl = host + '/img/emoji/' | |
27 … | + | |
28 … | + if (config.ws.remote) | |
29 … | + config.remote = config.ws.remote | |
30 … | + else | |
31 … | + config.remote = config.address | |
32 … | + | |
33 … | + return config | |
34 … | +} |
config/inject.js | ||
---|---|---|
@@ -1,0 +1,68 @@ | ||
1 … | +var path = require('path') | |
2 … | +var home = require('os-homedir') | |
3 … | +var nonPrivate = require('non-private-ip') | |
4 … | +var merge = require('deep-extend') | |
5 … | +var id = require('ssb-keys') | |
6 … | +var RC = require('rc') | |
7 … | +var SEC = 1e3 | |
8 … | +var MIN = 60*SEC | |
9 … | + | |
10 … | +module.exports = function (name, override) { | |
11 … | + console.log('Using the ' + name + ' config') | |
12 … | + | |
13 … | + var network | |
14 … | + | |
15 … | + if (name === 'ssb') { | |
16 … | + network = { | |
17 … | + port: 8008, | |
18 … | + ws: { | |
19 … | + port: 8080 | |
20 … | + }, | |
21 … | + caps: { | |
22 … | + shs: '1KHLiKZvAvjbY1ziZEHMXawbCEIM6qwjCDm3VYRan/s=', | |
23 … | + sign: null | |
24 … | + } | |
25 … | + } | |
26 … | + } | |
27 … | + | |
28 … | + if (name === 'testnet') { | |
29 … | + network = { | |
30 … | + port: 9999, | |
31 … | + ws: { | |
32 … | + port: 1337 | |
33 … | + }, | |
34 … | + caps: { | |
35 … | + shs: 'sR74I0+OW6LBYraQQ2YtFtqV5Ns77Tv5DyMfyWbrlpI=', | |
36 … | + sign: null | |
37 … | + } | |
38 … | + } | |
39 … | + } | |
40 … | + | |
41 … | + var HOME = home() || 'browser' //most probably browser | |
42 … | + | |
43 … | + return RC(name, merge(network, { | |
44 … | + name: name, | |
45 … | + //standard stuff that probably doesn't need to change below | |
46 … | + host: nonPrivate.v4 || '', | |
47 … | + timeout: 0, | |
48 … | + allowPrivate: true, | |
49 … | + pub: true, | |
50 … | + local: true, | |
51 … | + friends: { | |
52 … | + dunbar: 150, | |
53 … | + hops: 3 | |
54 … | + }, | |
55 … | + gossip: { | |
56 … | + connections: 3 | |
57 … | + }, | |
58 … | + path: path.join(HOME, '.' + name), | |
59 … | + timers: { | |
60 … | + connection: 0, | |
61 … | + reconnect: 5*SEC, | |
62 … | + ping: 5*MIN, | |
63 … | + handshake: 5*SEC | |
64 … | + }, | |
65 … | + master: [], | |
66 … | + party: true //disable quotas | |
67 … | + }, override || {})) | |
68 … | +} |
index.js | ||
---|---|---|
@@ -1,0 +1,29 @@ | ||
1 … | +var h = require('hyperscript') | |
2 … | +var route = require('./views') | |
3 … | + | |
4 … | +var id = require('./keys').id | |
5 … | + | |
6 … | +document.head.appendChild(h('style', require('./style.css.json'))) | |
7 … | + | |
8 … | +var screen = h('div#screen') | |
9 … | + | |
10 … | +var search = h('input.search', {placeholder: 'Search'}) | |
11 … | + | |
12 … | +var nav = h('div.navbar', | |
13 … | + h('div.internal', | |
14 … | + h('li', h('a', {href: '#' + id}, id)), | |
15 … | + h('li', h('a', {href: '#' }, 'Stream')), | |
16 … | + ) | |
17 … | +) | |
18 … | + | |
19 … | +document.body.appendChild(nav) | |
20 … | +document.body.appendChild(screen) | |
21 … | +route() | |
22 … | + | |
23 … | +window.onhashchange = function () { | |
24 … | + var oldscreen = document.getElementById('screen') | |
25 … | + var newscreen = h('div#screen') | |
26 … | + oldscreen.parentNode.replaceChild(newscreen, oldscreen) | |
27 … | + route() | |
28 … | +} | |
29 … | + |
keys.js | ||
---|---|---|
@@ -1,0 +1,6 @@ | ||
1 … | + | |
2 … | +var config = require('./config')() | |
3 … | +var ssbKeys = require('ssb-keys') | |
4 … | +var path = require('path') | |
5 … | + | |
6 … | +module.exports = ssbKeys.loadOrCreateSync(path.join(config.caps.shs + '/secret')) |
manifest.json | ||
---|---|---|
@@ -1,0 +1,63 @@ | ||
1 … | +{ | |
2 … | + "auth": "async", | |
3 … | + "address": "sync", | |
4 … | + "manifest": "sync", | |
5 … | + "get": "async", | |
6 … | + "createFeedStream": "source", | |
7 … | + "createLogStream": "source", | |
8 … | + "messagesByType": "source", | |
9 … | + "createHistoryStream": "source", | |
10 … | + "createUserStream": "source", | |
11 … | + "links": "source", | |
12 … | + "relatedMessages": "async", | |
13 … | + "add": "async", | |
14 … | + "publish": "async", | |
15 … | + "getAddress": "sync", | |
16 … | + "getLatest": "async", | |
17 … | + "latest": "source", | |
18 … | + "latestSequence": "async", | |
19 … | + "whoami": "sync", | |
20 … | + "usage": "sync", | |
21 … | + "gossip": { | |
22 … | + "peers": "sync", | |
23 … | + "add": "sync", | |
24 … | + "ping": "duplex", | |
25 … | + "connect": "async", | |
26 … | + "changes": "source", | |
27 … | + "reconnect": "sync" | |
28 … | + }, | |
29 … | + "friends": { | |
30 … | + "all": "async", | |
31 … | + "hops": "async", | |
32 … | + "createFriendStream": "source", | |
33 … | + "get": "sync" | |
34 … | + }, | |
35 … | + "replicate": { | |
36 … | + "changes": "source" | |
37 … | + }, | |
38 … | + "invite": { | |
39 … | + "create": "async", | |
40 … | + "accept": "async", | |
41 … | + "use": "async" | |
42 … | + }, | |
43 … | + "block": { | |
44 … | + "isBlocked": "sync" | |
45 … | + }, | |
46 … | + "private": { | |
47 … | + "publish": "async", | |
48 … | + "unbox": "sync" | |
49 … | + }, | |
50 … | + "blobs": { | |
51 … | + "get": "source", | |
52 … | + "add": "sink", | |
53 … | + "ls": "source", | |
54 … | + "has": "async", | |
55 … | + "size": "async", | |
56 … | + "meta": "async", | |
57 … | + "want": "async", | |
58 … | + "push": "async", | |
59 … | + "changes": "source", | |
60 … | + "createWants": "source" | |
61 … | + }, | |
62 … | + "ws": {}, | |
63 … | +} |
package.json | ||
---|---|---|
@@ -1,0 +1,46 @@ | ||
1 … | +{ | |
2 … | + "name": "mvd", | |
3 … | + "version": "1.10.1", | |
4 … | + "description": "minimum viable decent", | |
5 … | + "main": "index.js", | |
6 … | + "scripts": { | |
7 … | + "start": "node bin server", | |
8 … | + "decent": "node bin server --appname=decent", | |
9 … | + "ssb": "node bin server --appname=ssb", | |
10 … | + "testnet": "node bin server --appname=testnet", | |
11 … | + "build": "node style.js && mkdir -p build && browserify index.js | indexhtmlify > build/index.html" | |
12 … | + }, | |
13 … | + "devDependencies": { | |
14 … | + "browserify": "^16.2.2", | |
15 … | + "indexhtmlify": "^1.3.1" | |
16 … | + }, | |
17 … | + "author": "Ev Bogue <ev@evbogue.com>", | |
18 … | + "license": "MIT", | |
19 … | + "dependencies": { | |
20 … | + "decent-ws": "1.0.4", | |
21 … | + "deep-extend": "^0.6.0", | |
22 … | + "human-time": "0.0.1", | |
23 … | + "hyperloadmore": "^1.1.0", | |
24 … | + "hyperscript": "^2.0.2", | |
25 … | + "hyperscroll": "^1.0.0", | |
26 … | + "muxrpcli": "^1.1.0", | |
27 … | + "non-private-ip": "^1.4.3", | |
28 … | + "opn": "^5.3.0", | |
29 … | + "os-homedir": "^1.0.2", | |
30 … | + "pull-more": "^1.1.0", | |
31 … | + "pull-reconnect": "0.0.3", | |
32 … | + "pull-stream": "^3.6.8", | |
33 … | + "pull-stringify": "^2.0.0", | |
34 … | + "rc": "^1.2.7", | |
35 … | + "scuttlebot": "^11.3.0", | |
36 … | + "ssb-blobs": "^1.1.5", | |
37 … | + "ssb-client": "^4.5.7", | |
38 … | + "ssb-ebt": "^5.1.5", | |
39 … | + "ssb-feed": "^2.3.0", | |
40 … | + "ssb-friends": "^2.4.0", | |
41 … | + "ssb-keys": "^7.0.16", | |
42 … | + "ssb-links": "^3.0.3", | |
43 … | + "ssb-markdown": "^3.6.0", | |
44 … | + "ssb-ref": "^2.11.1" | |
45 … | + } | |
46 … | +} |
readme.md | ||
---|---|---|
@@ -1,0 +1,12 @@ | ||
1 … | +# mvp | |
2 … | + | |
3 … | +### Minimum Viable Phoenix | |
4 … | + | |
5 … | +This is for my up-coming series on how to code clients using ssb. | |
6 … | + | |
7 … | +### history | |
8 … | + | |
9 … | +`mvp` is a simplified fork of [`mvd`](%NPNNvcnTMZUFZSWl/2Z4XX+YSdqsqOhyPacp+lgpQUw=.sha256) | |
10 … | + | |
11 … | +--- | |
12 … | +MIT |
render.js | ||
---|---|---|
@@ -1,0 +1,16 @@ | ||
1 … | +var h = require('hyperscript') | |
2 … | +var tools = require('./tools') | |
3 … | + | |
4 … | +function hash () { | |
5 … | + return window.location.hash.substring(1) | |
6 … | +} | |
7 … | + | |
8 … | +module.exports = function (msg) { | |
9 … | + var message = h('div.message#' + msg.key.substring(0, 44)) | |
10 … | + | |
11 … | + //FULL FALLBACK | |
12 … | + message.appendChild(tools.header(msg)) | |
13 … | + message.appendChild(h('pre', tools.rawJSON(msg.value.content))) | |
14 … | + | |
15 … | + return message | |
16 … | +} |
scuttlebot.js | ||
---|---|---|
@@ -1,0 +1,75 @@ | ||
1 … | +var pull = require('pull-stream') | |
2 … | +var ssbKeys = require('ssb-keys') | |
3 … | +var ref = require('ssb-ref') | |
4 … | +var reconnect = require('pull-reconnect') | |
5 … | + | |
6 … | +var config = require('./config')() | |
7 … | +var createClient = require('ssb-client') | |
8 … | +var createFeed = require('ssb-feed') | |
9 … | + | |
10 … | +var keys = require('./keys') | |
11 … | + | |
12 … | +var CACHE = {} | |
13 … | + | |
14 … | +var rec = reconnect(function (isConn) { | |
15 … | + function notify (value) { | |
16 … | + isConn(value) | |
17 … | + } | |
18 … | + | |
19 … | + createClient(keys, { | |
20 … | + manifest: require('./manifest.json'), | |
21 … | + remote: config.remote, | |
22 … | + caps: config.caps | |
23 … | + }, function (err, _sbot) { | |
24 … | + if(err) | |
25 … | + return notify(err) | |
26 … | + | |
27 … | + sbot = _sbot | |
28 … | + sbot.on('closed', function () { | |
29 … | + sbot = null | |
30 … | + notify(new Error('closed')) | |
31 … | + }) | |
32 … | + | |
33 … | + notify() | |
34 … | + }) | |
35 … | +}) | |
36 … | + | |
37 … | +var internal = { | |
38 … | + getLatest: rec.async(function (id, cb) { | |
39 … | + sbot.getLatest(id, cb) | |
40 … | + }), | |
41 … | + add: rec.async(function (msg, cb) { | |
42 … | + sbot.add(msg, cb) | |
43 … | + }) | |
44 … | +} | |
45 … | + | |
46 … | +var feed = createFeed(internal, keys, {remote: true}) | |
47 … | + | |
48 … | +module.exports = { | |
49 … | + createLogStream: rec.source(function (opts) { | |
50 … | + return pull( | |
51 … | + sbot.createLogStream(opts), | |
52 … | + pull.through(function (e) { | |
53 … | + CACHE[e.key] = CACHE[e.key] || e.value | |
54 … | + }) | |
55 … | + ) | |
56 … | + }), | |
57 … | + userStream: rec.source(function (config) { | |
58 … | + return pull( | |
59 … | + sbot.createUserStream(config), | |
60 … | + pull.through(function (e) { | |
61 … | + CACHE[e.key] = CACHE[e.key] || e.value | |
62 … | + }) | |
63 … | + ) | |
64 … | + }), | |
65 … | + get: rec.async(function (key, cb) { | |
66 … | + if('function' !== typeof cb) | |
67 … | + throw new Error('cb must be function') | |
68 … | + if(CACHE[key]) cb(null, CACHE[key]) | |
69 … | + else sbot.get(key, function (err, value) { | |
70 … | + if(err) return cb(err) | |
71 … | + cb(null, CACHE[key] = value) | |
72 … | + }) | |
73 … | + }) | |
74 … | +} | |
75 … | + |
style.css | ||
---|---|---|
@@ -1,0 +1,87 @@ | ||
1 … | +body { | |
2 … | + margin: 0; | |
3 … | +} | |
4 … | + | |
5 … | +#screen { | |
6 … | + position: absolute; | |
7 … | + top: 35px; | |
8 … | + bottom: 0px; | |
9 … | + left: 0px; | |
10 … | + right: 0px; | |
11 … | +} | |
12 … | + | |
13 … | +.hyperscroll { | |
14 … | + width: 100%; | |
15 … | +} | |
16 … | + | |
17 … | +.header { | |
18 … | + padding-bottom: .7em; | |
19 … | + border-bottom: 1px solid #ccc; | |
20 … | +} | |
21 … | + | |
22 … | +.navbar { | |
23 … | + border-bottom: 1px solid #ccc; | |
24 … | + width: 100%; | |
25 … | + position: fixed; | |
26 … | + z-index: 1000; | |
27 … | + margin: 0; | |
28 … | + padding-top: .3em; | |
29 … | + padding-bottom: .3em; | |
30 … | + left: 0; right: 0; | |
31 … | + top: 0; | |
32 … | +} | |
33 … | + | |
34 … | +.navbar .internal { | |
35 … | + max-width: 97%; | |
36 … | + margin-left: auto; | |
37 … | + margin-right: auto; | |
38 … | +} | |
39 … | + | |
40 … | +.navbar li { | |
41 … | + margin-top: .3em; | |
42 … | + float: left; | |
43 … | + margin-right: .6em; | |
44 … | + margin-left: .3em; | |
45 … | + list-style-type: none; | |
46 … | +} | |
47 … | + | |
48 … | +.content { | |
49 … | + max-width: 680px; | |
50 … | + margin-left: auto; | |
51 … | + margin-right: auto; | |
52 … | +} | |
53 … | + | |
54 … | +.hyperscroll > .content { | |
55 … | + max-width: 680px; | |
56 … | + margin-left: auto; | |
57 … | + margin-right: auto; | |
58 … | +} | |
59 … | + | |
60 … | +.message img, .message video { | |
61 … | + max-width: 100%; | |
62 … | +} | |
63 … | + | |
64 … | +.timestamp, .votes { | |
65 … | + float: right; | |
66 … | +} | |
67 … | + | |
68 … | +pre { | |
69 … | + width: 100%; | |
70 … | + display: block; | |
71 … | +} | |
72 … | + | |
73 … | +code { | |
74 … | + display: inline-block; | |
75 … | + vertical-align: bottom; | |
76 … | +} | |
77 … | + | |
78 … | +code, pre { | |
79 … | +overflow: auto; | |
80 … | +word-break: break-all; | |
81 … | +word-wrap: break-word; | |
82 … | +white-space: pre; | |
83 … | +white-space: -moz-pre-wrap; | |
84 … | +white-space: pre-wrap; | |
85 … | +white-space: pre\9; | |
86 … | +} | |
87 … | + |
style.css.json | ||
---|---|---|
@@ -1,0 +1,1 @@ | ||
1 … | +"body {\n margin: 0;\n}\n\n#screen {\n position: absolute;\n top: 35px;\n bottom: 0px;\n left: 0px;\n right: 0px;\n}\n\n.hyperscroll {\n width: 100%;\n}\n\n.header {\n padding-bottom: .7em;\n border-bottom: 1px solid #ccc;\n}\n\n.navbar {\n border-bottom: 1px solid #ccc;\n width: 100%;\n position: fixed;\n z-index: 1000;\n margin: 0;\n padding-top: .3em;\n padding-bottom: .3em;\n left: 0; right: 0;\n top: 0;\n}\n\n.navbar .internal {\n max-width: 97%;\n margin-left: auto;\n margin-right: auto;\n}\n\n.navbar li {\n margin-top: .3em;\n float: left;\n margin-right: .6em;\n margin-left: .3em;\n list-style-type: none;\n}\n\n.content {\n max-width: 680px;\n margin-left: auto;\n margin-right: auto;\n}\n\n.hyperscroll > .content {\n max-width: 680px;\n margin-left: auto;\n margin-right: auto;\n}\n\n.message img, .message video {\n max-width: 100%;\n}\n\n.timestamp, .votes {\n float: right;\n}\n\npre {\n width: 100%;\n display: block;\n}\n\ncode {\n display: inline-block;\n vertical-align: bottom;\n}\n\ncode, pre {\noverflow: auto;\nword-break: break-all;\nword-wrap: break-word;\nwhite-space: pre;\nwhite-space: -moz-pre-wrap;\nwhite-space: pre-wrap;\nwhite-space: pre\\9;\n}\n\n" |
style.js | ||
---|---|---|
@@ -1,0 +1,8 @@ | ||
1 … | +var fs = require('fs') | |
2 … | +var path = require('path') | |
3 … | + | |
4 … | +fs.writeFileSync( | |
5 … | + path.join(__dirname, 'style.css.json'), | |
6 … | + JSON.stringify(fs.readFileSync(path.join(__dirname, 'style.css'), 'utf8')) | |
7 … | +) | |
8 … | + |
tools.js | ||
---|---|---|
@@ -1,0 +1,46 @@ | ||
1 … | +var h = require('hyperscript') | |
2 … | +var human = require('human-time') | |
3 … | +var ref = require('ssb-ref') | |
4 … | + | |
5 … | +var markdown = require('ssb-markdown') | |
6 … | +var config = require('./config')() | |
7 … | + | |
8 … | +module.exports.timestamp = function (msg, edited) { | |
9 … | + var timestamp = h('span.timestamp', h('a', {href: '#' + msg.key}, human(new Date(msg.value.timestamp)))) | |
10 … | + return timestamp | |
11 … | +} | |
12 … | + | |
13 … | +module.exports.header = function (msg) { | |
14 … | + var header = h('div.header') | |
15 … | + | |
16 … | + header.appendChild(h('span.avatar', | |
17 … | + h('a', {href: '#' + msg.value.author}, | |
18 … | + msg.value.author | |
19 … | + ) | |
20 … | + ) | |
21 … | + ) | |
22 … | + | |
23 … | + header.appendChild(exports.timestamp(msg)) | |
24 … | + return header | |
25 … | +} | |
26 … | + | |
27 … | +module.exports.rawJSON = function (obj) { | |
28 … | + return JSON.stringify(obj, null, 2) | |
29 … | + .split(/([%@&][a-zA-Z0-9\/\+]{43}=*\.[\w]+)/) | |
30 … | + .map(function (e) { | |
31 … | + if(ref.isMsg(e) || ref.isFeed(e) || ref.isBlob(e)) { | |
32 … | + return h('a', {href: '#' + e}, e) | |
33 … | + } | |
34 … | + return e | |
35 … | + }) | |
36 … | +} | |
37 … | + | |
38 … | + | |
39 … | +module.exports.markdown = function (msg, md) { | |
40 … | + return {innerHTML: markdown.block(msg, {toUrl: function (url, image) { | |
41 … | + if(url[0] == '%' || url[0] == '@') return '#' + url | |
42 … | + if(!image) return url | |
43 … | + if(url[0] !== '&') return url | |
44 … | + return config.blobsUrl + url | |
45 … | + }})} | |
46 … | +} |
views.js | |||
---|---|---|---|
@@ -1,0 +1,71 @@ | |||
1 … | +var pull = require('pull-stream') | ||
2 … | +var sbot = require('./scuttlebot') | ||
3 … | +var hyperscroll = require('hyperscroll') | ||
4 … | +var More = require('pull-more') | ||
5 … | +var stream = require('hyperloadmore/stream') | ||
6 … | +var h = require('hyperscript') | ||
7 … | +var render = require('./render') | ||
8 … | +var ref = require('ssb-ref') | ||
9 … | +var keys = require('./keys') | ||
10 … | + | ||
11 … | +var logStream = function () { | ||
12 … | + var content = h('div.content') | ||
13 … | + var screen = document.getElementById('screen') | ||
14 … | + screen.appendChild(hyperscroll(content)) | ||
15 … | + | ||
16 … | + function createStream (opts) { | ||
17 … | + return pull( | ||
18 … | + More(sbot.createLogStream, opts), | ||
19 … | + pull.map(function (msg) { | ||
20 … | + return render(msg) | ||
21 … | + }) | ||
22 … | + ) | ||
23 … | + } | ||
24 … | + | ||
25 … | + pull( | ||
26 … | + createStream({old: false, limit: 100}), | ||
27 … | + stream.top(content) | ||
28 … | + ) | ||
29 … | + | ||
30 … | + pull( | ||
31 … | + createStream({reverse: true, live: false, limit: 100}), | ||
32 … | + stream.bottom(content) | ||
33 … | + ) | ||
34 … | +} | ||
35 … | + | ||
36 … | +var userStream = function (src) { | ||
37 … | + var content = h('div.content') | ||
38 … | + var screen = document.getElementById('screen') | ||
39 … | + screen.appendChild(hyperscroll(content)) | ||
40 … | + function createStream (opts) { | ||
41 … | + return pull( | ||
42 … | + More(sbot.userStream, opts, ['value', 'sequence']), | ||
43 … | + pull.map(function (msg) { | ||
44 … | + return render(msg) | ||
45 … | + }) | ||
46 … | + ) | ||
47 … | + } | ||
48 … | + | ||
49 … | + pull( | ||
50 … | + createStream({old: false, limit: 10, id: src}), | ||
51 … | + stream.top(content) | ||
52 … | + ) | ||
53 … | + | ||
54 … | + pull( | ||
55 … | + createStream({reverse: true, live: false, limit: 10, id: src}), | ||
56 … | + stream.bottom(content) | ||
57 … | + ) | ||
58 … | +} | ||
59 … | + | ||
60 … | +function hash () { | ||
61 … | + return window.location.hash.substring(1) | ||
62 … | +} | ||
63 … | + | ||
64 … | +module.exports = function () { | ||
65 … | + var src = hash() | ||
66 … | + if (ref.isFeed(src)) { | ||
67 … | + userStream(src) | ||
68 … | + } else { | ||
69 … | + logStream() | ||
70 … | + } | ||
71 … | +} |
Built with git-ssb-web