git ssb

10+

Matt McKegg / patchwork



Tree: ff68049609e2f8564d4c3ef26142370345e55d70

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

6703 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' && typeof item.value.content === 'object') {
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 function renderItem (item) {
153 if (item.type === 'message') {
154 var meta = null
155 var previousId = item.messageId
156 var replies = item.replies.slice(-4).map((msg) => {
157 var result = api.message.html.render(msg, {inContext: true, inSummary: true, previousId})
158 previousId = msg.key
159 return result
160 })
161 var renderedMessage = item.message ? api.message.html.render(item.message, {inContext: true}) : null
162 if (renderedMessage) {
163 if (item.lastUpdateType === 'reply' && item.repliesFrom.size) {
164 meta = h('div.meta', {
165 title: names(item.repliesFrom)
166 }, [
167 api.profile.html.manyPeople(item.repliesFrom), ' replied'
168 ])
169 } else if (item.lastUpdateType === 'like' && item.likes.size) {
170 meta = h('div.meta', {
171 title: names(item.likes)
172 }, [
173 api.profile.html.manyPeople(item.likes), ' like this message'
174 ])
175 }
176
177 return h('FeedEvent', [
178 meta,
179 renderedMessage,
180 when(replies.length, [
181 when(item.replies.length > replies.length || opts.partial,
182 h('a.full', {href: item.messageId}, ['View full thread'])
183 ),
184 h('div.replies', replies)
185 ])
186 ])
187 } else {
188 if (item.lastUpdateType === 'reply' && item.repliesFrom.size) {
189 meta = h('div.meta', {
190 title: names(item.repliesFrom)
191 }, [
192 api.profile.html.manyPeople(item.repliesFrom), ' replied to ', api.message.html.link(item.messageId)
193 ])
194 } else if (item.lastUpdateType === 'like' && item.likes.size) {
195 meta = h('div.meta', {
196 title: names(item.likes)
197 }, [
198 api.profile.html.manyPeople(item.likes), ' likes ', api.message.html.link(item.messageId)
199 ])
200 }
201
202 if (meta || replies.length) {
203 return h('FeedEvent', [
204 meta, h('div.replies', replies)
205 ])
206 }
207 }
208 } else if (item.type === 'follow') {
209 return h('FeedEvent -follow', [
210 h('div.meta', {
211 title: names(item.contacts)
212 }, [
213 api.profile.html.person(item.id), ' followed ', api.profile.html.manyPeople(item.contacts)
214 ])
215 ])
216 }
217
218 return h('div')
219 }
220 }
221
222 function ensureAuthor (item, cb) {
223 if (item.type === 'message' && !item.message) {
224 api.sbot.async.get(item.messageId, (_, value) => {
225 if (value) {
226 item.author = value.author
227 }
228 cb(null, item)
229 })
230 } else {
231 cb(null, item)
232 }
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