git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: ad76660a40cc006f0ccf04dd43afa04529f1b38c

Files: ad76660a40cc006f0ccf04dd43afa04529f1b38c / modules / feed / html / rollup.js

6504 bytesRaw
1var Value = require('mutant/value')
2var when = require('mutant/when')
3var computed = require('mutant/computed')
4var h = require('mutant/h')
5var MutantArray = require('mutant/array')
6var Abortable = require('pull-abortable')
7var map = require('mutant/map')
8var pull = require('pull-stream')
9var nest = require('depnest')
10
11var onceTrue = require('mutant/once-true')
12var Scroller = require('pull-scroll')
13
14exports.needs = nest({
15 'message.html': {
16 render: 'first',
17 link: 'first'
18 },
19 'sbot.async.get': 'first',
20 'keys.sync.id': 'first',
21 'about.obs.name': 'first',
22 feed: {
23 'html.rollup': 'first',
24 'pull.summary': 'first'
25 },
26 profile: {
27 'html.person': 'first',
28 'html.manyPeople': 'first'
29 }
30})
31
32exports.gives = nest({
33 'feed.html': ['rollup']
34})
35
36exports.create = function (api) {
37 return nest({
38 'feed.html': { rollup }
39 })
40 function rollup (getStream, opts) {
41 var sync = Value(false)
42 var updates = Value(0)
43
44 var filter = opts && opts.filter
45 var bumpFilter = opts && opts.bumpFilter
46 var windowSize = opts && opts.windowSize
47 var waitFor = opts && opts.waitFor || true
48
49 var updateLoader = h('a Notifier -loader', {
50 href: '#',
51 'ev-click': refresh
52 }, [
53 'Show ',
54 h('strong', [updates]), ' ',
55 when(computed(updates, a => a === 1), 'update', 'updates')
56 ])
57
58 var content = h('section.content', {
59 hidden: computed(sync, s => !s)
60 })
61
62 var container = h('Scroller', {
63 style: { overflow: 'auto' }
64 }, [
65 h('div.wrapper', [
66 h('section.prepend', opts.prepend),
67 when(sync, null, h('Loading -large')),
68 content
69 ])
70 ])
71
72 setTimeout(refresh, 10)
73
74 onceTrue(waitFor, () => {
75 pull(
76 getStream({old: false}),
77 pull.drain((item) => {
78 var type = item && item.value && item.value.content.type
79 if (type && type !== 'vote') {
80 if (item.value && item.value.author === api.keys.sync.id() && !updates() && type !== 'git-update') {
81 return refresh()
82 }
83 if (filter) {
84 var update = (item.value.content.type === 'post' && item.value.content.root) ? {
85 type: 'message',
86 messageId: item.value.content.root,
87 channel: item.value.content.channel
88 } : {
89 type: 'message',
90 author: item.value.author,
91 channel: item.value.content.channel,
92 messageId: item.key
93 }
94
95 ensureAuthor(update, (err, update) => {
96 if (!err) {
97 if (filter(update)) {
98 updates.set(updates() + 1)
99 }
100 }
101 })
102 } else {
103 updates.set(updates() + 1)
104 }
105 }
106 })
107 )
108 })
109
110 var abortLastFeed = null
111
112 var result = MutantArray([
113 when(updates, updateLoader),
114 container
115 ])
116
117 result.reload = refresh
118 result.pendingUpdates = updates
119
120 return result
121
122 // scoped
123
124 function refresh () {
125 if (abortLastFeed) {
126 abortLastFeed()
127 }
128 updates.set(0)
129 sync.set(false)
130 content.innerHTML = ''
131
132 var abortable = Abortable()
133 abortLastFeed = abortable.abort
134
135 pull(
136 api.feed.pull.summary(getStream, {windowSize, bumpFilter}, () => {
137 sync.set(true)
138 }),
139 pull.asyncMap(ensureAuthor),
140 pull.filter((item) => {
141 if (filter) {
142 return filter(item)
143 } else {
144 return true
145 }
146 }),
147 abortable,
148 Scroller(container, content, renderItem, false, false)
149 )
150 }
151 }
152
153 function ensureAuthor (item, cb) {
154 if (item.type === 'message' && !item.message) {
155 api.sbot.async.get(item.messageId, (_, value) => {
156 if (value) {
157 item.author = value.author
158 }
159 cb(null, item)
160 })
161 } else {
162 cb(null, item)
163 }
164 }
165
166 function renderItem (item) {
167 if (item.type === 'message') {
168 var meta = null
169 var previousId = item.messageId
170 var replies = item.replies.slice(-4).map((msg) => {
171 var result = api.message.html.render(msg, {inContext: true, inSummary: true, previousId})
172 previousId = msg.key
173 return result
174 })
175 var renderedMessage = item.message ? api.message.html.render(item.message, {inContext: true}) : null
176 if (renderedMessage) {
177 if (item.lastUpdateType === 'reply' && item.repliesFrom.size) {
178 meta = h('div.meta', {
179 title: names(item.repliesFrom)
180 }, [
181 api.profile.html.manyPeople(item.repliesFrom), ' replied'
182 ])
183 } else if (item.lastUpdateType === 'dig' && item.digs.size) {
184 meta = h('div.meta', {
185 title: names(item.digs)
186 }, [
187 api.profile.html.manyPeople(item.digs), ' dug this message'
188 ])
189 }
190
191 return h('FeedEvent', [
192 meta,
193 renderedMessage,
194 when(replies.length, [
195 when(item.replies.length > replies.length,
196 h('a.full', {href: item.messageId}, ['View full thread'])
197 ),
198 h('div.replies', replies)
199 ])
200 ])
201 } else {
202 if (item.lastUpdateType === 'reply' && item.repliesFrom.size) {
203 meta = h('div.meta', {
204 title: names(item.repliesFrom)
205 }, [
206 api.profile.html.manyPeople(item.repliesFrom), ' replied to ', api.message.html.link(item.messageId)
207 ])
208 } else if (item.lastUpdateType === 'dig' && item.digs.size) {
209 meta = h('div.meta', {
210 title: names(item.digs)
211 }, [
212 api.profile.html.manyPeople(item.digs), ' dug ', api.message.html.link(item.messageId)
213 ])
214 }
215
216 if (meta || replies.length) {
217 return h('FeedEvent', [
218 meta, h('div.replies', replies)
219 ])
220 }
221 }
222 } else if (item.type === 'follow') {
223 return h('FeedEvent -follow', [
224 h('div.meta', {
225 title: names(item.contacts)
226 }, [
227 api.profile.html.person(item.id), ' followed ', api.profile.html.manyPeople(item.contacts)
228 ])
229 ])
230 }
231
232 return h('div')
233 }
234
235 function names (ids) {
236 var items = map(ids, api.about.obs.name)
237 return computed([items], (names) => names.map((n) => `- ${n}`).join('\n'))
238 }
239}
240

Built with git-ssb-web