git ssb

0+

ev / 0qc



Commit 231a81f9e0adf80fa3566be93221cf1f5158fcb6

initial commit

Ev Bogue committed on 3/6/2019, 7:11:26 PM

Files changed

.gitignoreadded
avatar.jsadded
bin.jsadded
compose.jsadded
config.jsadded
index.jsadded
keys.jsadded
manifest.jsonadded
package-lock.jsonadded
package.jsonadded
readme.mdadded
render.jsadded
scuttlebot.jsadded
style.cssadded
style.css.jsonadded
style.jsadded
tools.jsadded
views.jsadded
.gitignoreView
@@ -1,0 +1,2 @@
1 +build
2 +node_modules
avatar.jsView
@@ -1,0 +1,92 @@
1 +var pull = require('pull-stream')
2 +var query = require('./scuttlebot').query
3 +var h = require('hyperscript')
4 +var visualize = require('visualize-buffer')
5 +
6 +var avatar = require('ssb-avatar')
7 +
8 +var sbot = require('./scuttlebot')
9 +
10 +var config = require('./config')()
11 +
12 +var id = require('./keys').id
13 +
14 +var ref = require('ssb-ref')
15 +
16 +module.exports.name = function (key) {
17 +
18 + var avatarname = h('span', key.substring(0, 10))
19 + if (ref.isFeedId(key)) {
20 + avatar(sbot, id, key, function (err, data) {
21 + if (err) throw err
22 + if (data.name) {
23 + if (data.name[0] != '@') {
24 + var name = '@' + data.name
25 + } else {
26 + var name = data.name
27 + }
28 + localStorage[key + 'name'] = name
29 + avatarname.textContent = name
30 + }
31 + })
32 + }
33 + return avatarname
34 +}
35 +
36 +module.exports.image = function (key) {
37 + var img = visualize(new Buffer(key.substring(1), 'base64'), 256)
38 +
39 + if (ref.isFeedId(key)) {
40 + avatar(sbot, id, key, function (err, data) {
41 + if (err) throw err
42 + if (data.image) {
43 + localStorage[key + 'image'] = data.image
44 + img.src = config.blobsUrl + data.image
45 + }
46 + })
47 + }
48 + return img
49 +}
50 +
51 +module.exports.cachedName = function (key) {
52 + var avatarname = h('span', key.substring(0, 10))
53 +
54 + if (localStorage[key + 'name']) {
55 + avatarname.textContent = localStorage[key + 'name']
56 + } else {
57 + if (ref.isFeedId(key)) {
58 + avatar(sbot, id, key, function (err, data) {
59 + if (data.name) {
60 + if (data.name[0] != '@') {
61 + var name = '@' + data.name
62 + } else {
63 + var name = data.name
64 + }
65 + localStorage[key + 'name'] = name
66 + avatarname.textContent = name
67 + }
68 + })
69 + }
70 + }
71 +
72 + return avatarname
73 +}
74 +
75 +module.exports.cachedImage = function (key) {
76 + var img = visualize(new Buffer(key.substring(1), 'base64'), 256)
77 +
78 + if (localStorage[key + 'image']) {
79 + img.src = config.blobsUrl + localStorage[key + 'image']
80 + } else {
81 + if (ref.isFeedId(key)) {
82 + avatar(sbot, id, key, function (err, data) {
83 + if (data.image) {
84 + localStorage[key + 'image'] = data.image
85 + img.src = config.blobsUrl + data.image
86 + }
87 + })
88 + }
89 + }
90 +
91 + return img
92 +}
bin.jsView
@@ -1,0 +1,186 @@
1 +#! /usr/bin/env node
2 +
3 +var fs = require('fs')
4 +var path = require('path')
5 +var pull = require('pull-stream')
6 +var toPull = require('stream-to-pull-stream')
7 +var File = require('pull-file')
8 +var explain = require('explain-error')
9 +var ssbKeys = require('ssb-keys')
10 +var stringify = require('pull-stringify')
11 +var createHash = require('multiblob/util').createHash
12 +var minimist = require('minimist')
13 +var muxrpcli = require('muxrpcli')
14 +var cmdAliases = require('minsbot/lib/cli-cmd-aliases')
15 +var ProgressBar = require('minsbot/lib/progress')
16 +var packageJson = require('minsbot/package.json')
17 +
18 +var open = require('opn')
19 +
20 +//get config as cli options after --, options before that are
21 +//options to the command.
22 +var argv = process.argv.slice(2)
23 +var i = argv.indexOf('--')
24 +var conf = argv.slice(i+1)
25 +argv = ~i ? argv.slice(0, i) : argv
26 +
27 +var network = 'ssb'
28 +//var network = 'decent'
29 +//var network = 'testnet'
30 +
31 +var config = require('minsbot/config/inject')(network)
32 +
33 +keys = ssbKeys.loadOrCreateSync(path.join(config.path, 'secret'))
34 +
35 +var liteClient = fs.readFileSync(path.join('./build/index.html'))
36 +
37 +var manifestFile = path.join(config.path, 'manifest.json')
38 +
39 +if (argv[0] == 'server') {
40 + console.log('my key ID:', keys.public)
41 +
42 + // special server command:
43 + // import sbot and start the server
44 +
45 + var createSbot = require('minsbot')
46 + .use(require('minsbot/plugins/plugins'))
47 + .use(require('minsbot/plugins/master'))
48 + .use(require('minsbot/plugins/gossip'))
49 + .use(require('minsbot/plugins/replicate'))
50 + .use(require('ssb-friends'))
51 + .use(require('ssb-blobs'))
52 + .use(require('minsbot/plugins/invite'))
53 + .use(require('minsbot/plugins/local'))
54 + .use(require('minsbot/plugins/logging'))
55 + .use(require('minsbot/query'))
56 + .use(require('ssb-links'))
57 + .use(require('ssb-backlinks'))
58 + .use(require('minsbot/decent-ws'))
59 + .use(require('ssb-ebt'))
60 + .use({
61 + name: 'serve',
62 + version: '1.0.0',
63 + init: function (sbot) {
64 + sbot.ws.use(function (req, res, next) {
65 + //res.setHeader('Access-Control-Allow-Origin', '*')
66 + var send = config
67 + delete send.keys // very important to keep this, as it removes the server keys from the config before broadcast
68 + send.address = sbot.ws.getAddress()
69 + sbot.invite.create({modern: true}, function (err, cb) {
70 + send.invite = cb
71 + })
72 + if(req.url == '/')
73 + res.end(liteClient)
74 + if(req.url == '/get-config')
75 + res.end(JSON.stringify(send))
76 + else next()
77 + })
78 + }
79 + })
80 +
81 + open('http://localhost:' + config.ws.port, {wait: false})
82 +
83 +
84 + // add third-party plugins
85 + //require('./plugins/plugins').loadUserPlugins(createSbot, config)
86 +
87 + // start server
88 +
89 + config.keys = keys
90 + var server = createSbot(config)
91 +
92 + // write RPC manifest to ~/.ssb/manifest.json
93 + fs.writeFileSync(manifestFile, JSON.stringify(server.getManifest(), null, 2))
94 +
95 +
96 + ProgressBar(server.progress)
97 +
98 +} else {
99 +
100 + // normal command:
101 + // create a client connection to the server
102 +
103 + // read manifest.json
104 + var manifest
105 + try {
106 + manifest = JSON.parse(fs.readFileSync(manifestFile))
107 + } catch (err) {
108 + throw explain(err,
109 + 'no manifest file'
110 + + '- should be generated first time server is run'
111 + )
112 + }
113 +
114 + // connect
115 + require('ssb-client')(keys, {
116 + manifest: manifest,
117 + port: config.port,
118 + host: config.host||'localhost',
119 + caps: config.caps,
120 + key: config.key || keys.id
121 + }, function (err, rpc) {
122 + if(err) {
123 + if (/could not connect/.test(err.message)) {
124 + console.log('Error: Could not connect to the scuttlebot server.')
125 + console.log('Use the "server" command to start it.')
126 + if(config.verbose) throw err
127 + process.exit(1)
128 + }
129 + throw err
130 + }
131 +
132 + // add aliases
133 + for (var k in cmdAliases) {
134 + rpc[k] = rpc[cmdAliases[k]]
135 + manifest[k] = manifest[cmdAliases[k]]
136 + }
137 +
138 + // add some extra commands
139 + manifest.version = 'async'
140 + manifest.config = 'sync'
141 + rpc.version = function (cb) {
142 + console.log(require('./package.json').version)
143 + cb()
144 + }
145 + rpc.config = function (cb) {
146 + console.log(JSON.stringify(config, null, 2))
147 + cb()
148 + }
149 +
150 + // HACK
151 + // we need to output the hash of blobs that are added via blobs.add
152 + // because muxrpc doesnt support the `sink` callback yet, we need this manual override
153 + // -prf
154 + if (process.argv[2] === 'blobs.add') {
155 + var filename = process.argv[3]
156 + var source =
157 + filename ? File(process.argv[3])
158 + : !process.stdin.isTTY ? toPull.source(process.stdin)
159 + : (function () {
160 + console.error('USAGE:')
161 + console.error(' blobs.add <filename> # add a file')
162 + console.error(' source | blobs.add # read from stdin')
163 + process.exit(1)
164 + })()
165 + var hasher = createHash('sha256')
166 + pull(
167 + source,
168 + hasher,
169 + rpc.blobs.add(function (err) {
170 + if (err)
171 + throw err
172 + console.log('&'+hasher.digest)
173 + process.exit()
174 + })
175 + )
176 + return
177 + }
178 +
179 + // run commandline flow
180 + muxrpcli(argv, manifest, rpc, config.verbose)
181 + })
182 +}
183 +
184 +
185 +
186 +
compose.jsView
@@ -1,0 +1,187 @@
1 +var h = require('hyperscript')
2 +var pull = require('pull-stream')
3 +var sbot = require('./scuttlebot')
4 +var human = require('human-time')
5 +var id = require('./keys').id
6 +var mentions = require('ssb-mentions')
7 +
8 +var avatar = require('./avatar')
9 +var tools = require('./tools')
10 +
11 +var mime = require('simple-mime')('application/octect-stream')
12 +var split = require('split-buffer')
13 +
14 +var route = require('./views')
15 +
16 +function file_input (onAdded) {
17 + return h('label.btn', 'Upload file',
18 + h('input', { type: 'file', hidden: true,
19 + onchange: function (ev) {
20 + var file = ev.target.files[0]
21 + if (!file) return
22 + var reader = new FileReader()
23 + reader.onload = function () {
24 + pull(
25 + pull.values(split(new Buffer(reader.result), 64*1024)),
26 + sbot.addblob(function (err, blob) {
27 + if(err) return console.error(err)
28 + onAdded({
29 + link: blob,
30 + name: file.name,
31 + size: reader.result.length || reader.result.byteLength,
32 + type: mime(file.name)
33 + })
34 + })
35 + )
36 + }
37 + reader.readAsArrayBuffer(file)
38 + }
39 + }))
40 +}
41 +
42 +module.exports = function (opts, fallback) {
43 + var files = []
44 + var filesById = {}
45 +
46 + var composer = h('div.composer')
47 + var container = h('div.container')
48 + if (opts.boostAuthor) {
49 + var boostName = avatar.cachedName(opts.boostAuthor)
50 + }
51 + if (opts.boostContent) {
52 + var textarea = h('textarea.compose')
53 + var str = opts.boostContent
54 + var lines = str.split("\n")
55 + for(var i=0; i<lines.length; i++) {
56 + lines[i] = "> " + lines[i]
57 + }
58 + var newContent = lines.join("\n")
59 + var content = 'Boosting: ' + opts.boostKey + '\n\n' + newContent + ' - [' + boostName.textContent + ']('+ opts.boostAuthor + ')'
60 + textarea.value = content
61 + }
62 +
63 + else if (opts.mentions) {
64 + var textarea = h('textarea.compose', opts.mentions)
65 + }
66 +
67 + else if (opts.type == 'wiki')
68 + var textarea = h('textarea.compose', {placeholder: opts.placeholder || 'Write a wiki (anyone can edit)'})
69 + else if (opts.type == 'post')
70 + var textarea = h('textarea.compose', {placeholder: opts.placeholder || 'Write a message (only you can edit)'})
71 + else
72 + var textarea = h('textarea.compose', {placeholder: opts.placeholder || 'Write a message (only you can edit)'}, fallback.messageText)
73 +
74 + var cancelBtn = h('button.btn', 'Cancel', {
75 + onclick: function () {
76 + var cancel
77 + console.log(opts)
78 +
79 + if (opts.type == 'edit') {
80 + cancel = document.getElementById('edit:' + opts.branch.substring(0,44))
81 + var oldMessage = h('div.message__body', tools.markdown(fallback.messageText))
82 + cancel.parentNode.replaceChild(oldMessage, cancel)
83 + oldMessage.parentNode.appendChild(fallback.buttons)
84 + } else if (opts.branch) {
85 + //cancel reply composer
86 + cancel = document.getElementById('re:' + opts.branch.substring(0,44))
87 + cancel.parentNode.removeChild(cancel)
88 + message = document.getElementById(opts.branch.substring(0,44))
89 + message.appendChild(fallback.buttons)
90 + } else {
91 + // cancel generic composer
92 + cancel = document.getElementById('composer')
93 + cancel.parentNode.removeChild(cancel)
94 + }
95 + }
96 +
97 + })
98 +
99 + var initialButtons = h('span',
100 + h('button.btn', 'Preview', {
101 + onclick: function () {
102 + if (textarea.value) {
103 + var msg = {}
104 +
105 + msg.value = {
106 + "author": id,
107 + "content": opts
108 + }
109 +
110 + msg.value.content.text = textarea.value
111 + msg.value.content.mentions = mentions(textarea.value).map(
112 + function (mention) {
113 + var file = filesById[mention.link]
114 + if (file) {
115 + if (file.type) mention.type = file.type
116 + if (file.size) mention.size = file.size
117 + }
118 + return mention
119 + }
120 + )
121 +
122 + if (opts.recps)
123 + msg.value.private = true
124 +
125 + console.log(msg)
126 + if (opts.type == 'post' || opts.type == 'wiki')
127 + var header = tools.header(msg)
128 + if (opts.type == 'update')
129 + var header = tools.timestamp(msg, {edited: true})
130 + var preview = h('div',
131 + header,
132 + h('div.message__content', tools.markdown(msg.value.content.text)),
133 + h('button.btn', 'Publish', {
134 + onclick: function () {
135 + if (msg.value.content) {
136 + sbot.publish(msg.value.content, function (err, msg) {
137 + if(err) throw err
138 + console.log('Published!', msg)
139 + if (opts.type == 'edit') {
140 + var message = document.getElementById(opts.branch.substring(0,44))
141 + fallback.messageText = msg.value.content.text
142 + var editBody = h('div.message__body',
143 + tools.timestamp(msg, {edited: true}),
144 + h('div', tools.markdown(msg.value.content.text))
145 + )
146 +
147 + message.replaceChild(editBody, message.childNodes[message.childNodes.length - 1])
148 + editBody.parentNode.appendChild(fallback.buttons)
149 + } else {
150 + if (opts.branch)
151 + cancel = document.getElementById('re:' + opts.branch.substring(0,44))
152 + else
153 + cancel = document.getElementById('composer')
154 + cancel.parentNode.removeChild(cancel)
155 + }
156 + })
157 + }
158 + }
159 + }),
160 + h('button.btn', 'Cancel', {
161 + onclick: function () {
162 + composer.replaceChild(container, composer.firstChild)
163 + container.appendChild(textarea)
164 + container.appendChild(initialButtons)
165 + }
166 + })
167 + )
168 + composer.replaceChild(preview, composer.firstChild)
169 + }
170 + }
171 + }),
172 + file_input(function (file) {
173 + files.push(file)
174 + filesById[file.link] = file
175 + var embed = file.type.indexOf('image/') === 0 ? '!' : ''
176 + textarea.value += embed + '['+file.name+']('+file.link+')'
177 + }),
178 + cancelBtn
179 + )
180 +
181 + composer.appendChild(container)
182 + container.appendChild(textarea)
183 + container.appendChild(initialButtons)
184 +
185 + return composer
186 +}
187 +
config.jsView
@@ -1,0 +1,36 @@
1 +var http = require('http')
2 +
3 +module.exports = function () {
4 + //var host = window.location.origin
5 +
6 + var host = 'http://localhost:8989'
7 +
8 + function getConfig () {
9 + http.get(host + '/get-config', function (res) {
10 + res.on('data', function (data, remote) {
11 + var config = data
12 + localStorage[host] = config
13 + })
14 + })
15 + }
16 +
17 + if (localStorage[host]) {
18 + var config = JSON.parse(localStorage[host])
19 + getConfig()
20 + } else {
21 + getConfig()
22 + setTimeout(function () {
23 + location.reload()
24 + }, 1000)
25 + }
26 +
27 + config.blobsUrl = host + '/blobs/get/'
28 + config.emojiUrl = host + '/img/emoji/'
29 +
30 + if (config.ws.remote)
31 + config.remote = config.ws.remote
32 + else
33 + config.remote = config.address
34 +
35 + return config
36 +}
index.jsView
@@ -1,0 +1,54 @@
1 +var h = require('hyperscript')
2 +var route = require('./views')
3 +var avatar = require('./avatar')
4 +var compose = require('./compose')
5 +var id = require('./keys').id
6 +
7 +// append global stylesheet to <head> (built using style.js)
8 +document.head.appendChild(h('style', require('./style.css.json')))
9 +
10 +// #screen is the element everything else gets appended.
11 +var screen = h('div#screen')
12 +
13 +// navbar is appended to the top of the document (outside of screen)
14 +var nav = h('div.navbar',
15 + h('div.internal',
16 + h('li', h('a', {href: '#' + id}, h('span.avatar--small', avatar.image(id)))),
17 + h('li', h('a', {href: '#' + id}, avatar.name(id))),
18 + h('li', h('a', 'New Post', {
19 + onclick: function () {
20 + if (document.getElementById('composer')) { return }
21 + else {
22 + var currentScreen = document.getElementById('screen')
23 + var opts = {}
24 + opts.type = 'post'
25 + var composer = h('div.content#composer', h('div.message', compose(opts)))
26 + if (currentScreen.firstChild.firstChild) {
27 + currentScreen.firstChild.insertBefore(composer, currentScreen.firstChild.firstChild)
28 + } else {
29 + currentScreen.firstChild.appendChild(composer)
30 + }
31 + }
32 + }
33 + })),
34 + h('li', h('a', {href: '#' }, 'All')),
35 + h('li', h('a', {href: '#wall/' + id }, 'Wall')),
36 + h('li', h('a', {href: '#key' }, 'Key'))
37 + )
38 +)
39 +
40 +//append those two elements
41 +document.body.appendChild(nav)
42 +document.body.appendChild(screen)
43 +
44 +// call the router
45 +route()
46 +
47 +// this code clears the #screen element when the #hash changes
48 +window.onhashchange = function () {
49 + var oldscreen = document.getElementById('screen')
50 + var newscreen = h('div#screen')
51 + oldscreen.parentNode.replaceChild(newscreen, oldscreen)
52 + route()
53 +}
54 +
keys.jsView
@@ -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.jsonView
@@ -1,0 +1,86 @@
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 + "names": {
47 + "get": "async",
48 + "getImages": "async",
49 + "getImageFor": "async",
50 + "getSignifier": "async",
51 + "getSignifies": "async",
52 + "dump": "sync"
53 + },
54 + "private": {
55 + "publish": "async",
56 + "unbox": "sync"
57 + },
58 + "blobs": {
59 + "get": "source",
60 + "add": "sink",
61 + "ls": "source",
62 + "has": "async",
63 + "size": "async",
64 + "meta": "async",
65 + "want": "async",
66 + "push": "async",
67 + "changes": "source",
68 + "createWants": "source"
69 + },
70 + "links2": {
71 + "read": "source",
72 + "dump": "source"
73 + },
74 + "backlinks": {
75 + "read": "source",
76 + "dump": "source"
77 + },
78 + "query": {
79 + "read": "source",
80 + "dump": "source"
81 + },
82 + "ws": {},
83 + "search": {
84 + "query": "source"
85 + }
86 +}
package-lock.jsonView
The diff is too large to show. Use a local git client to view these changes.
Old file size: 0 bytes
New file size: 242276 bytes
package.jsonView
@@ -1,0 +1,57 @@
1 +{
2 + "name": "0qc",
3 + "version": "1.0.0",
4 + "description": "one query client (experimental)",
5 + "main": "index.js",
6 + "scripts": {
7 + "start": "node bin server",
8 + "build": "node style.js && mkdir -p build && browserify index.js | indexhtmlify > build/index.html"
9 + },
10 + "devDependencies": {
11 + "browserify": "^16.2.2",
12 + "indexhtmlify": "^1.3.1"
13 + },
14 + "author": "Ev Bogue <ev@evbogue.com>",
15 + "license": "MIT",
16 + "dependencies": {
17 + "dataurl-": "^0.1.0",
18 + "decent-ws": "1.0.4",
19 + "deep-extend": "^0.6.0",
20 + "diff": "^3.5.0",
21 + "emoji-server": "^1.0.0",
22 + "human-time": "0.0.1",
23 + "hyperfile": "^2.0.0",
24 + "hyperloadmore": "^1.1.0",
25 + "hyperscript": "^2.0.2",
26 + "hyperscroll": "^1.0.0",
27 + "minsbot": "^1.0.0",
28 + "multiblob-http": "^0.4.2",
29 + "muxrpcli": "^1.1.0",
30 + "non-private-ip": "^1.4.3",
31 + "opn": "^5.3.0",
32 + "os-homedir": "^1.0.2",
33 + "pull-more": "^1.1.0",
34 + "pull-next-query": "^1.0.0",
35 + "pull-reconnect": "0.0.3",
36 + "pull-stream": "^3.6.8",
37 + "pull-stringify": "^2.0.0",
38 + "rc": "^1.2.7",
39 + "simple-mime": "^0.1.0",
40 + "split-buffer": "^1.0.0",
41 + "ssb-avatar": "^0.2.0",
42 + "ssb-backlinks": "^0.7.1",
43 + "ssb-blobs": "^1.1.5",
44 + "ssb-client": "^4.5.7",
45 + "ssb-ebt": "^5.1.5",
46 + "ssb-feed": "^2.3.0",
47 + "ssb-friends": "^2.4.0",
48 + "ssb-keys": "^7.0.16",
49 + "ssb-links": "^3.0.3",
50 + "ssb-markdown": "^3.6.0",
51 + "ssb-mentions": "^0.5.0",
52 + "ssb-ref": "^2.11.1",
53 + "ssb-search": "^1.0.1",
54 + "stack": "^0.1.0",
55 + "visualize-buffer": "0.0.1"
56 + }
57 +}
readme.mdView
@@ -1,0 +1,30 @@
1 +# one query client (0qc)
2 +
3 +so I woke up one morning and it occurred to me that I could render all information linking to a message using a single `sbot.backlinks` query.
4 +
5 +In [mvd](%NPNNvcnTMZUFZSWl/2Z4XX+YSdqsqOhyPacp+lgpQUw=.sha256) I'd been using a few queries per message to grab different things, and each query added additional complexity. Why not simplify by doing one query and then updating the message object based on what was returned?
6 +
7 +This is an experimental client that is a fork, and simplify of [mvd](%NPNNvcnTMZUFZSWl/2Z4XX+YSdqsqOhyPacp+lgpQUw=.sha256)
8 +
9 +To use it grab `minsbot` from `git-ssb`
10 +
11 +```
12 +git clone ssb://%bEpNCZyU/f+Hi0Jhk6C9SQoNorT9NMyJen2NTCS0vaU=.sha256 minsbot
13 +cd minsbot
14 +npm install
15 +npm start
16 +```
17 +
18 +Then navigate to another terminal and clone down `0qc`
19 +
20 +```
21 +git clone
22 +npm install
23 +npm run build
24 +npm start
25 +```
26 +
27 +and it should 'just work' because `0qc` will grab its sbot config from `http://localhost:8989` which is your `minsbot` server's websocket. If your `minsbot` server is located elsewhere, edit the hostname in `config` to target your sbot server. Ex: `http://evbogue.com:8989`.
28 +
29 +---
30 +MIT
render.jsView
@@ -1,0 +1,235 @@
1 +var h = require('hyperscript')
2 +var pull = require('pull-stream')
3 +var human = require('human-time')
4 +
5 +var sbot = require('./scuttlebot')
6 +var composer = require('./compose')
7 +var tools = require('./tools')
8 +
9 +var config = require('./config')()
10 +var id = require('./keys').id
11 +var avatar = require('./avatar')
12 +var ssbAvatar = require('ssb-avatar')
13 +
14 +var ssbKeys = require('ssb-keys')
15 +var keys = require('./keys')
16 +
17 +var diff = require('diff')
18 +
19 +function hash () {
20 + return window.location.hash.substring(1)
21 +}
22 +
23 +module.exports.render = function (msg) {
24 +
25 + var message = h('div.message#' + msg.key.substring(0, 44))
26 +
27 + var messageContent = h('div.messageContent')
28 + message.appendChild(messageContent)
29 +
30 + pull(
31 + sbot.backlinks({
32 + query: [{$filter: {dest: msg.key}}],
33 + live: true,
34 + reverse: false,
35 + index: 'DTA'
36 + }),
37 + pull.drain(function (data) {
38 + if (data.sync) {
39 + console.log('waiting')
40 + } else {
41 + var dataId = 'NOTNULL'
42 + var dataId = document.getElementById(data.key.substring(0, 44))
43 +
44 + if (dataId == null) {
45 + console.log('rendering message')
46 + if (
47 + (data.value.content.vote) ||
48 + (msg.key == data.value.content.root) ||
49 + (msg.key == data.value.content.repo) ||
50 + (msg.key == data.value.content.link)
51 + ) {
52 + message.appendChild(h('div.submessage', (exports.render(data))))
53 + } else if (!data.value.content.root) {
54 + var link = h('span', ' backlinked from ', tools.messageLink(data.key))
55 + message.appendChild(h('div.submessage', h('div.messageContent', tools.mini(data, link))))
56 + } else {
57 + //console.log('DOES NOT MATCH')
58 + message.appendChild(h('div.submessage', (exports.render(data))))
59 + }
60 + } else { console.log('message already rendered')}
61 + }
62 + })
63 + )
64 +
65 + if (msg.value.content.type == 'post') {
66 +
67 + messageContent.appendChild(tools.header(msg))
68 +
69 + if (msg.value.content.root) {
70 + messageContent.appendChild(h('span', 're: ', tools.messageLink(msg.value.content.root)))
71 + }
72 +
73 + messageContent.appendChild(h('div.message__body', tools.markdown(msg.value.content.text)))
74 + return message
75 + }
76 +
77 + if (msg.value.content.type == 'vote') {
78 + if (msg.value.content.vote.value == 1)
79 + var link = h('span', ' ', h('img.emoji', {src: config.emojiUrl + 'star.png'}), ' ', h('a', {href: '#' + msg.value.content.vote.link}, tools.messageLink(msg.value.content.vote.link)))
80 + else if (msg.value.content.vote.value == -1)
81 + var link = h('span', ' ', h('img.emoji', {src: config.emojiUrl + 'stars.png'}), ' ', h('a', {href: '#' + msg.value.content.vote.link}, tools.messageLink(msg.value.content.vote.link)))
82 + messageContent.appendChild(tools.mini(msg, link))
83 + return message
84 + }
85 +
86 + if (msg.value.content.type == 'about') {
87 + if (msg.value.content.image) {
88 + var image = h('span.avatar--small',
89 + ' identified ',
90 + h('a', {href: '#' + msg.value.content.about}, avatar.cachedName(msg.value.content.about)),
91 + ' as ',
92 + h('img', {src: config.blobsUrl + msg.value.content.image.link})
93 + )
94 + messageContent.appendChild(tools.mini(msg, image))
95 + return message
96 + }
97 + if (msg.value.content.name) {
98 + var name = h('span',
99 + ' identified ',
100 + h('a', {href: '#' + msg.value.content.about}, avatar.cachedName(msg.value.content.about)),
101 + ' as ', msg.value.content.name
102 + )
103 + messageContent.appendChild(tools.mini(msg, name))
104 + return message
105 + }
106 + }
107 +
108 + if (msg.value.content.type == 'label'){
109 + var content = h('span', ' labeled ', tools.messageLink(msg.value.content.link), ' as ', h('mark', h('a', {href: '#label/' + msg.value.content.label}, msg.value.content.label)))
110 + messageContent.appendChild(tools.mini(msg, content))
111 + return message
112 + }
113 +
114 + if (msg.value.content.type == 'queue') {
115 + if (msg.value.content.queue == true) {
116 + var content = h('span', ' added ', tools.messageLink(msg.value.content.message), ' to their ', h('a', {href: '#queue'}, 'queue'))
117 + messageContent.appendChild(tools.mini(msg, content))
118 + }
119 + if (msg.value.content.queue == false) {
120 + var content = h('span', ' removed ', tools.messageLink(msg.value.content.message), ' from their ', h('a', {href: '#queue'}, 'queue'))
121 + messageContent.appendChild(tools.mini(msg, content))
122 +
123 + }
124 + return message
125 + }
126 +
127 + if (msg.value.content.type == 'scat_message') {
128 + var src = hash()
129 + if (src != 'backchannel') {
130 + messageContent.appendChild(h('button.btn.right', h('a', {href: '#backchannel'}, 'Chat')))
131 + }
132 + messageContent.appendChild(tools.mini(msg, ' ' + msg.value.content.text))
133 + return message
134 + }
135 +
136 + else if (msg.value.content.type == 'contact') {
137 + if (msg.value.content.contact) {
138 + var contact = h('a', {href: '#' + msg.value.content.contact}, avatar.name(msg.value.content.contact))
139 + } else { var contact = h('p', 'no contact named')}
140 +
141 + if (msg.value.content.following == true) {
142 + var following = h('span', ' follows ', contact)
143 + messageContent.appendChild(tools.mini(msg, following))
144 + }
145 + if (msg.value.content.following == false) {
146 + var unfollowing = h('span', ' unfollows ', contact)
147 + messageContent.appendChild(tools.mini(msg, unfollowing))
148 + }
149 + if (msg.value.content.blocking == true) {
150 + var blocking = h('span', ' blocks ', contact)
151 + messageContent.appendChild(tools.mini(msg, blocking))
152 + }
153 + if (msg.value.content.blocking == false) {
154 + var unblocking = h('span', ' unblocks ', contact)
155 + messageContent.appendChild(tools.mini(msg, unblocking))
156 + }
157 + return message
158 +
159 + }
160 +
161 +
162 + if (msg.value.content.type == 'git-update') {
163 +
164 + messageContent.appendChild(tools.header(msg))
165 +
166 + var reponame = h('p', 'pushed to ', h('a', {href: '#' + msg.value.content.repo}, msg.value.content.repo))
167 +
168 + var cloneurl = h('pre', 'git clone ssb://' + msg.value.content.repo)
169 +
170 + messageContent.appendChild(reponame)
171 +
172 + ssbAvatar(sbot, id, msg.value.content.repo, function (err, data) {
173 + if (data) {
174 + var actualname = h('p', 'pushed to ', h('a', {href: '#' + msg.value.content.repo}, '%' + data.name))
175 + reponame.parentNode.replaceChild(actualname, reponame)
176 + }
177 + })
178 +
179 + messageContent.appendChild(cloneurl)
180 +
181 + var commits = h('ul')
182 + if (msg.value.content.commits) {
183 + msg.value.content.commits.map(function (commit) {
184 + commits.appendChild(h('li', h('code', commit.sha1), ' - ', commit.title))
185 + })
186 + }
187 +
188 + messageContent.appendChild(commits)
189 +
190 + return message
191 + }
192 +
193 + if (msg.value.content.type == 'git-repo') {
194 + messageContent.appendChild(tools.header(msg))
195 +
196 + var reponame = h('p', 'git-ssb repo ', h('a', {href: '#' + msg.key}, msg.key))
197 +
198 + messageContent.appendChild(reponame)
199 +
200 + ssbAvatar(sbot, id, msg.key, function (err, data) {
201 + if (data)
202 + var actualname = h('p', 'git-ssb repo ', h('a', {href: '#' + msg.key}, '%' + data.name))
203 + reponame.parentNode.replaceChild(actualname, reponame)
204 + })
205 +
206 + var cloneurl = h('pre', 'git clone ssb://' + msg.key)
207 + messageContent.appendChild(cloneurl)
208 + return message
209 + }
210 +
211 + if (typeof msg.value.content === 'string') {
212 + var unboxed = ssbKeys.unbox(msg.value.content, keys)
213 + if (unboxed) {
214 + msg.value.content = unboxed
215 + msg.value.private = true
216 + return module.exports(msg)
217 + } else {
218 + var privateMsg = h('span', ' sent a private message.')
219 + messageContent.appendChild(tools.mini(msg, privateMsg))
220 + return message
221 + }
222 + }
223 +
224 + else {
225 + //FULL FALLBACK
226 + //message.appendChild(tools.header(msg))
227 + messageContent.appendChild(tools.header(msg))
228 + messageContent.appendChild(h('pre', tools.rawJSON(msg.value)))
229 +
230 + //MINI FALLBACK
231 + //var fallback = h('span', ' ' + msg.value.content.type)
232 + //message.appendChild(tools.mini(msg, fallback))
233 + return message
234 + }
235 +}
scuttlebot.jsView
@@ -1,0 +1,86 @@
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 +var config = require('./config')()
6 +var createClient = require('ssb-client')
7 +var createFeed = require('ssb-feed')
8 +var keys = require('./keys')
9 +
10 +var CACHE = {}
11 +
12 +var rec = reconnect(function (isConn) {
13 + function notify (value) {
14 + isConn(value)
15 + }
16 +
17 + createClient(keys, {
18 + manifest: require('./manifest.json'),
19 + remote: config.remote,
20 + caps: config.caps
21 + }, function (err, _sbot) {
22 + if(err)
23 + return notify(err)
24 +
25 + sbot = _sbot
26 + sbot.on('closed', function () {
27 + sbot = null
28 + notify(new Error('closed'))
29 + })
30 +
31 + notify()
32 + })
33 +})
34 +
35 +var internal = {
36 + getLatest: rec.async(function (id, cb) {
37 + sbot.getLatest(id, cb)
38 + }),
39 + add: rec.async(function (msg, cb) {
40 + sbot.add(msg, cb)
41 + })
42 +}
43 +
44 +var feed = createFeed(internal, keys, {remote: true})
45 +
46 +module.exports = {
47 + backlinks: rec.source(function (query) {
48 + return sbot.backlinks.read(query)
49 + }),
50 + links: rec.source(function (query) {
51 + return sbot.links(query)
52 + }),
53 + query: rec.source(function (query) {
54 + return sbot.query.read(query)
55 + }),
56 + get: rec.async(function (key, cb) {
57 + if(CACHE[key]) cb(null, CACHE[key])
58 + else sbot.get(key, function (err, value) {
59 + if(err) return cb(err)
60 + cb(null, CACHE[key] = value)
61 + })
62 + }),
63 + addblob: rec.sink(function (cb) {
64 + return sbot.blobs.add(cb)
65 + }),
66 + publish: rec.async(function (content, cb) {
67 + if(content.recps)
68 + content = ssbKeys.box(content, content.recps.map(function (e) {
69 + return ref.isFeed(e) ? e : e.link
70 + }))
71 + else if(content.mentions)
72 + content.mentions.forEach(function (mention) {
73 + if(ref.isBlob(mention.link)) {
74 + sbot.blobs.push(mention.link, function (err) {
75 + if(err) console.error(err)
76 + })
77 + }
78 + })
79 + feed.add(content, function (err, msg) {
80 + if(err) console.error(err)
81 + else if(!cb) console.log(msg)
82 + cb && cb(err, msg)
83 + })
84 + })
85 +}
86 +
style.cssView
@@ -1,0 +1,177 @@
1 +body {
2 + margin: 0;
3 + background: black;
4 + font-family: sans-serif;
5 + color: #f5f5f5;
6 + font-size: 14px;
7 + line-height: 20px;
8 +}
9 +
10 +#screen {
11 + position: absolute;
12 + top: 35px;
13 + bottom: 0px;
14 + left: 0px;
15 + right: 0px;
16 +}
17 +
18 +.hyperscroll {
19 + width: 100%;
20 +}
21 +
22 +.header {
23 + padding-bottom: .7em;
24 + border-bottom: 1px solid #252525;
25 +}
26 +
27 +h1, h2, h3, h4, h5, h6 {
28 + font-size: 1.2em;
29 + margin-top: .35ex;
30 +}
31 +
32 +hr {
33 + border: solid #222;
34 + clear: both;
35 + border-width: 1px 0 0;
36 + height: 0;
37 + margin-bottom: .9em;
38 +}
39 +
40 +p {
41 + margin-top: .35ex;
42 + margin-bottom: 10px;
43 +}
44 +
45 +a {
46 + color: cyan;
47 + text-decoration: none;
48 +}
49 +
50 +a:hover, a:focus {
51 + color: violet;
52 + text-decoration: underline;
53 +}
54 +
55 +.navbar {
56 + background: #1b1b1b;
57 + *background: linear-gradient(#222, #111);
58 + border-bottom: 1px solid #252525;
59 +}
60 +
61 +.navbar {
62 + width: 100%;
63 + position: fixed;
64 + z-index: 1000;
65 + margin: 0;
66 + padding-top: .3em;
67 + padding-bottom: .3em;
68 + left: 0; right: 0;
69 + top: 0;
70 +}
71 +
72 +.navbar .internal {
73 + max-width: 97%;
74 + margin-left: auto;
75 + margin-right: auto;
76 +}
77 +
78 +.navbar li {
79 + margin-top: .3em;
80 + float: left;
81 + margin-right: .6em;
82 + margin-left: .3em;
83 + list-style-type: none;
84 +}
85 +
86 +.navbar li.right {
87 + padding-left: .4em;
88 + padding-right: .4em;
89 + margin-top: .3em;
90 + margin-right: 1.7em;
91 + float: right;
92 + list-style-type: none;
93 + background: #333;
94 + border-radius: 100%;
95 +}
96 +
97 +.content {
98 + max-width: 760px;
99 + margin-left: auto;
100 + margin-right: auto;
101 +}
102 +
103 +.message, .message > *, .navbar, .navbar > * {
104 + animation: fadein .5s;
105 +}
106 +
107 +@keyframes fadein {
108 + from { opacity: 0; }
109 + to { opacity: 1; }
110 +}
111 +
112 +.messageContent {
113 + display: block;
114 + margin: .6em;
115 + background: #111;
116 + padding: .7em;
117 + border-radius: 3px;
118 + border: 1px solid #252525;
119 +}
120 +
121 +.submessage {
122 + margin-left: 2em;
123 +}
124 +
125 +.message img, .message video {
126 + max-width: 100%;
127 +}
128 +
129 +.avatar--small img {
130 + vertical-align: top;
131 + width: 1.4em;
132 + height: 1.4em;
133 + margin-right: .2em;
134 +}
135 +
136 +.avatar--medium img {
137 + float: left;
138 + vertical-align: top;
139 + width: 5em;
140 + height: 5em;
141 + margin-right: .5em;
142 + margin-bottom: .5em;
143 +}
144 +
145 +.right {
146 + float: right;
147 + margin-right: .25em;
148 +}
149 +
150 +.emoji {
151 + padding: .2em;
152 + *float: left;
153 + width: 1em;
154 + vertical-align: top;
155 +}
156 +
157 +pre {
158 + width: 100%;
159 + display: block;
160 +}
161 +
162 +code {
163 + display: inline-block;
164 + vertical-align: bottom;
165 +}
166 +
167 +code, pre {
168 + overflow: auto;
169 + word-break: break-all;
170 + word-wrap: break-word;
171 + white-space: pre;
172 + white-space: -moz-pre-wrap;
173 + white-space: pre-wrap;
174 + white-space: pre\9;
175 + color: violet;
176 +}
177 +
style.css.jsonView
@@ -1,0 +1,1 @@
1 +"body {\n margin: 0;\n background: black;\n font-family: sans-serif;\n color: #f5f5f5;\n font-size: 14px; \n line-height: 20px;\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 #252525;\n}\n\nh1, h2, h3, h4, h5, h6 {\n font-size: 1.2em;\n margin-top: .35ex;\n}\n\nhr {\n border: solid #222;\n clear: both;\n border-width: 1px 0 0;\n height: 0;\n margin-bottom: .9em;\n}\n\np {\n margin-top: .35ex;\n margin-bottom: 10px;\n}\n\na {\n color: cyan;\n text-decoration: none;\n}\n\na:hover, a:focus {\n color: violet;\n text-decoration: underline;\n}\n\n.navbar {\n background: #1b1b1b;\n *background: linear-gradient(#222, #111);\n border-bottom: 1px solid #252525;\n}\n\n.navbar {\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.navbar li.right {\n padding-left: .4em;\n padding-right: .4em;\n margin-top: .3em;\n margin-right: 1.7em;\n float: right;\n list-style-type: none;\n background: #333;\n border-radius: 100%;\n}\n\n.content {\n max-width: 760px;\n margin-left: auto;\n margin-right: auto;\n}\n\n.message, .message > *, .navbar, .navbar > * {\n animation: fadein .5s;\n}\n\n@keyframes fadein {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.messageContent {\n display: block;\n margin: .6em;\n background: #111;\n padding: .7em;\n border-radius: 3px;\n border: 1px solid #252525;\n}\n\n.submessage {\n margin-left: 2em;\n}\n\n.message img, .message video {\n max-width: 100%;\n}\n\n.avatar--small img {\n vertical-align: top;\n width: 1.4em;\n height: 1.4em;\n margin-right: .2em;\n}\n\n.avatar--medium img {\n float: left;\n vertical-align: top;\n width: 5em;\n height: 5em;\n margin-right: .5em;\n margin-bottom: .5em;\n}\n\n.right {\n float: right;\n margin-right: .25em;\n}\n\n.emoji {\n padding: .2em;\n *float: left;\n width: 1em;\n vertical-align: top;\n}\n\npre {\n width: 100%;\n display: block;\n}\n\ncode {\n display: inline-block;\n vertical-align: bottom;\n}\n\ncode, pre {\n overflow: auto;\n word-break: break-all;\n word-wrap: break-word;\n white-space: pre;\n white-space: -moz-pre-wrap;\n white-space: pre-wrap;\n white-space: pre\\9;\n color: violet;\n}\n\n"
style.jsView
@@ -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.jsView
@@ -1,0 +1,132 @@
1 +var h = require('hyperscript')
2 +var ref = require('ssb-ref')
3 +var ssbKeys = require('ssb-keys')
4 +var sbot = require('./scuttlebot')
5 +var config = require('./config')()
6 +var markdown = require('ssb-markdown')
7 +var id = require('./keys').id
8 +var avatar = require('./avatar')
9 +var human = require('human-time')
10 +
11 +module.exports.timestamp = function (msg, edited) {
12 + var timestamp = h('span.right', h('a', {href: '#' + msg.key}, human(new Date(msg.value.timestamp))))
13 + return timestamp
14 +}
15 +
16 +
17 +module.exports.header = function (msg) {
18 + var header = h('div.header')
19 +
20 + header.appendChild(h('span.avatar',
21 + h('a', {href: '#' + msg.value.author},
22 + h('span.avatar--small', avatar.cachedImage(msg.value.author)),
23 + avatar.cachedName(msg.value.author)
24 + )
25 + )
26 + )
27 +
28 + header.appendChild(exports.timestamp(msg))
29 +
30 + return header
31 +}
32 +
33 +module.exports.mini = function (msg, content) {
34 + var mini = h('div.mini')
35 +
36 + mini.appendChild(
37 + h('span.avatar',
38 + h('a', {href: '#' + msg.value.author},
39 + h('span.avatar--small', avatar.cachedImage(msg.value.author)),
40 + avatar.cachedName(msg.value.author)
41 + )
42 + )
43 + )
44 + var lock = h('span.right', h('img.emoji', {src: config.emojiUrl + 'lock.png'}))
45 +
46 +
47 + mini.appendChild(h('span', content))
48 + mini.appendChild(exports.timestamp(msg))
49 +
50 + if (msg.value.content.recps) {
51 + mini.appendChild(lock)
52 + }
53 +
54 + if (typeof msg.value.content === 'string') {
55 + mini.appendChild(lock)
56 + }
57 +
58 + return mini
59 +}
60 +
61 +
62 +
63 +module.exports.box = function (content) {
64 + return ssbKeys.box(content, content.recps.map(function (e) {
65 + return ref.isFeed(e) ? e : e.link
66 + }))
67 +}
68 +
69 +module.exports.publish = function (content, cb) {
70 + if(content.recps)
71 + content = exports.box(content)
72 + sbot.publish(content, function (err, msg) {
73 + if(err) throw err
74 + console.log('Published!', msg)
75 + if(cb) cb(err, msg)
76 + })
77 +}
78 +
79 +module.exports.messageName = function (id, cb) {
80 + // gets the first few characters of a message, for message-link
81 + function title (s) {
82 + var m = /^\n*([^\n]{0,40})/.exec(s)
83 + return m && (m[1].length == 40 ? m[1]+'...' : m[1])
84 + }
85 +
86 + sbot.get(id, function (err, value) {
87 + if(err && err.name == 'NotFoundError')
88 + return cb(null, id.substring(0, 10)+'...(missing)')
89 + if(value.content.type === 'post' && 'string' === typeof value.content.text)
90 + return cb(null, title(value.content.text))
91 + else if('string' === typeof value.content.text)
92 + return cb(null, value.content.type + ':'+title(value.content.text))
93 + else
94 + return cb(null, id.substring(0, 10)+'...')
95 + })
96 +}
97 +
98 +var messageName = exports.messageName
99 +
100 +module.exports.messageLink = function (id) {
101 + if (ref.isMsg(id)) {
102 + var link = h('a', {href: '#'+id}, id.substring(0, 10)+'...')
103 + messageName(id, function (err, name) {
104 + if(err) console.error(err)
105 + else link.textContent = name
106 + })
107 + } else {
108 + var link = id
109 + }
110 + return link
111 +}
112 +
113 +module.exports.rawJSON = function (obj) {
114 + return JSON.stringify(obj, null, 2)
115 + .split(/([%@&][a-zA-Z0-9\/\+]{43}=*\.[\w]+)/)
116 + .map(function (e) {
117 + if(ref.isMsg(e) || ref.isFeed(e) || ref.isBlob(e)) {
118 + return h('a', {href: '#' + e}, e)
119 + }
120 + return e
121 + })
122 +}
123 +
124 +module.exports.markdown = function (msg, md) {
125 + return {innerHTML: markdown.block(msg, {toUrl: function (url, image) {
126 + if(url[0] == '%' || url[0] == '@' || url[0] == '#') return '#' + url
127 + if(url[0] !== '&') return url
128 + //if(url[0] == '&') return config.blobsUrl + url
129 + //if(!image) return url
130 + return config.blobsUrl + url
131 + }})}
132 +}
views.jsView
@@ -1,0 +1,212 @@
1 +var pull = require('pull-stream')
2 +var sbot = require('./scuttlebot')
3 +var hyperscroll = require('hyperscroll')
4 +var stream = require('hyperloadmore/stream')
5 +var h = require('hyperscript')
6 +var ref = require('ssb-ref')
7 +var next = require('pull-next-query')
8 +
9 +var render = require('./render').render
10 +
11 +var config = require('./config')()
12 +
13 +var id = require('./keys').id
14 +
15 +var compose = require('./compose')
16 +
17 +var keyPage = function () {
18 + var screen = document.getElementById('screen')
19 +
20 + var importKey = h('textarea.import', {placeholder: 'Import a new public/private key', name: 'textarea', style: 'width: 97%; height: 100px;'})
21 +
22 + var content = h('div.content',
23 + h('div.message#key',
24 + h('h1', 'Your Key'),
25 + h('p', {innerHTML: 'Your public/private key is: <pre><code>' + localStorage[config.caps.shs + '/secret'] + '</code></pre>'},
26 + h('button.btn', {onclick: function (e){
27 + localStorage[config.caps.shs +'/secret'] = ''
28 + alert('Your public/private key has been deleted')
29 + e.preventDefault()
30 + location.hash = ""
31 + location.reload()
32 + }}, 'Delete Key')
33 + ),
34 + h('hr'),
35 + h('form',
36 + importKey,
37 + h('button.btn', {onclick: function (e){
38 + if(importKey.value) {
39 + localStorage[config.caps.shs + '/secret'] = importKey.value.replace(/\s+/g, ' ')
40 + e.preventDefault()
41 + alert('Your public/private key has been updated')
42 + }
43 + location.hash = ""
44 + location.reload()
45 + }}, 'Import key'),
46 + )
47 + )
48 + )
49 + screen.appendChild(hyperscroll(content))
50 +}
51 +
52 +function getMessage (src) {
53 +
54 + var content = h('div.content')
55 + var screen = document.getElementById('screen')
56 +
57 + screen.appendChild(hyperscroll(content))
58 +
59 + sbot.get(src, function (err, data) {
60 + if (err) {
61 + var message = h('div.message', 'Missing message!')
62 + content.appendChild(message)
63 + }
64 + if (data) {
65 + var message = {}
66 +
67 + message.value = data
68 + message.key = src
69 +
70 + if (content.firstChild) {
71 + content.insertBefore(render(message), content.firstChild)
72 + } else {
73 + content.appendChild(render(message))
74 + }
75 + }
76 + })
77 +}
78 +
79 +
80 +function userStream (src) {
81 +
82 + var screen = document.getElementById('screen')
83 + var content = h('div.content')
84 +
85 + screen.appendChild(hyperscroll(content))
86 +
87 + function createStream (opts) {
88 + return pull(
89 + next(sbot.query, opts, ['value', 'timestamp']),
90 + pull.map(function (msg) {
91 + return render(msg)
92 + })
93 + )
94 + }
95 +
96 + pull(
97 + createStream({
98 + limit: 100,
99 + reverse: true,
100 + live: false,
101 + query: [{$filter: { value: { author: src, timestamp: { $gt: 0 }}}}]
102 + }),
103 + stream.bottom(content)
104 + )
105 +
106 + pull(
107 + createStream({
108 + limit: 100,
109 + old: false,
110 + live: true,
111 + query: [{$filter: { value: {author: src, timestamp: { $gt: 0 }}}}]
112 + }),
113 + stream.top(content)
114 + )
115 +}
116 +
117 +function mentionsStream (src) {
118 +
119 + var screen = document.getElementById('screen')
120 + var content = h('div.content')
121 +
122 + screen.appendChild(hyperscroll(content))
123 +
124 + function createStream (opts) {
125 + return pull(
126 + next(sbot.backlinks, opts, ['value', 'timestamp']),
127 + pull.map(function (msg) {
128 + return render(msg)
129 + })
130 + )
131 + }
132 +
133 + pull(
134 + createStream({
135 + limit: 100,
136 + reverse: true,
137 + index: 'DTA',
138 + live: false,
139 + query: [{$filter: {dest: src}}]
140 + }),
141 + stream.bottom(content)
142 + )
143 +
144 + pull(
145 + createStream({
146 + limit: 100,
147 + old: false,
148 + index: 'DTA',
149 + live: true,
150 + query: [{$filter: {dest: src}}]
151 + }),
152 + stream.top(content)
153 + )
154 +}
155 +
156 +function everythingStream () {
157 +
158 + var screen = document.getElementById('screen')
159 + var content = h('div.content')
160 +
161 + screen.appendChild(hyperscroll(content))
162 +
163 + function createStream (opts) {
164 + return pull(
165 + next(sbot.query, opts, ['value', 'timestamp']),
166 + pull.map(function (msg) {
167 + if (msg.value) {
168 + return render(msg)
169 + }
170 + })
171 + )
172 + }
173 +
174 + pull(
175 + createStream({
176 + limit: 100,
177 + reverse: true,
178 + live: false,
179 + query: [{$filter: { value: { timestamp: { $gt: 0 }}}}]
180 + }),
181 + stream.bottom(content)
182 + )
183 +
184 + pull(
185 + createStream({
186 + limit: 100,
187 + old: false,
188 + live: true,
189 + query: [{$filter: { value: { timestamp: { $gt: 0 }}}}]
190 + }),
191 + stream.top(content)
192 + )
193 +}
194 +
195 +module.exports = function () {
196 + var src = window.location.hash.substring(1)
197 +
198 + if (ref.isFeed(src)) {
199 + userStream(src)
200 + } else if (ref.isMsg(src)) {
201 + getMessage(src)
202 + } else if ((src.substring(0, 5) == 'wall/') && (ref.isFeed(src.substring(5)))) {
203 + mentionsStream(src.substring(5))
204 + } else if (ref.isMsg(src)) {
205 + messageStream()
206 + } else if (src == 'key') {
207 + keyPage()
208 + } else {
209 + everythingStream()
210 + }
211 +
212 +}

Built with git-ssb-web