git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: bcadbcc3325b20b2d11e736b46940303044e756c

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

6831 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 = Value()
59
60 var container = h('Scroller', {
61 style: { overflow: 'auto' }
62 }, [
63 h('div.wrapper', [
64 h('section.prepend', opts.prepend),
65 when(sync, null, h('Loading -large')),
66 content
67 ])
68 ])
69
70 onceTrue(waitFor, () => {
71 refresh()
72 pull(
73 getStream({old: false}),
74 pull.drain((item) => {
75 var type = item && item.value && item.value.content.type
76 if (type && type !== 'vote' && typeof item.value.content === 'object' && item.value.timestamp > twoDaysAgo()) {
77 if (item.value && item.value.author === api.keys.sync.id() && !updates() && type !== 'git-update') {
78 return refresh()
79 }
80 if (filter) {
81 var update = (item.value.content.type === 'post' && item.value.content.root) ? {
82 type: 'message',
83 messageId: item.value.content.root,
84 channel: item.value.content.channel
85 } : {
86 type: 'message',
87 author: item.value.author,
88 channel: item.value.content.channel,
89 messageId: item.key
90 }
91
92 ensureAuthor(update, (err, update) => {
93 if (!err) {
94 if (filter(update)) {
95 updates.set(updates() + 1)
96 }
97 }
98 })
99 } else {
100 updates.set(updates() + 1)
101 }
102 }
103 })
104 )
105 })
106
107 var abortLastFeed = null
108
109 var result = MutantArray([
110 when(updates, updateLoader),
111 container
112 ])
113
114 result.reload = refresh
115 result.pendingUpdates = updates
116
117 return result
118
119 // scoped
120
121 function refresh () {
122 if (abortLastFeed) {
123 abortLastFeed()
124 }
125 updates.set(0)
126 sync.set(false)
127
128 content.set(
129 h('section.content', {
130 hidden: computed(sync, s => !s)
131 })
132 )
133
134 var abortable = Abortable()
135 abortLastFeed = abortable.abort
136
137 pull(
138 api.feed.pull.summary(getStream, {windowSize, bumpFilter}, () => {
139 sync.set(true)
140 }),
141 pull.asyncMap(ensureAuthor),
142 pull.filter((item) => {
143 if (filter) {
144 return filter(item)
145 } else {
146 return true
147 }
148 }),
149 abortable,
150 Scroller(container, content(), renderItem, false, false)
151 )
152 }
153
154 function renderItem (item) {
155 if (item.type === 'message') {
156 var meta = null
157 var previousId = item.messageId
158 var replies = item.replies.slice(-4).map((msg) => {
159 var result = api.message.html.render(msg, {inContext: true, inSummary: true, previousId})
160 previousId = msg.key
161 return result
162 })
163 var renderedMessage = item.message ? api.message.html.render(item.message, {inContext: true}) : null
164 if (renderedMessage) {
165 if (item.lastUpdateType === 'reply' && item.repliesFrom.size) {
166 meta = h('div.meta', {
167 title: names(item.repliesFrom)
168 }, [
169 api.profile.html.manyPeople(item.repliesFrom), ' replied'
170 ])
171 } else if (item.lastUpdateType === 'like' && item.likes.size) {
172 meta = h('div.meta', {
173 title: names(item.likes)
174 }, [
175 api.profile.html.manyPeople(item.likes), ' liked this message'
176 ])
177 }
178
179 return h('FeedEvent', [
180 meta,
181 renderedMessage,
182 when(replies.length, [
183 when(item.replies.length > replies.length || opts.partial,
184 h('a.full', {href: item.messageId}, ['View full thread'])
185 ),
186 h('div.replies', replies)
187 ])
188 ])
189 } else {
190 if (item.lastUpdateType === 'reply' && item.repliesFrom.size) {
191 meta = h('div.meta', {
192 title: names(item.repliesFrom)
193 }, [
194 api.profile.html.manyPeople(item.repliesFrom), ' replied to ', api.message.html.link(item.messageId)
195 ])
196 } else if (item.lastUpdateType === 'like' && item.likes.size) {
197 meta = h('div.meta', {
198 title: names(item.likes)
199 }, [
200 api.profile.html.manyPeople(item.likes), ' liked ', api.message.html.link(item.messageId)
201 ])
202 }
203
204 if (meta || replies.length) {
205 return h('FeedEvent', [
206 meta, h('div.replies', replies)
207 ])
208 }
209 }
210 } else if (item.type === 'follow') {
211 return h('FeedEvent -follow', [
212 h('div.meta', {
213 title: names(item.contacts)
214 }, [
215 api.profile.html.person(item.id), ' followed ', api.profile.html.manyPeople(item.contacts)
216 ])
217 ])
218 }
219
220 return h('div')
221 }
222 }
223
224 function ensureAuthor (item, cb) {
225 if (item.type === 'message' && !item.message) {
226 api.sbot.async.get(item.messageId, (_, value) => {
227 if (value) {
228 item.author = value.author
229 }
230 cb(null, item)
231 })
232 } else {
233 cb(null, item)
234 }
235 }
236
237 function names (ids) {
238 var items = map(ids, api.about.obs.name)
239 return computed([items], (names) => names.map((n) => `- ${n}`).join('\n'))
240 }
241}
242
243function twoDaysAgo () {
244 return Date.now() - (2 * 24 * 60 * 60 * 1000)
245}
246

Built with git-ssb-web