git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: b3cef6b8b9f06ee8809214af5a022124d25cac40

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

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

Built with git-ssb-web