Commit c18b233c6d62741496978cd8dc1f34a093946f8b
first pass
Michael Williams committed on 2/13/2017, 12:37:05 PMParent: 8261134eb7daa7f16ad7f83ebb1549e60b37b70d
Files changed
config.js | ||
---|---|---|
@@ -1,14 +1,15 @@ | ||
1 | 1 … | const Config = require('ssb-config/inject') |
2 … | +const nest = require('depnest') | |
2 | 3 … | |
3 | -module.exports = { | |
4 | - gives: 'config', | |
5 | - create: () => { | |
4 … | +exports.gives = nest('config.sync.load') | |
5 … | +exports.create = (api) => { | |
6 … | + return nest('config.sync.load', () => { | |
6 | 7 … | var config |
7 | 8 … | return () => { |
8 | 9 … | if (!config) { |
9 | 10 … | config = Config(process.env.ssb_appname) |
10 | 11 … | } |
11 | 12 … | return config |
12 | 13 … | } |
13 | - } | |
14 … | + }) | |
14 | 15 … | } |
keys.js | ||
---|---|---|
@@ -1,18 +1,17 @@ | ||
1 | 1 … | const Path = require('path') |
2 | 2 … | const Keys = require('ssb-keys') |
3 … | +const nest = require('depnest') | |
3 | 4 … | |
4 | -module.exports = { | |
5 | - needs: { config: 'first' }, | |
6 | - gives: 'keys', | |
7 | - create: (api) => { | |
5 … | +exports.needs = nest('config.sync.load', 'first') | |
6 … | +exports.gives = nest('keys.sync.load') | |
7 … | +exports.create = (api) => { | |
8 … | + return nest('keys.sync.load', () => { | |
8 | 9 … | var keys |
9 | - return () => { | |
10 | - if (!keys) { | |
11 | - const config = api.config() | |
12 | - const keyPath = Path.join(config.path, 'secret') | |
13 | - keys = Keys.loadOrCreateSync(keyPath) | |
14 | - } | |
15 | - return keys | |
10 … | + if (!keys) { | |
11 … | + const config = api.config.sync.load() | |
12 … | + const keyPath = Path.join(config.path, 'secret') | |
13 … | + keys = Keys.loadOrCreateSync(keyPath) | |
16 | 14 … | } |
17 | - } | |
15 … | + return keys | |
16 … | + }) | |
18 | 17 … | } |
package.json | ||
---|---|---|
@@ -5,9 +5,9 @@ | ||
5 | 5 … | "main": "index.js", |
6 | 6 … | "scripts": { |
7 | 7 … | "start": "electro example", |
8 | 8 … | "rebuild": "npm rebuild --runtime=electron --target=$(electron -v) --abi=$(electron --abi) --disturl=https://atom.io/download/atom-shell", |
9 | - "test": "tape test" | |
9 … | + "test": "standard" | |
10 | 10 … | }, |
11 | 11 … | "repository": { |
12 | 12 … | "type": "git", |
13 | 13 … | "url": "git+https://github.com/ssbc/patchcore.git" |
@@ -49,7 +49,8 @@ | ||
49 | 49 … | }, |
50 | 50 … | "devDependencies": { |
51 | 51 … | "depject": "github:dominictarr/depject#reduce-with-context", |
52 | 52 … | "electro": "^2.0.3", |
53 | - "insert-css": "^2.0.0" | |
53 … | + "insert-css": "^2.0.0", | |
54 … | + "standard": "^8.6.0" | |
54 | 55 … | } |
55 | 56 … | } |
sbot.js | ||
---|---|---|
@@ -5,29 +5,35 @@ | ||
5 | 5 … | var createFeed = require('ssb-feed') |
6 | 6 … | |
7 | 7 … | var cache = CACHE = {} |
8 | 8 … | |
9 | -exports.needs = { | |
10 | - config: 'first', | |
11 | - keys: 'first', | |
12 | - connection_status: 'map' | |
13 | -} | |
9 … | +exports.needs = nest({ | |
10 … | + 'config.sync.load': 'first', | |
11 … | + 'keys.sync.load': 'first', | |
12 … | + 'sbot.obs.connectionStatus': 'first' | |
13 … | +}) | |
14 | 14 … | |
15 | 15 … | exports.gives = { |
16 | - sbot_log: true, | |
17 | - sbot_get: true, | |
18 | - sbot_user_feed: true, | |
19 | - sbot_query: true, | |
20 | - sbot_publish: true, | |
21 | - connection_status: true | |
16 … | + sbot: { | |
17 … | + pull: { | |
18 … | + log: true, | |
19 … | + get: true, | |
20 … | + userFeed: true, | |
21 … | + query: true, | |
22 … | + publish: true, | |
23 … | + }, | |
24 … | + obs: { | |
25 … | + connectionStatus: true | |
26 … | + } | |
27 … | + } | |
22 | 28 … | } |
23 | 29 … | |
24 | 30 … | exports.create = function (api) { |
25 | - const config = api.config() | |
26 | - const keys = api.keys() | |
31 … | + const config = api.config.sync.load() | |
32 … | + const keys = api.keys.sync.load() | |
27 | 33 … | |
28 | 34 … | var sbot = null |
29 | - var connection_status = [] | |
35 … | + var connectionStatus = Value() | |
30 | 36 … | |
31 | 37 … | var rec = { |
32 | 38 … | sync: () => {}, |
33 | 39 … | async: () => {}, |
@@ -35,18 +41,19 @@ | ||
35 | 41 … | } |
36 | 42 … | |
37 | 43 … | var rec = Reconnect(function (isConn) { |
38 | 44 … | function notify (value) { |
39 | - isConn(value); api.connection_status(value) //.forEach(function (fn) { fn(value) }) | |
45 … | + isConn(value); connectionStatus.set(value) | |
40 | 46 … | } |
41 | 47 … | |
42 | 48 … | createClient(keys, { |
43 | 49 … | manifest: require('./manifest.json'), |
44 | 50 … | remote: config.remote, |
45 | 51 … | caps: config.caps |
46 | 52 … | }, function (err, _sbot) { |
47 | - if(err) | |
53 … | + if (err) { | |
48 | 54 … | return notify(err) |
55 … | + } | |
49 | 56 … | |
50 | 57 … | sbot = _sbot |
51 | 58 … | sbot.on('closed', function () { |
52 | 59 … | sbot = null |
@@ -68,50 +75,60 @@ | ||
68 | 75 … | |
69 | 76 … | var feed = createFeed(internal, keys, {remote: true}) |
70 | 77 … | |
71 | 78 … | return { |
72 | - connection_status: () => connection_status, | |
73 | - sbot_query: rec.source(query => { | |
74 | - return sbot.query.read(query) | |
75 | - }), | |
76 | - sbot_user_feed: rec.source(opts => { | |
77 | - return sbot.createUserStream(opts) | |
78 | - }), | |
79 | - sbot_get: rec.async(function (key, cb) { | |
80 | - if('function' !== typeof cb) | |
81 | - throw new Error('cb must be function') | |
82 | - if(CACHE[key]) cb(null, CACHE[key]) | |
83 | - else sbot.get(key, function (err, value) { | |
84 | - if(err) return cb(err) | |
85 | - cb(null, CACHE[key] = value) | |
86 | - }) | |
87 | - }), | |
88 | - sbot_publish: rec.async((content, cb) => { | |
89 | - if(content.recps) | |
90 | - content = ssbKeys.box(content, content.recps.map(e => { | |
91 | - return ref.isFeed(e) ? e : e.link | |
92 | - })) | |
93 | - else if(content.mentions) | |
94 | - content.mentions.forEach(mention => { | |
95 | - if(ref.isBlob(mention.link)) { | |
96 | - sbot.blobs.push(mention.link, err => { | |
97 | - if(err) console.error(err) | |
79 … | + sbot: { | |
80 … | + pull: { | |
81 … | + query: rec.source(query => { | |
82 … | + return sbot.query.read(query) | |
83 … | + }), | |
84 … | + userFeed: rec.source(opts => { | |
85 … | + return sbot.createUserStream(opts) | |
86 … | + }), | |
87 … | + get: rec.async(function (key, cb) { | |
88 … | + if (typeof cb !== 'function') { | |
89 … | + throw new Error('cb must be function') | |
90 … | + } | |
91 … | + if (CACHE[key]) cb(null, CACHE[key]) | |
92 … | + else { | |
93 … | + sbot.get(key, function (err, value) { | |
94 … | + if (err) return cb(err) | |
95 … | + cb(null, CACHE[key] = value) | |
98 | 96 … | }) |
99 | 97 … | } |
100 | - }) | |
98 … | + }), | |
99 … | + publish: rec.async((content, cb) => { | |
100 … | + if (content.recps) { | |
101 … | + content = ssbKeys.box(content, content.recps.map(e => { | |
102 … | + return ref.isFeed(e) ? e : e.link | |
103 … | + })) | |
104 … | + } else if (content.mentions) { | |
105 … | + content.mentions.forEach(mention => { | |
106 … | + if (ref.isBlob(mention.link)) { | |
107 … | + sbot.blobs.push(mention.link, err => { | |
108 … | + if (err) console.error(err) | |
109 … | + }) | |
110 … | + } | |
111 … | + }) | |
112 … | + } | |
101 | 113 … | |
102 | - feed.add(content, (err, msg) => { | |
103 | - if(err) console.error(err) | |
104 | - else if(!cb) console.log(msg) | |
105 | - cb && cb(err, msg) | |
106 | - }) | |
107 | - }), | |
108 | - sbot_log: rec.source(opts => { | |
109 | - return pull( | |
110 | - sbot.createLogStream(opts), | |
111 | - pull.through(e => { | |
112 | - CACHE[e.key] = CACHE[e.key] || e.value | |
114 … | + feed.add(content, (err, msg) => { | |
115 … | + if (err) console.error(err) | |
116 … | + else if (!cb) console.log(msg) | |
117 … | + cb && cb(err, msg) | |
118 … | + }) | |
119 … | + }), | |
120 … | + log: rec.source(opts => { | |
121 … | + return pull( | |
122 … | + sbot.createLogStream(opts), | |
123 … | + pull.through(e => { | |
124 … | + CACHE[e.key] = CACHE[e.key] || e.value | |
125 … | + }) | |
126 … | + ) | |
113 | 127 … | }) |
114 | - ) | |
115 | - }) | |
128 … | + }, | |
129 … | + obs: { | |
130 … | + connectionStatus: (listener) => connectionStatus(listener) | |
131 … | + } | |
132 … | + } | |
116 | 133 … | } |
117 | 134 … | } |
components/markdown.js | ||
---|---|---|
@@ -1,55 +1,0 @@ | ||
1 | -const renderer = require('ssb-markdown') | |
2 | -const fs = require('fs') | |
3 | -const h = require('mutant/h') | |
4 | -const ref = require('ssb-ref') | |
5 | - | |
6 | -exports.needs = { | |
7 | - blob_url: 'first', | |
8 | - emoji_url: 'first' | |
9 | -} | |
10 | - | |
11 | -exports.gives = { | |
12 | - markdown: true, | |
13 | - mcss: true | |
14 | -} | |
15 | - | |
16 | -exports.create = function (api) { | |
17 | - return { | |
18 | - markdown, | |
19 | - mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') | |
20 | - } | |
21 | - | |
22 | - function markdown (content) { | |
23 | - if('string' === typeof content) | |
24 | - content = {text: content} | |
25 | - //handle patchwork style mentions. | |
26 | - var mentions = {} | |
27 | - if(Array.isArray(content.mentions)) | |
28 | - content.mentions.forEach(function (link) { | |
29 | - if(link.name) mentions["@"+link.name] = link.link | |
30 | - }) | |
31 | - | |
32 | - var md = h('Markdown') | |
33 | - md.innerHTML = renderer.block(content.text, { | |
34 | - emoji: renderEmoji, | |
35 | - toUrl: (id) => { | |
36 | - if(ref.isBlob(id)) return api.blob_url(id) | |
37 | - return '#'+(mentions[id]?mentions[id]:id) | |
38 | - }, | |
39 | - imageLink: (id) => '#' + id | |
40 | - }) | |
41 | - | |
42 | - return md | |
43 | - | |
44 | - } | |
45 | - | |
46 | - function renderEmoji(emoji) { | |
47 | - var url = api.emoji_url(emoji) | |
48 | - if (!url) return ':' + emoji + ':' | |
49 | - return '<img src="' + encodeURI(url) + '"' | |
50 | - + ' alt=":' + escape(emoji) + ':"' | |
51 | - + ' title=":' + escape(emoji) + ':"' | |
52 | - + ' class="emoji">' | |
53 | - } | |
54 | - | |
55 | -} |
components/message/author.js | ||
---|---|---|
@@ -1,21 +1,0 @@ | ||
1 | -const h = require('mutant/h') | |
2 | - | |
3 | -exports.needs = { | |
4 | - obs_about_name: 'first' | |
5 | -} | |
6 | - | |
7 | -exports.gives = { | |
8 | - message_author: true | |
9 | -} | |
10 | - | |
11 | -exports.create = function (api) { | |
12 | - return { | |
13 | - message_author | |
14 | - } | |
15 | - | |
16 | - function message_author (msg) { | |
17 | - return h('div', {title: msg.value.author}, [ | |
18 | - '@', api.obs_about_name(msg.value.author) | |
19 | - ]) | |
20 | - } | |
21 | -} |
components/message/backlinks.js | ||
---|---|---|
@@ -1,40 +1,0 @@ | ||
1 | -const fs = require('fs') | |
2 | -const h = require('mutant/h') | |
3 | - | |
4 | -exports.needs = { | |
5 | - message_name: 'first' | |
6 | -} | |
7 | - | |
8 | -exports.gives = 'message_backlinks' | |
9 | - | |
10 | -exports.create = function (api) { | |
11 | - return function (msg) { | |
12 | - var links = [] | |
13 | - for(var k in CACHE) { | |
14 | - var _msg = CACHE[k] | |
15 | - var mentions = _msg.content.mentions | |
16 | - | |
17 | - if(Array.isArray(mentions)) { | |
18 | - for(var i = 0; i < mentions.length; i++) | |
19 | - if(mentions[i].link == msg.key) | |
20 | - links.push(k) | |
21 | - } | |
22 | - } | |
23 | - | |
24 | - if (links.length === 0) return null | |
25 | - | |
26 | - var hrefList = h('ul') | |
27 | - links.forEach(link => { | |
28 | - api.message_name(link, (err, name) => { | |
29 | - if (err) throw err | |
30 | - hrefList.appendChild(h('li', [ | |
31 | - h('a -backlink', { href: `#${link}` }, name) | |
32 | - ])) | |
33 | - }) | |
34 | - }) | |
35 | - return h('MessageBacklinks', [ | |
36 | - h('header', 'backlinks:'), | |
37 | - hrefList | |
38 | - ]) | |
39 | - } | |
40 | -} |
components/message/link.js | ||
---|---|---|
@@ -1,27 +1,0 @@ | ||
1 | -var h = require('mutant/h') | |
2 | -var ref = require('ssb-ref') | |
3 | - | |
4 | -exports.needs = { | |
5 | - message_name: 'first' | |
6 | -} | |
7 | - | |
8 | -exports.gives = 'message_link' | |
9 | - | |
10 | -exports.create = function (api) { | |
11 | - | |
12 | - return function (id) { | |
13 | - | |
14 | - if('string' !== typeof id) | |
15 | - throw new Error('link must be to message id') | |
16 | - | |
17 | - var link = h('a', {href: '#'+id}, id.substring(0, 10)+'...') | |
18 | - | |
19 | - if(ref.isMsg(id)) | |
20 | - api.message_name(id, function (err, name) { | |
21 | - if(err) console.error(err) | |
22 | - else link.textContent = name | |
23 | - }) | |
24 | - | |
25 | - return link | |
26 | - } | |
27 | -} |
components/message/name.js | ||
---|---|---|
@@ -1,25 +1,0 @@ | ||
1 | - | |
2 | -function title (s) { | |
3 | - var m = /^\n*([^\n]{0,40})/.exec(s) | |
4 | - return m && (m[1].length == 40 ? m[1]+'...' : m[1]) | |
5 | -} | |
6 | - | |
7 | -exports.needs = { sbot_get: 'first' } | |
8 | -exports.gives = 'message_name' | |
9 | - | |
10 | -//TODO: rewrite as observable? | |
11 | - | |
12 | -exports.create = function (api) { | |
13 | - return function (id, cb) { | |
14 | - api.sbot_get(id, function (err, value) { | |
15 | - if(err && err.name == 'NotFoundError') | |
16 | - return cb(null, id.substring(0, 10)+'...(missing)') | |
17 | - if(value.content.type === 'post' && 'string' === typeof value.content.text) | |
18 | - return cb(null, title(value.content.text)) | |
19 | - else if('string' === typeof value.content.text) | |
20 | - return cb(null, value.content.type + ':'+title(value.content.text)) | |
21 | - else | |
22 | - return cb(null, id.substring(0, 10)+'...') | |
23 | - }) | |
24 | - } | |
25 | -} |
components/message/timestamp.js | ||
---|---|---|
@@ -1,17 +1,0 @@ | ||
1 | -const h = require('mutant/h') | |
2 | -const nest = require('depnest') | |
3 | - | |
4 | -exports.gives = nest('message.timestamp') | |
5 | -exports.needs = nest('obs.timeAgo', 'first') | |
6 | - | |
7 | -exports.create = function (api) { | |
8 | - return nest('message.timestamp', timestamp) | |
9 | - | |
10 | - function timestamp (msg) { | |
11 | - return h('a.Timestamp', { | |
12 | - href: msg.key, | |
13 | - title: new Date(msg.value.timestamp) | |
14 | - }, api.obs.timeAgo(msg.value.timestamp)) | |
15 | - } | |
16 | -} | |
17 | - |
components/render_feed.js | ||
---|---|---|
@@ -1,28 +1,0 @@ | ||
1 | -const pull = require('pull-stream') | |
2 | -const h = require('mutant/h') | |
3 | - | |
4 | -exports.needs = { | |
5 | - message_render: 'first', | |
6 | - sbot_log: 'first' | |
7 | -} | |
8 | - | |
9 | -exports.gives = { | |
10 | - render_feed: true | |
11 | -} | |
12 | - | |
13 | -exports.create = function (api) { | |
14 | - return { | |
15 | - render_feed | |
16 | - } | |
17 | - | |
18 | - function render_feed (stream) { | |
19 | - const container = h('div') | |
20 | - | |
21 | - pull( | |
22 | - stream({reverse: true, limit: 100}), | |
23 | - pull.drain(msg => container.appendChild(api.message_render(msg))) | |
24 | - ) | |
25 | - | |
26 | - return container | |
27 | - } | |
28 | -} |
about/obs.js | ||
---|---|---|
@@ -1,0 +1,98 @@ | ||
1 … | +var {Value, Struct, computed} = require('mutant') | |
2 … | +var Abortable = require('pull-abortable') | |
3 … | +var pull = require('pull-stream') | |
4 … | +var msgs = require('ssb-msgs') | |
5 … | +var nest = require('depnest') | |
6 … | + | |
7 … | +exports.needs = nest({ | |
8 … | + 'sbot.pull.userFeed': 'first', | |
9 … | + 'blob.sync.url': 'first' | |
10 … | +}) | |
11 … | +exports.gives = nest({ | |
12 … | + 'about.obs': [ | |
13 … | + 'name', | |
14 … | + 'image', | |
15 … | + 'imageUrl' | |
16 … | + ] | |
17 … | +}) | |
18 … | + | |
19 … | +exports.create = function (api) { | |
20 … | + var cache = {} | |
21 … | + | |
22 … | + return nest({ | |
23 … | + 'about.obs': { | |
24 … | + name: (id) => get(id).displayName, | |
25 … | + image: (id) => get(id).image, | |
26 … | + imageUrl: (id) => { | |
27 … | + return computed(get(id).image, (image) => { | |
28 … | + var obj = msgs.link(image, 'blob') | |
29 … | + if (obj) { | |
30 … | + return api.blob_url(obj.link) | |
31 … | + } | |
32 … | + }) | |
33 … | + } | |
34 … | + } | |
35 … | + }) | |
36 … | + | |
37 … | + } | |
38 … | + | |
39 … | + function get (id) { | |
40 … | + if (!cache[id]) { | |
41 … | + cache[id] = About(api, id) | |
42 … | + } | |
43 … | + return cache[id] | |
44 … | + } | |
45 … | +} | |
46 … | + | |
47 … | +function About (api, id) { | |
48 … | + // naive about that only looks at what a feed asserts about itself | |
49 … | + | |
50 … | + var obs = Struct({ | |
51 … | + displayName: Value(id.slice(1, 10)), | |
52 … | + image: Value() | |
53 … | + }) | |
54 … | + | |
55 … | + var hasName = false | |
56 … | + var hasImage = false | |
57 … | + | |
58 … | + var abortable = Abortable() | |
59 … | + | |
60 … | + // search history | |
61 … | + pull( | |
62 … | + api.sbot.pull.userFeed({reverse: true, id}), | |
63 … | + abortable, | |
64 … | + pull.drain(function (item) { | |
65 … | + update(item) | |
66 … | + if (hasName && obs.image()) { | |
67 … | + abortable.abort() | |
68 … | + } | |
69 … | + }) | |
70 … | + ) | |
71 … | + | |
72 … | + // get live changes | |
73 … | + pull( | |
74 … | + api.sbot.pull.userFeed({old: false, id}), | |
75 … | + pull.drain(update) | |
76 … | + ) | |
77 … | + | |
78 … | + return obs | |
79 … | + | |
80 … | + // scoped | |
81 … | + | |
82 … | + function update (item) { | |
83 … | + if (item.value && item.value.content.type === 'about' && item.value.content.about === id) { | |
84 … | + if (item.value.content.name) { | |
85 … | + if (!hasName || hasName < item.value.timestamp) { | |
86 … | + hasName = item.value.timestamp | |
87 … | + obs.displayName.set(item.value.content.name) | |
88 … | + } | |
89 … | + } | |
90 … | + if (item.value.content.image) { | |
91 … | + if (!hasImage || hasImage < item.value.timestamp) { | |
92 … | + hasImage = item.value.timestamp | |
93 … | + obs.image.set(item.value.content.image) | |
94 … | + } | |
95 … | + } | |
96 … | + } | |
97 … | + } | |
98 … | +} |
blob.js | ||
---|---|---|
@@ -1,0 +1,9 @@ | ||
1 … | +const nest = require('depnest') | |
2 … | + | |
3 … | +exports.gives = nest('blob.sync.url') | |
4 … | + | |
5 … | +exports.create = function (api) { | |
6 … | + return nest('blob.sync.url', function (id) { | |
7 … | + return id | |
8 … | + } | |
9 … | +} |
helpers/blob_url.js | ||
---|---|---|
@@ -1,7 +1,0 @@ | ||
1 | -exports.gives = 'blob_url' | |
2 | - | |
3 | -exports.create = function (api) { | |
4 | - return function (id) { | |
5 | - return id | |
6 | - } | |
7 | -} |
helpers/emoji.js | ||
---|---|---|
@@ -1,17 +1,0 @@ | ||
1 | -var emojis = require('emoji-named-characters') | |
2 | -var emojiNames = Object.keys(emojis) | |
3 | - | |
4 | -exports.needs = { blob_url: 'first' } | |
5 | -exports.gives = { emoji_names: true, emoji_url: true } | |
6 | - | |
7 | -exports.create = function (api) { | |
8 | - return { | |
9 | - emoji_names: function () { | |
10 | - return emojiNames | |
11 | - }, | |
12 | - emoji_url: function (emoji) { | |
13 | - return emoji in emojis && | |
14 | - api.blob_url(emoji).replace(/\/blobs\/get/, '/img/emoji') + '.png' | |
15 | - } | |
16 | - } | |
17 | -} |
emoji.js | ||
---|---|---|
@@ -1,0 +1,28 @@ | ||
1 … | +const emojis = require('emoji-named-characters') | |
2 … | +const emojiNames = Object.keys(emojis) | |
3 … | +const nest = require('depnest') | |
4 … | + | |
5 … | +exports.needs = nest('blob.sync.url', 'first') | |
6 … | +exports.gives = nest({ | |
7 … | + 'emoji.sync': [ | |
8 … | + 'names', | |
9 … | + 'url' | |
10 … | + ] | |
11 … | + | |
12 … | +exports.create = function (api) { | |
13 … | + return nest({ | |
14 … | + 'emoji.sync': { | |
15 … | + names, | |
16 … | + url | |
17 … | + } | |
18 … | + }) | |
19 … | + | |
20 … | + function names () { | |
21 … | + return emojiNames | |
22 … | + } | |
23 … | + | |
24 … | + function url (emoji) { | |
25 … | + return emoji in emojis && | |
26 … | + api.blob.sync.url(emoji).replace(/\/blobs\/get/, '/img/emoji') + '.png' | |
27 … | + } | |
28 … | +} |
feed/html.js | ||
---|---|---|
@@ -1,0 +1,28 @@ | ||
1 … | +const pull = require('pull-stream') | |
2 … | +const h = require('mutant/h') | |
3 … | + | |
4 … | +exports.needs = { | |
5 … | + message_render: 'first', | |
6 … | + sbot_log: 'first' | |
7 … | +} | |
8 … | + | |
9 … | +exports.gives = { | |
10 … | + render_feed: true | |
11 … | +} | |
12 … | + | |
13 … | +exports.create = function (api) { | |
14 … | + return { | |
15 … | + render_feed | |
16 … | + } | |
17 … | + | |
18 … | + function render_feed (stream) { | |
19 … | + const container = h('div') | |
20 … | + | |
21 … | + pull( | |
22 … | + stream({reverse: true, limit: 100}), | |
23 … | + pull.drain(msg => container.appendChild(api.message_render(msg))) | |
24 … | + ) | |
25 … | + | |
26 … | + return container | |
27 … | + } | |
28 … | +} |
feed/pull/private.js | ||
---|---|---|
@@ -1,0 +1,30 @@ | ||
1 … | +const pull = require('pull-stream') | |
2 … | +const nest = require('depnest') | |
3 … | + | |
4 … | +exports.gives = nest('feed.pull.private') | |
5 … | +exports.needs = nest({ | |
6 … | + 'sbot.log': 'first', | |
7 … | + // 'message.sync.unbox': 'first' | |
8 … | +}) | |
9 … | +exports.create = function (api) { | |
10 … | + return nest('feed.pull.private': function (opts) { | |
11 … | + pull( | |
12 … | + api.sbot_log(opts)//, | |
13 … | + // unbox() | |
14 … | + ) | |
15 … | + } | |
16 … | + | |
17 … | + // scoped | |
18 … | + | |
19 … | + function unbox () { | |
20 … | + return pull( | |
21 … | + pull.filter(function (msg) { | |
22 … | + return typeof msg.value.content === 'string' | |
23 … | + }), | |
24 … | + pull.map(function (msg) { | |
25 … | + return api.message.sync.unbox(msg) | |
26 … | + }), | |
27 … | + pull.filter(Boolean) | |
28 … | + ) | |
29 … | + } | |
30 … | +} |
feed/pull/public.js | ||
---|---|---|
@@ -1,0 +1,7 @@ | ||
1 … | +const nest = require('depnest') | |
2 … | + | |
3 … | +exports.gives = nest('feed.pull.public') | |
4 … | +exports.needs = nest('sbot.log', 'first') | |
5 … | +exports.create = function (api) { | |
6 … | + return nest('feed.pull.public', api.sbot.pull.log) | |
7 … | +} |
observables/about.js | ||
---|---|---|
@@ -1,91 +1,0 @@ | ||
1 | -var {Value, Struct, computed} = require('mutant') | |
2 | -var Abortable = require('pull-abortable') | |
3 | -var pull = require('pull-stream') | |
4 | -var msgs = require('ssb-msgs') | |
5 | - | |
6 | -exports.needs = { | |
7 | - sbot_user_feed: 'first', | |
8 | - blob_url: 'first' | |
9 | -} | |
10 | -exports.gives = { | |
11 | - obs_about_name: true, | |
12 | - obs_about_image: true, | |
13 | - obs_about_image_url: true | |
14 | -} | |
15 | - | |
16 | -exports.create = function (api) { | |
17 | - var cache = {} | |
18 | - | |
19 | - return { | |
20 | - obs_about_name: (id) => get(id).displayName, | |
21 | - obs_about_image: (id) => get(id).image, | |
22 | - obs_about_image_url: (id) => { | |
23 | - return computed(get(id).image, (image) => { | |
24 | - var obj = msgs.link(image, 'blob') | |
25 | - if (obj) { | |
26 | - return api.blob_url(obj.link) | |
27 | - } | |
28 | - }) | |
29 | - } | |
30 | - } | |
31 | - | |
32 | - function get (id) { | |
33 | - if (!cache[id]) { | |
34 | - cache[id] = About(api, id) | |
35 | - } | |
36 | - return cache[id] | |
37 | - } | |
38 | -} | |
39 | - | |
40 | -function About (api, id) { | |
41 | - // naive about that only looks at what a feed asserts about itself | |
42 | - | |
43 | - var obs = Struct({ | |
44 | - displayName: Value(id.slice(1, 10)), | |
45 | - image: Value() | |
46 | - }) | |
47 | - | |
48 | - var hasName = false | |
49 | - var hasImage = false | |
50 | - | |
51 | - var abortable = Abortable() | |
52 | - | |
53 | - // search history | |
54 | - pull( | |
55 | - api.sbot_user_feed({reverse: true, id}), | |
56 | - abortable, | |
57 | - pull.drain(function (item) { | |
58 | - update(item) | |
59 | - if (hasName && obs.image()) { | |
60 | - abortable.abort() | |
61 | - } | |
62 | - }) | |
63 | - ) | |
64 | - | |
65 | - // get live changes | |
66 | - pull( | |
67 | - api.sbot_user_feed({old: false, id}), | |
68 | - pull.drain(update) | |
69 | - ) | |
70 | - | |
71 | - return obs | |
72 | - | |
73 | - // scoped | |
74 | - | |
75 | - function update (item) { | |
76 | - if (item.value && item.value.content.type === 'about' && item.value.content.about === id) { | |
77 | - if (item.value.content.name) { | |
78 | - if (!hasName || hasName < item.value.timestamp) { | |
79 | - hasName = item.value.timestamp | |
80 | - obs.displayName.set(item.value.content.name) | |
81 | - } | |
82 | - } | |
83 | - if (item.value.content.image) { | |
84 | - if (!hasImage || hasImage < item.value.timestamp) { | |
85 | - hasImage = item.value.timestamp | |
86 | - obs.image.set(item.value.content.image) | |
87 | - } | |
88 | - } | |
89 | - } | |
90 | - } | |
91 | -} |
observables/timeAgo.js | ||
---|---|---|
@@ -1,34 +1,0 @@ | ||
1 | -const Value = require('mutant/value') | |
2 | -const computed = require('mutant/computed') | |
3 | -const nest = require('depnest') | |
4 | -const human = require('human-time') | |
5 | - | |
6 | -exports.gives = nest('obs.timeAgo') | |
7 | - | |
8 | -exports.create = function (api) { | |
9 | - return nest('obs.timeAgo', timeAgo) | |
10 | - | |
11 | - function timeAgo (timestamp) { | |
12 | - var timer | |
13 | - var value = Value(Time(timestamp)) | |
14 | - return computed([value], (a) => a, { | |
15 | - onListen: () => { | |
16 | - timer = setInterval(refresh, 5e3) | |
17 | - refresh() | |
18 | - }, | |
19 | - onUnlisten: () => { | |
20 | - clearInterval(timer) | |
21 | - } | |
22 | - }) | |
23 | - | |
24 | - function refresh () { | |
25 | - value.set(Time(timestamp)) | |
26 | - } | |
27 | - } | |
28 | -} | |
29 | - | |
30 | -function Time (timestamp) { | |
31 | - return human(new Date(timestamp)) | |
32 | - .replace(/minute/, 'min') | |
33 | - .replace(/second/, 'sec') | |
34 | -} |
lib/timeAgo.js | ||
---|---|---|
@@ -1,0 +1,34 @@ | ||
1 … | +const Value = require('mutant/value') | |
2 … | +const computed = require('mutant/computed') | |
3 … | +const nest = require('depnest') | |
4 … | +const human = require('human-time') | |
5 … | + | |
6 … | +exports.gives = nest('lib.obs.timeAgo') | |
7 … | + | |
8 … | +exports.create = function (api) { | |
9 … | + return nest('lib.obs.timeAgo', timeAgo) | |
10 … | + | |
11 … | + function timeAgo (timestamp) { | |
12 … | + var timer | |
13 … | + var value = Value(Time(timestamp)) | |
14 … | + return computed([value], (a) => a, { | |
15 … | + onListen: () => { | |
16 … | + timer = setInterval(refresh, 5e3) | |
17 … | + refresh() | |
18 … | + }, | |
19 … | + onUnlisten: () => { | |
20 … | + clearInterval(timer) | |
21 … | + } | |
22 … | + }) | |
23 … | + | |
24 … | + function refresh () { | |
25 … | + value.set(Time(timestamp)) | |
26 … | + } | |
27 … | + } | |
28 … | +} | |
29 … | + | |
30 … | +function Time (timestamp) { | |
31 … | + return human(new Date(timestamp)) | |
32 … | + .replace(/minute/, 'min') | |
33 … | + .replace(/second/, 'sec') | |
34 … | +} |
plugs/message_action/reply.js | ||
---|---|---|
@@ -1,9 +1,0 @@ | ||
1 | -var h = require('mutant/h') | |
2 | - | |
3 | -exports.gives = 'message_action' | |
4 | - | |
5 | -exports.create = function () { | |
6 | - return function reply (msg) { | |
7 | - return h('a', { href: '#' + msg.key }, 'Reply') | |
8 | - } | |
9 | -} |
plugs/message_decorate/data-id.js | ||
---|---|---|
@@ -1,8 +1,0 @@ | ||
1 | -exports.gives = 'message_decorate' | |
2 | - | |
3 | -exports.create = function (api) { | |
4 | - return function (element, { msg }) { | |
5 | - element.dataset.id = msg.key | |
6 | - return element | |
7 | - } | |
8 | -} |
plugs/message_layout/default.js | ||
---|---|---|
@@ -1,37 +1,0 @@ | ||
1 | -const h = require('mutant/h') | |
2 | - | |
3 | -exports.needs = { | |
4 | - message_backlinks: 'first', | |
5 | - message_author: 'first', | |
6 | - message_meta: 'map', | |
7 | - message_action: 'map', | |
8 | - message: { | |
9 | - timestamp: 'first' | |
10 | - } | |
11 | -} | |
12 | - | |
13 | -exports.gives = { | |
14 | - message_layout: true | |
15 | -} | |
16 | - | |
17 | -exports.create = function (api) { | |
18 | - return { | |
19 | - message_layout | |
20 | - } | |
21 | - | |
22 | - function message_layout (msg, opts) { | |
23 | - if (!(opts.layout === undefined || opts.layout === 'default')) return | |
24 | - return h('div', { | |
25 | - classList: 'Message' | |
26 | - }, [ | |
27 | - h('header.author', {}, api.message_author(msg)), | |
28 | - h('section.timestamp', {}, api.message.timestamp(msg)), | |
29 | - h('section.title', {}, opts.title), | |
30 | - h('section.meta', {}, api.message_meta(msg)), | |
31 | - h('section.content', {}, opts.content), | |
32 | - h('section.raw-content'), | |
33 | - h('section.action', {}, api.message_action(msg)), | |
34 | - h('footer.backlinks', {}, api.message_backlinks(msg)) | |
35 | - ]) | |
36 | - } | |
37 | -} |
plugs/message_layout/mini.js | ||
---|---|---|
@@ -1,33 +1,0 @@ | ||
1 | -const h = require('mutant/h') | |
2 | - | |
3 | -exports.needs = { | |
4 | - message_backlinks: 'first', | |
5 | - message_author: 'first', | |
6 | - message_meta: 'map', | |
7 | - message: { | |
8 | - timestamp: 'first' | |
9 | - } | |
10 | -} | |
11 | - | |
12 | -exports.gives = { | |
13 | - message_layout: true | |
14 | -} | |
15 | - | |
16 | -exports.create = function (api) { | |
17 | - return { | |
18 | - message_layout | |
19 | - } | |
20 | - | |
21 | - function message_layout (msg, opts) { | |
22 | - if (opts.layout !== 'mini') return | |
23 | - return h('div', { | |
24 | - classList: 'Message -mini' | |
25 | - }, [ | |
26 | - h('header.author', {}, api.message_author(msg, { size: 'mini' })), | |
27 | - h('section.timestamp', {}, api.message.timestamp(msg)), | |
28 | - h('section.meta', {}, api.message_meta(msg)), | |
29 | - h('section.content', {}, opts.content), | |
30 | - h('section.raw-content') | |
31 | - ]) | |
32 | - } | |
33 | -} |
plugs/message_meta/channel.js | ||
---|---|---|
@@ -1,10 +1,0 @@ | ||
1 | -const h = require('mutant/h') | |
2 | - | |
3 | -exports.gives = 'message_meta' | |
4 | - | |
5 | -exports.create = function (api) { | |
6 | - return function channel (msg) { | |
7 | - const { channel } = msg.value.content | |
8 | - if (channel) return h('span', {}, ['#' + channel]) | |
9 | - } | |
10 | -} |
plugs/message_render/post.js | ||
---|---|---|
@@ -1,39 +1,0 @@ | ||
1 | -var h = require('mutant/h') | |
2 | - | |
3 | -exports.needs = { | |
4 | - message_decorate: 'reduce', | |
5 | - message_layout: 'first', | |
6 | - message_link: 'first', | |
7 | - markdown: 'first' | |
8 | -} | |
9 | - | |
10 | -exports.gives = { | |
11 | - message_render: true | |
12 | -} | |
13 | - | |
14 | -exports.create = function (api) { | |
15 | - return { | |
16 | - message_render | |
17 | - } | |
18 | - | |
19 | - function message_render (msg) { | |
20 | - if (msg.value.content.type !== 'post') return | |
21 | - var element = api.message_layout(msg, { | |
22 | - title: message_title(msg), | |
23 | - content: message_content(msg), | |
24 | - layout: 'default' | |
25 | - }) | |
26 | - | |
27 | - return api.message_decorate(element, { msg }) | |
28 | - } | |
29 | - | |
30 | - function message_content (data) { | |
31 | - if (!data.value.content || !data.value.content.text) return | |
32 | - return h('div', {}, api.markdown(data.value.content)) | |
33 | - } | |
34 | - | |
35 | - function message_title (data) { | |
36 | - var root = data.value.content && data.value.content.root | |
37 | - return !root ? null : h('span', ['re: ', api.message_link(root)]) | |
38 | - } | |
39 | -} |
plugs/message_render/vote.js | ||
---|---|---|
@@ -1,35 +1,0 @@ | ||
1 | -var h = require('mutant/h') | |
2 | - | |
3 | -exports.needs = { | |
4 | - message_layout: 'first', | |
5 | - message_decorate: 'reduce', | |
6 | - message_link: 'first', | |
7 | - markdown: 'first' | |
8 | -} | |
9 | - | |
10 | -exports.gives = { | |
11 | - message_render: true | |
12 | -} | |
13 | - | |
14 | -exports.create = function (api) { | |
15 | - return { | |
16 | - message_render | |
17 | - } | |
18 | - | |
19 | - function message_render (msg) { | |
20 | - if (msg.value.content.type !== 'vote') return | |
21 | - var element = api.message_layout(msg, { | |
22 | - content: render_vote(msg), | |
23 | - layout: 'mini' | |
24 | - }) | |
25 | - | |
26 | - return api.message_decorate(element, { msg }) | |
27 | - } | |
28 | - | |
29 | - function render_vote (msg) { | |
30 | - var link = msg.value.content.vote.link | |
31 | - return [ | |
32 | - msg.value.content.vote.value > 0 ? 'dug' : 'undug', ' ', api.message_link(link) | |
33 | - ] | |
34 | - } | |
35 | -} |
plugs/message_render/zz_fallback.js | ||
---|---|---|
@@ -1,33 +1,0 @@ | ||
1 | -var h = require('mutant/h') | |
2 | - | |
3 | -exports.needs = { | |
4 | - message_layout: 'first', | |
5 | - message_decorate: 'reduce' | |
6 | -} | |
7 | - | |
8 | -exports.gives = { | |
9 | - message_render: true | |
10 | -} | |
11 | - | |
12 | -exports.create = function (api) { | |
13 | - return { | |
14 | - message_render | |
15 | - } | |
16 | - | |
17 | - function message_render (msg) { | |
18 | - var element = api.message_layout(msg, { | |
19 | - content: message_content(msg), | |
20 | - layout: 'mini' | |
21 | - }) | |
22 | - | |
23 | - return api.message_decorate(element, { msg }) | |
24 | - } | |
25 | - | |
26 | - function message_content (msg) { | |
27 | - if (typeof msg.value.content === 'string') { | |
28 | - return h('code', {}, 'PRIVATE') | |
29 | - } else { | |
30 | - return h('code', {}, msg.value.content.type) | |
31 | - } | |
32 | - } | |
33 | -} |
message/html/action/reply.js | ||
---|---|---|
@@ -1,0 +1,9 @@ | ||
1 … | +var h = require('mutant/h') | |
2 … | + | |
3 … | +exports.gives = 'message_action' | |
4 … | + | |
5 … | +exports.create = function () { | |
6 … | + return function reply (msg) { | |
7 … | + return h('a', { href: '#' + msg.key }, 'Reply') | |
8 … | + } | |
9 … | +} |
message/html/author.js | ||
---|---|---|
@@ -1,0 +1,20 @@ | ||
1 … | +const h = require('mutant/h') | |
2 … | +const nest = require('depnest') | |
3 … | + | |
4 … | +exports.needs = nest('obs.about.name', 'first') | |
5 … | + | |
6 … | +exports.gives = nest('obs.'){ | |
7 … | + message_author: true | |
8 … | +} | |
9 … | + | |
10 … | +exports.create = function (api) { | |
11 … | + return { | |
12 … | + message_author | |
13 … | + } | |
14 … | + | |
15 … | + function message_author (msg) { | |
16 … | + return h('div', {title: msg.value.author}, [ | |
17 … | + '@', api.obs_about_name(msg.value.author) | |
18 … | + ]) | |
19 … | + } | |
20 … | +} |
message/html/backlinks.js | ||
---|---|---|
@@ -1,0 +1,42 @@ | ||
1 … | +const fs = require('fs') | |
2 … | +const h = require('mutant/h') | |
3 … | + | |
4 … | +exports.needs = { | |
5 … | + message_name: 'first' | |
6 … | +} | |
7 … | + | |
8 … | +exports.gives = 'message_backlinks' | |
9 … | + | |
10 … | +exports.create = function (api) { | |
11 … | + return function (msg) { | |
12 … | + var links = [] | |
13 … | + for (var k in CACHE) { | |
14 … | + var _msg = CACHE[k] | |
15 … | + var mentions = _msg.content.mentions | |
16 … | + | |
17 … | + if (Array.isArray(mentions)) { | |
18 … | + for (var i = 0; i < mentions.length; i++) { | |
19 … | + if (mentions[i].link == msg.key) { | |
20 … | + links.push(k) | |
21 … | + } | |
22 … | + } | |
23 … | + } | |
24 … | + } | |
25 … | + | |
26 … | + if (links.length === 0) return null | |
27 … | + | |
28 … | + var hrefList = h('ul') | |
29 … | + links.forEach(link => { | |
30 … | + api.message_name(link, (err, name) => { | |
31 … | + if (err) throw err | |
32 … | + hrefList.appendChild(h('li', [ | |
33 … | + h('a -backlink', { href: `#${link}` }, name) | |
34 … | + ])) | |
35 … | + }) | |
36 … | + }) | |
37 … | + return h('MessageBacklinks', [ | |
38 … | + h('header', 'backlinks:'), | |
39 … | + hrefList | |
40 … | + ]) | |
41 … | + } | |
42 … | +} |
message/html/decorate/data-id.js | ||
---|---|---|
@@ -1,0 +1,8 @@ | ||
1 … | +exports.gives = 'message_decorate' | |
2 … | + | |
3 … | +exports.create = function (api) { | |
4 … | + return function (element, { msg }) { | |
5 … | + element.dataset.id = msg.key | |
6 … | + return element | |
7 … | + } | |
8 … | +} |
message/html/layout/default.js | ||
---|---|---|
@@ -1,0 +1,37 @@ | ||
1 … | +const h = require('mutant/h') | |
2 … | + | |
3 … | +exports.needs = { | |
4 … | + message_backlinks: 'first', | |
5 … | + message_author: 'first', | |
6 … | + message_meta: 'map', | |
7 … | + message_action: 'map', | |
8 … | + message: { | |
9 … | + timestamp: 'first' | |
10 … | + } | |
11 … | +} | |
12 … | + | |
13 … | +exports.gives = { | |
14 … | + message_layout: true | |
15 … | +} | |
16 … | + | |
17 … | +exports.create = function (api) { | |
18 … | + return { | |
19 … | + message_layout | |
20 … | + } | |
21 … | + | |
22 … | + function message_layout (msg, opts) { | |
23 … | + if (!(opts.layout === undefined || opts.layout === 'default')) return | |
24 … | + return h('div', { | |
25 … | + classList: 'Message' | |
26 … | + }, [ | |
27 … | + h('header.author', {}, api.message_author(msg)), | |
28 … | + h('section.timestamp', {}, api.message.timestamp(msg)), | |
29 … | + h('section.title', {}, opts.title), | |
30 … | + h('section.meta', {}, api.message_meta(msg)), | |
31 … | + h('section.content', {}, opts.content), | |
32 … | + h('section.raw-content'), | |
33 … | + h('section.action', {}, api.message_action(msg)), | |
34 … | + h('footer.backlinks', {}, api.message_backlinks(msg)) | |
35 … | + ]) | |
36 … | + } | |
37 … | +} |
message/html/layout/mini.js | ||
---|---|---|
@@ -1,0 +1,33 @@ | ||
1 … | +const h = require('mutant/h') | |
2 … | + | |
3 … | +exports.needs = { | |
4 … | + message_backlinks: 'first', | |
5 … | + message_author: 'first', | |
6 … | + message_meta: 'map', | |
7 … | + message: { | |
8 … | + timestamp: 'first' | |
9 … | + } | |
10 … | +} | |
11 … | + | |
12 … | +exports.gives = { | |
13 … | + message_layout: true | |
14 … | +} | |
15 … | + | |
16 … | +exports.create = function (api) { | |
17 … | + return { | |
18 … | + message_layout | |
19 … | + } | |
20 … | + | |
21 … | + function message_layout (msg, opts) { | |
22 … | + if (opts.layout !== 'mini') return | |
23 … | + return h('div', { | |
24 … | + classList: 'Message -mini' | |
25 … | + }, [ | |
26 … | + h('header.author', {}, api.message_author(msg, { size: 'mini' })), | |
27 … | + h('section.timestamp', {}, api.message.timestamp(msg)), | |
28 … | + h('section.meta', {}, api.message_meta(msg)), | |
29 … | + h('section.content', {}, opts.content), | |
30 … | + h('section.raw-content') | |
31 … | + ]) | |
32 … | + } | |
33 … | +} |
message/html/link.js | ||
---|---|---|
@@ -1,0 +1,25 @@ | ||
1 … | +var h = require('mutant/h') | |
2 … | +var ref = require('ssb-ref') | |
3 … | + | |
4 … | +exports.needs = { | |
5 … | + message_name: 'first' | |
6 … | +} | |
7 … | + | |
8 … | +exports.gives = 'message_link' | |
9 … | + | |
10 … | +exports.create = function (api) { | |
11 … | + return function (id) { | |
12 … | + if (typeof id !== 'string') { throw new Error('link must be to message id') } | |
13 … | + | |
14 … | + var link = h('a', {href: '#' + id}, id.substring(0, 10) + '...') | |
15 … | + | |
16 … | + if (ref.isMsg(id)) { | |
17 … | + api.message_name(id, function (err, name) { | |
18 … | + if (err) console.error(err) | |
19 … | + else link.textContent = name | |
20 … | + }) | |
21 … | + } | |
22 … | + | |
23 … | + return link | |
24 … | + } | |
25 … | +} |
message/html/markdown.js | ||
---|---|---|
@@ -1,0 +1,55 @@ | ||
1 … | +const renderer = require('ssb-markdown') | |
2 … | +const fs = require('fs') | |
3 … | +const h = require('mutant/h') | |
4 … | +const ref = require('ssb-ref') | |
5 … | + | |
6 … | +exports.needs = { | |
7 … | + blob_url: 'first', | |
8 … | + emoji_url: 'first' | |
9 … | +} | |
10 … | + | |
11 … | +exports.gives = { | |
12 … | + markdown: true | |
13 … | +} | |
14 … | + | |
15 … | +exports.create = function (api) { | |
16 … | + return { | |
17 … | + markdown | |
18 … | + } | |
19 … | + | |
20 … | + function markdown (content) { | |
21 … | + if (typeof content === 'string') { content = {text: content} } | |
22 … | + // handle patchwork style mentions. | |
23 … | + var mentions = {} | |
24 … | + if (Array.isArray(content.mentions)) { | |
25 … | + content.mentions.forEach(function (link) { | |
26 … | + if (link.name) mentions['@' + link.name] = link.link | |
27 … | + }) | |
28 … | + } | |
29 … | + | |
30 … | + var md = h('Markdown') | |
31 … | + md.innerHTML = renderer.block(content.text, { | |
32 … | + emoji: renderEmoji, | |
33 … | + toUrl: (id) => { | |
34 … | + if (ref.isBlob(id)) return api.blob_url(id) | |
35 … | + return '#' + (mentions[id] ? mentions[id] : id) | |
36 … | + }, | |
37 … | + imageLink: (id) => '#' + id | |
38 … | + }) | |
39 … | + | |
40 … | + return md | |
41 … | + } | |
42 … | + | |
43 … | + function renderEmoji (emoji) { | |
44 … | + var url = api.emoji_url(emoji) | |
45 … | + if (!url) return ':' + emoji + ':' | |
46 … | + return ` | |
47 … | + <img | |
48 … | + src="${encodeURI(url)}" | |
49 … | + alt=":${escape(emoji)}:" | |
50 … | + title=":${escape(emoji)}:" | |
51 … | + class="emoji" | |
52 … | + > | |
53 … | + ` | |
54 … | + } | |
55 … | +} |
message/html/meta/channel.js | ||
---|---|---|
@@ -1,0 +1,10 @@ | ||
1 … | +const h = require('mutant/h') | |
2 … | + | |
3 … | +exports.gives = 'message_meta' | |
4 … | + | |
5 … | +exports.create = function (api) { | |
6 … | + return function channel (msg) { | |
7 … | + const { channel } = msg.value.content | |
8 … | + if (channel) return h('span', {}, ['#' + channel]) | |
9 … | + } | |
10 … | +} |
message/html/name.js | ||
---|---|---|
@@ -1,0 +1,23 @@ | ||
1 … | + | |
2 … | +function title (s) { | |
3 … | + var m = /^\n*([^\n]{0,40})/.exec(s) | |
4 … | + return m && (m[1].length == 40 ? m[1] + '...' : m[1]) | |
5 … | +} | |
6 … | + | |
7 … | +exports.needs = { sbot_get: 'first' } | |
8 … | +exports.gives = 'message_name' | |
9 … | + | |
10 … | +// TODO: rewrite as observable? | |
11 … | + | |
12 … | +exports.create = function (api) { | |
13 … | + return function (id, cb) { | |
14 … | + api.sbot_get(id, function (err, value) { | |
15 … | + if (err && err.name == 'NotFoundError') { return cb(null, id.substring(0, 10) + '...(missing)') } | |
16 … | + if (value.content.type === 'post' && typeof value.content.text === 'string') { | |
17 … | + return cb(null, title(value.content.text)) | |
18 … | + } else if (typeof value.content.text === 'string') { return cb(null, value.content.type + ':' + title(value.content.text)) } else { | |
19 … | + return cb(null, id.substring(0, 10) + '...') | |
20 … | + } | |
21 … | + }) | |
22 … | + } | |
23 … | +} |
message/html/render/post.js | ||
---|---|---|
@@ -1,0 +1,39 @@ | ||
1 … | +var h = require('mutant/h') | |
2 … | + | |
3 … | +exports.needs = { | |
4 … | + message_decorate: 'reduce', | |
5 … | + message_layout: 'first', | |
6 … | + message_link: 'first', | |
7 … | + markdown: 'first' | |
8 … | +} | |
9 … | + | |
10 … | +exports.gives = { | |
11 … | + message_render: true | |
12 … | +} | |
13 … | + | |
14 … | +exports.create = function (api) { | |
15 … | + return { | |
16 … | + message_render | |
17 … | + } | |
18 … | + | |
19 … | + function message_render (msg) { | |
20 … | + if (msg.value.content.type !== 'post') return | |
21 … | + var element = api.message_layout(msg, { | |
22 … | + title: message_title(msg), | |
23 … | + content: message_content(msg), | |
24 … | + layout: 'default' | |
25 … | + }) | |
26 … | + | |
27 … | + return api.message_decorate(element, { msg }) | |
28 … | + } | |
29 … | + | |
30 … | + function message_content (data) { | |
31 … | + if (!data.value.content || !data.value.content.text) return | |
32 … | + return h('div', {}, api.markdown(data.value.content)) | |
33 … | + } | |
34 … | + | |
35 … | + function message_title (data) { | |
36 … | + var root = data.value.content && data.value.content.root | |
37 … | + return !root ? null : h('span', ['re: ', api.message_link(root)]) | |
38 … | + } | |
39 … | +} |
message/html/render/vote.js | ||
---|---|---|
@@ -1,0 +1,35 @@ | ||
1 … | +var h = require('mutant/h') | |
2 … | + | |
3 … | +exports.needs = { | |
4 … | + message_layout: 'first', | |
5 … | + message_decorate: 'reduce', | |
6 … | + message_link: 'first', | |
7 … | + markdown: 'first' | |
8 … | +} | |
9 … | + | |
10 … | +exports.gives = { | |
11 … | + message_render: true | |
12 … | +} | |
13 … | + | |
14 … | +exports.create = function (api) { | |
15 … | + return { | |
16 … | + message_render | |
17 … | + } | |
18 … | + | |
19 … | + function message_render (msg) { | |
20 … | + if (msg.value.content.type !== 'vote') return | |
21 … | + var element = api.message_layout(msg, { | |
22 … | + content: render_vote(msg), | |
23 … | + layout: 'mini' | |
24 … | + }) | |
25 … | + | |
26 … | + return api.message_decorate(element, { msg }) | |
27 … | + } | |
28 … | + | |
29 … | + function render_vote (msg) { | |
30 … | + var link = msg.value.content.vote.link | |
31 … | + return [ | |
32 … | + msg.value.content.vote.value > 0 ? 'dug' : 'undug', ' ', api.message_link(link) | |
33 … | + ] | |
34 … | + } | |
35 … | +} |
message/html/render/zz_fallback.js | ||
---|---|---|
@@ -1,0 +1,33 @@ | ||
1 … | +var h = require('mutant/h') | |
2 … | + | |
3 … | +exports.needs = { | |
4 … | + message_layout: 'first', | |
5 … | + message_decorate: 'reduce' | |
6 … | +} | |
7 … | + | |
8 … | +exports.gives = { | |
9 … | + message_render: true | |
10 … | +} | |
11 … | + | |
12 … | +exports.create = function (api) { | |
13 … | + return { | |
14 … | + message_render | |
15 … | + } | |
16 … | + | |
17 … | + function message_render (msg) { | |
18 … | + var element = api.message_layout(msg, { | |
19 … | + content: message_content(msg), | |
20 … | + layout: 'mini' | |
21 … | + }) | |
22 … | + | |
23 … | + return api.message_decorate(element, { msg }) | |
24 … | + } | |
25 … | + | |
26 … | + function message_content (msg) { | |
27 … | + if (typeof msg.value.content === 'string') { | |
28 … | + return h('code', {}, 'PRIVATE') | |
29 … | + } else { | |
30 … | + return h('code', {}, msg.value.content.type) | |
31 … | + } | |
32 … | + } | |
33 … | +} |
message/html/timestamp.js | ||
---|---|---|
@@ -1,0 +1,17 @@ | ||
1 … | +const h = require('mutant/h') | |
2 … | +const nest = require('depnest') | |
3 … | + | |
4 … | +exports.gives = nest('message.timestamp') | |
5 … | +exports.needs = nest('obs.timeAgo', 'first') | |
6 … | + | |
7 … | +exports.create = function (api) { | |
8 … | + return nest('message.timestamp', timestamp) | |
9 … | + | |
10 … | + function timestamp (msg) { | |
11 … | + return h('a.Timestamp', { | |
12 … | + href: msg.key, | |
13 … | + title: new Date(msg.value.timestamp) | |
14 … | + }, api.obs.timeAgo(msg.value.timestamp)) | |
15 … | + } | |
16 … | +} | |
17 … | + |
streams/private.js | ||
---|---|---|
@@ -1,39 +1,0 @@ | ||
1 | -const pull = require('pull-stream') | |
2 | - | |
3 | -exports.gives = { | |
4 | - streams: { | |
5 | - private: true | |
6 | - } | |
7 | -} | |
8 | - | |
9 | -exports.needs = { | |
10 | - sbot_log: 'first', | |
11 | - //message_unbox: true | |
12 | -} | |
13 | - | |
14 | -exports.create = function (api) { | |
15 | - return { | |
16 | - streams: { | |
17 | - 'private': function (opts) { | |
18 | - pull( | |
19 | - api.sbot_log(opts)//, | |
20 | - //unbox() | |
21 | - ) | |
22 | - } | |
23 | - } | |
24 | - } | |
25 | - | |
26 | - // scoped | |
27 | - | |
28 | - function unbox () { | |
29 | - return pull( | |
30 | - pull.filter(function (msg) { | |
31 | - return typeof msg.value.content === 'string' | |
32 | - }), | |
33 | - pull.map(function (msg) { | |
34 | - return api.message_unbox(msg) | |
35 | - }), | |
36 | - pull.filter(Boolean) | |
37 | - ) | |
38 | - } | |
39 | -} |
Built with git-ssb-web