git ssb

10+

Matt McKegg / patchwork



Tree: 42cae2aa63c9ac20f1bbba02550d98ed4ec680e5

Files: 42cae2aa63c9ac20f1bbba02550d98ed4ec680e5 / modules / feed / html / rollup.js

8059 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 }
29})
30
31exports.gives = nest({
32 'feed.html': ['rollup']
33})
34
35exports.create = function (api) {
36 return nest({
37 'feed.html': { rollup }
38 })
39 function rollup (getStream, opts) {
40 var sync = Value(false)
41 var updates = Value(0)
42
43 var filter = opts && opts.filter
44 var bumpFilter = opts && opts.bumpFilter
45 var windowSize = opts && opts.windowSize
46 var waitFor = opts && opts.waitFor || true
47
48 var updateLoader = h('a Notifier -loader', {
49 href: '#',
50 'ev-click': refresh
51 }, [
52 'Show ',
53 h('strong', [updates]), ' ',
54 when(computed(updates, a => a === 1), 'update', 'updates')
55 ])
56
57 var content = Value()
58
59 var container = h('Scroller', {
60 style: { overflow: 'auto' }
61 }, [
62 h('div.wrapper', [
63 h('section.prepend', opts.prepend),
64 when(sync, null, h('Loading -large')),
65 content
66 ])
67 ])
68
69 onceTrue(waitFor, () => {
70 refresh()
71 pull(
72 getStream({old: false}),
73 pull.drain((item) => {
74 var type = item && item.value && item.value.content.type
75 if (type && type !== 'vote' && typeof item.value.content === 'object' && item.value.timestamp > twoDaysAgo()) {
76 if (item.value && item.value.author === api.keys.sync.id() && !updates() && type !== 'git-update') {
77 return refresh()
78 }
79 if (filter) {
80 var update = (item.value.content.type === 'post' && item.value.content.root) ? {
81 type: 'message',
82 messageId: item.value.content.root,
83 channel: item.value.content.channel
84 } : {
85 type: 'message',
86 author: item.value.author,
87 channel: item.value.content.channel,
88 messageId: item.key
89 }
90
91 ensureAuthor(update, (err, update) => {
92 if (!err) {
93 if (filter(update)) {
94 updates.set(updates() + 1)
95 }
96 }
97 })
98 } else {
99 updates.set(updates() + 1)
100 }
101 }
102 })
103 )
104 })
105
106 var abortLastFeed = null
107
108 var result = MutantArray([
109 when(updates, updateLoader),
110 container
111 ])
112
113 result.reload = refresh
114 result.pendingUpdates = updates
115
116 return result
117
118 // scoped
119
120 function refresh () {
121 if (abortLastFeed) {
122 abortLastFeed()
123 }
124 updates.set(0)
125 sync.set(false)
126
127 content.set(
128 h('section.content', {
129 hidden: computed(sync, s => !s)
130 })
131 )
132
133 var abortable = Abortable()
134 abortLastFeed = abortable.abort
135
136 pull(
137 api.feed.pull.summary(getStream, {windowSize, bumpFilter}, () => {
138 sync.set(true)
139 }),
140 pull.asyncMap(ensureAuthor),
141 pull.filter((item) => {
142 if (filter) {
143 return filter(item)
144 } else {
145 return true
146 }
147 }),
148 abortable,
149 Scroller(container, content(), renderItem, false, false)
150 )
151 }
152
153 function renderItem (item) {
154 if (item.type === 'message') {
155 var meta = null
156 var previousId = item.messageId
157 var replies = item.replies.slice(-4).map((msg) => {
158 var result = api.message.html.render(msg, {inContext: true, inSummary: true, previousId})
159 previousId = msg.key
160 return result
161 })
162 var renderedMessage = item.message ? api.message.html.render(item.message, {inContext: true}) : null
163 if (renderedMessage) {
164 if (item.lastUpdateType === 'reply' && item.repliesFrom.size) {
165 meta = h('div.meta', {
166 title: names(item.repliesFrom)
167 }, [
168 many(item.repliesFrom, api.profile.html.person), ' replied'
169 ])
170 } else if (item.lastUpdateType === 'like' && item.likes.size) {
171 meta = h('div.meta', {
172 title: names(item.likes)
173 }, [
174 many(item.likes, api.profile.html.person), ' liked this message'
175 ])
176 }
177
178 return h('FeedEvent', [
179 meta,
180 renderedMessage,
181 when(replies.length, [
182 when(item.replies.length > replies.length || opts.partial,
183 h('a.full', {href: item.messageId}, ['View full thread'])
184 ),
185 h('div.replies', replies)
186 ])
187 ])
188 } else {
189 if (item.lastUpdateType === 'reply' && item.repliesFrom.size) {
190 meta = h('div.meta', {
191 title: names(item.repliesFrom)
192 }, [
193 many(item.repliesFrom, api.profile.html.person), ' replied to ', api.message.html.link(item.messageId)
194 ])
195 } else if (item.lastUpdateType === 'like' && item.likes.size) {
196 meta = h('div.meta', {
197 title: names(item.likes)
198 }, [
199 many(item.likes, api.profile.html.person), ' liked ', api.message.html.link(item.messageId)
200 ])
201 }
202
203 if (meta || replies.length) {
204 return h('FeedEvent', [
205 meta, h('div.replies', replies)
206 ])
207 }
208 }
209 } else if (item.type === 'follow') {
210 return h('FeedEvent -follow', [
211 h('div.meta', {
212 title: names(item.contacts)
213 }, [
214 api.profile.html.person(item.id), ' followed ', many(item.contacts, api.profile.html.person)
215 ])
216 ])
217 } else if (item.type === 'subscribe') {
218 return h('FeedEvent -subscribe', [
219 h('div.meta', {
220 title: Array.from(item.channels).map(c => `#${c}`).join('\n')
221 }, [
222 api.profile.html.person(item.id), ' subscribed to ', many(item.channels, (channel) => {
223 return h('a', {href: `#${channel}`}, `#${channel}`)
224 })
225 ])
226 ])
227 }
228
229 return h('div')
230 }
231 }
232
233 function ensureAuthor (item, cb) {
234 if (item.type === 'message' && !item.message) {
235 api.sbot.async.get(item.messageId, (_, value) => {
236 if (value) {
237 item.author = value.author
238 }
239 cb(null, item)
240 })
241 } else {
242 cb(null, item)
243 }
244 }
245
246 function names (ids) {
247 var items = map(Array.from(ids), api.about.obs.name)
248 return computed([items], (names) => names.map((n) => `- ${n}`).join('\n'))
249 }
250}
251
252function twoDaysAgo () {
253 return Date.now() - (2 * 24 * 60 * 60 * 1000)
254}
255
256function many (ids, fn) {
257 ids = Array.from(ids)
258 var featuredIds = ids.slice(-4).reverse()
259
260 if (ids.length) {
261 if (ids.length > 4) {
262 return [
263 fn(featuredIds[0]), ', ',
264 fn(featuredIds[1]), ', ',
265 fn(featuredIds[2]), ' and ',
266 ids.length - 3, ' others'
267 ]
268 } else if (ids.length === 4) {
269 return [
270 fn(featuredIds[0]), ', ',
271 fn(featuredIds[1]), ', ',
272 fn(featuredIds[2]), ' and ',
273 fn(featuredIds[3])
274 ]
275 } else if (ids.length === 3) {
276 return [
277 fn(featuredIds[0]), ', ',
278 fn(featuredIds[1]), ' and ',
279 fn(featuredIds[2])
280 ]
281 } else if (ids.length === 2) {
282 return [
283 fn(featuredIds[0]), ' and ',
284 fn(featuredIds[1])
285 ]
286 } else {
287 return fn(featuredIds[0])
288 }
289 }
290}
291

Built with git-ssb-web