Files: 8c686fb2b73c28246604759c61d6636e2812d871 / apis / thread.js
5431 bytesRaw
1 | var pull = require('pull-stream') |
2 | var ref = require('ssb-ref') |
3 | var msum = require('markdown-summary') |
4 | var sort = require('ssb-sort') |
5 | |
6 | var u = require('../util') |
7 | |
8 | var h = u.h |
9 | toUrl = u.toUrl |
10 | |
11 | function uniqueRecps (recps) { |
12 | if(!recps || !recps.length) return |
13 | recps = recps.map(function (e) { |
14 | return 'string' === typeof e ? e : e.link |
15 | }) |
16 | .filter(Boolean) |
17 | return recps.filter(function (id, i) { |
18 | return !~recps.indexOf(id, i+1) |
19 | }) |
20 | } |
21 | |
22 | function getThread(sbot, id, cb) { |
23 | pull( |
24 | sbot.query.read({ |
25 | //hack so that ssb-query filters on post but uses |
26 | //indexes for root. |
27 | query: [{$filter: { |
28 | value: { content: {root: id} } |
29 | }},{$filter: { |
30 | value: { content: {type: 'post'} } |
31 | }}] |
32 | }), |
33 | pull.collect(function (err, ary) { |
34 | if(err) return cb(err) |
35 | cb(null, ary) |
36 | }) |
37 | ) |
38 | } |
39 | |
40 | function isObject(o) { |
41 | return o && 'object' === typeof o |
42 | } |
43 | var isArray = Array.isArray |
44 | |
45 | function backlinks (sbot, id, cb) { |
46 | var likes = [], backlinks = [] |
47 | pull( |
48 | sbot.links({dest: id, values: true}), |
49 | pull.drain(function (e) { |
50 | var content = e.value.content |
51 | var vote = content.vote |
52 | if(isObject(vote) && |
53 | vote.value == 1 && vote.link == id) |
54 | likes.push(e) |
55 | else if(content.type == 'post' && isArray(content.mentions)) { |
56 | for(var i in content.mentions) { |
57 | var m = content.mentions[i] |
58 | if(m && m.link == id) { |
59 | backlinks.push(e) |
60 | return //if something links twice, don't back link it twice |
61 | } |
62 | } |
63 | } |
64 | }, function () { |
65 | cb(null, likes, backlinks) |
66 | }) |
67 | ) |
68 | } |
69 | |
70 | |
71 | module.exports = function (sbot) { |
72 | return function (opts, apply, req) { |
73 | var context = req.cookies |
74 | var since = apply.since |
75 | var tr = require('../translations')(context.lang) |
76 | return function (cb) { |
77 | var cacheTime = 0 |
78 | if(!ref.isMsg(opts.id)) |
79 | return cb(new Error('expected valid msg id as id')) |
80 | sbot.get({id:opts.id, private: true}, function (err, msg) { |
81 | if(err) return cb(err) |
82 | var data = {key: opts.id, value: msg, timestamp: msg.timestamp || Date.now() } |
83 | if(data.value.content.root) |
84 | cb(null, apply('message', data)) //just show one message |
85 | else if(data.value.content.type != 'post') |
86 | cb(null, apply('message', data)) //just show one message |
87 | else |
88 | getThread(sbot, opts.id, function (err, ary) { |
89 | ary.unshift(data) |
90 | var o = {}, cacheTime |
91 | ary = ary.filter(function (e) { |
92 | if(o[e.key]) return false |
93 | return o[e.key] = true |
94 | }) |
95 | sort(ary) |
96 | var recipients = ' ' |
97 | if(ary[0].value.content.recps) |
98 | recipients = ['div.Recipients', tr('ThreadRecipients'), |
99 | ary[0].value.content.recps.map(function (e) { |
100 | return apply('avatar', e) |
101 | })] |
102 | cb(null, |
103 | h('div.thread', |
104 | u.cacheTag(apply.toUrl('thread', opts), data.key, since), |
105 | ary[0].value.content.text && h('title', msum.title(ary[0].value.content.text)), |
106 | recipients, |
107 | h('form', {name: 'publish', method: 'POST'}, |
108 | ary.map(function (data) { |
109 | return h('div', |
110 | apply('message', data), |
111 | function (cb) { |
112 | backlinks(sbot, data.key, function (err, likes, backlinks) { |
113 | if(err) return cb(err) |
114 | |
115 | var expression = tr('Like') |
116 | cb(null, ['div.MessageExtra', |
117 | apply('publish', { |
118 | id: context.id, |
119 | suggestedRecps: data.value.author, |
120 | content: { |
121 | type: 'vote', |
122 | vote: { |
123 | link:data.key, value: 1, |
124 | expression: expression |
125 | }, |
126 | channel: data.value.content.channel |
127 | }, |
128 | name: expression + ' ' + (likes.length ? '('+likes.length+')' : '') |
129 | }), |
130 | (backlinks.length ? |
131 | ['ul.MessageBacklinks', |
132 | backlinks.map(function (e) { |
133 | return ['li', |
134 | apply('avatar', {id:e.value.author}), |
135 | ' ', |
136 | apply('messageLink', e), |
137 | ' ', |
138 | e.value.content.channel && apply('channelLink', e.value.content.channel) |
139 | ] |
140 | }) |
141 | ] : '') |
142 | ]) |
143 | }) |
144 | } |
145 | ) |
146 | }) |
147 | ), |
148 | apply('compose', { |
149 | content: { |
150 | type: 'post', |
151 | root: opts.id, |
152 | recps: uniqueRecps(ary[0].value.content.recps), |
153 | branch: sort.heads(ary) |
154 | } |
155 | }) |
156 | ) |
157 | ) |
158 | }) |
159 | }) |
160 | } |
161 | } |
162 | } |
163 | |
164 |
Built with git-ssb-web