git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: 26d9fa8ec162c42e2fa61b44c63952e0f38b38c3

Files: 26d9fa8ec162c42e2fa61b44c63952e0f38b38c3 / modules / page / html / render / public.js

7112 bytesRaw
1var nest = require('depnest')
2var { h, send, when, computed, map } = require('mutant')
3var extend = require('xtend')
4var pull = require('pull-stream')
5
6exports.gives = nest({
7 'page.html.render': true
8})
9
10exports.needs = nest({
11 sbot: {
12 pull: {
13 log: 'first',
14 feed: 'first',
15 userFeed: 'first'
16 },
17 async: {
18 publish: 'first'
19 },
20 obs: {
21 connectedPeers: 'first',
22 localPeers: 'first'
23 }
24 },
25 'about.html.image': 'first',
26 'about.obs.name': 'first',
27 'message.html.compose': 'first',
28
29 'feed.html.rollup': 'first',
30 'profile.obs.recentlyUpdated': 'first',
31 'contact.obs.following': 'first',
32 'channel.obs': {
33 subscribed: 'first',
34 recent: 'first'
35 },
36 'keys.sync.id': 'first'
37})
38
39exports.create = function (api) {
40 return nest('page.html.render', page)
41
42 function page (path) {
43 if (path !== '/public') return // "/" is a sigil for "page"
44
45 var id = api.keys.sync.id()
46 var following = api.contact.obs.following(id)
47 var subscribedChannels = api.channel.obs.subscribed(id)
48 var loading = computed(subscribedChannels.sync, x => !x)
49 var channels = computed(api.channel.obs.recent(), items => items.slice(0, 8), {comparer: arrayEq})
50 var connectedPeers = api.sbot.obs.connectedPeers()
51 var localPeers = api.sbot.obs.localPeers()
52 var connectedPubs = computed([connectedPeers, localPeers], (c, l) => c.filter(x => !l.includes(x)))
53
54 var oldest = Date.now() - (2 * 24 * 60 * 60e3)
55 getFirstMessage(id, (_, msg) => {
56 if (msg) {
57 // fall back to timestamp stream before this, give 48 hrs for feeds to stabilize
58 if (msg.value.timestamp > oldest) {
59 oldest = Date.now()
60 }
61 }
62 })
63
64 var prepend = [
65 api.message.html.compose({ meta: { type: 'post' }, placeholder: 'Write a public message' })
66 ]
67
68 var feedView = api.feed.html.rollup(getFeed, {
69 prepend,
70 waitUntil: computed([
71 following.sync,
72 subscribedChannels.sync
73 ], (...x) => x.every(Boolean)),
74 windowSize: 500,
75 filter: (item) => {
76 return !item.boxed && (
77 id === item.author ||
78 following().has(item.author) ||
79 subscribedChannels().has(item.channel) ||
80 (item.repliesFrom && item.repliesFrom.has(id)) ||
81 item.digs && item.digs.has(id)
82 )
83 },
84 bumpFilter: (msg, group) => {
85 if (!group.message) {
86 return (
87 isMentioned(id, msg.value.content.mentions) ||
88 msg.value.author === id || (
89 fromDay(msg, group.fromTime) && (
90 following().has(msg.value.author) ||
91 group.repliesFrom.has(id)
92 )
93 )
94 )
95 }
96 return true
97 }
98 })
99
100 var result = h('div.SplitView', [
101 h('div.side', [
102 getSidebar()
103 ]),
104 h('div.main', feedView)
105 ])
106
107 result.pendingUpdates = feedView.pendingUpdates
108 result.reload = feedView.reload
109
110 return result
111
112 function getSidebar () {
113 var whoToFollow = computed([following, api.profile.obs.recentlyUpdated(200)], (following, recent) => {
114 return Array.from(recent).filter(x => x !== id && !following.has(x)).slice(0, 10)
115 })
116 return [
117 h('h2', 'Active Channels'),
118 when(loading, [ h('Loading') ]),
119 h('div', {
120 classList: 'ChannelList',
121 hidden: loading
122 }, [
123 map(channels, (channel) => {
124 var subscribed = subscribedChannels.has(channel)
125 return h('a.channel', {
126 href: `#${channel}`,
127 classList: [
128 when(subscribed, '-subscribed')
129 ]
130 }, [
131 h('span.name', '#' + channel),
132 when(subscribed,
133 h('a.-unsubscribe', {
134 'ev-click': send(unsubscribe, channel)
135 }, 'Unsubscribe'),
136 h('a.-subscribe', {
137 'ev-click': send(subscribe, channel)
138 }, 'Subscribe')
139 )
140 ])
141 }, {maxTime: 5})
142 ]),
143
144 when(computed(localPeers, x => x.length), h('h2', 'Local')),
145 h('div', {
146 classList: 'ProfileList'
147 }, [
148 map(localPeers, (id) => {
149 return h('a.profile', {
150 classList: [
151 when(computed([connectedPeers, id], (p, id) => p.includes(id)), '-connected')
152 ],
153 href: id
154 }, [
155 h('div.avatar', [api.about.html.image(id)]),
156 h('div.main', [
157 h('div.name', [ '@', api.about.obs.name(id) ])
158 ])
159 ])
160 })
161 ]),
162
163 when(computed(whoToFollow, x => x.length), h('h2', 'Who to follow')),
164 when(following.sync,
165 h('div', {
166 classList: 'ProfileList'
167 }, [
168 map(whoToFollow, (id) => {
169 return h('a.profile', {
170 href: id
171 }, [
172 h('div.avatar', [api.about.html.image(id)]),
173 h('div.main', [
174 h('div.name', [ '@', api.about.obs.name(id) ])
175 ])
176 ])
177 })
178 ]),
179 h('div', {classList: 'Loading'})
180 ),
181
182 when(computed(connectedPubs, x => x.length), h('h2', 'Connected Pubs')),
183 h('div', {
184 classList: 'ProfileList'
185 }, [
186 map(connectedPubs, (id) => {
187 return h('a.profile', {
188 classList: [ '-connected' ],
189 href: id
190 }, [
191 h('div.avatar', [api.about.html.image(id)]),
192 h('div.main', [
193 h('div.name', [ '@', api.about.obs.name(id) ])
194 ])
195 ])
196 })
197 ])
198 ]
199 }
200
201 function getFeed (opts) {
202 if (opts.lt && opts.lt < oldest) {
203 opts = extend(opts, {lt: parseInt(opts.lt, 10)})
204 return pull(
205 api.sbot.pull.feed(opts),
206 pull.map((msg) => {
207 if (msg.sync) {
208 return msg
209 } else {
210 return {key: msg.key, value: msg.value, timestamp: msg.value.timestamp}
211 }
212 })
213 )
214 } else {
215 return api.sbot.pull.log(opts)
216 }
217 }
218
219 function getFirstMessage (feedId, cb) {
220 api.sbot.pull.userFeed({id: feedId, gte: 0, limit: 1})(null, cb)
221 }
222
223 function subscribe (id) {
224 api.sbot.async.publish({
225 type: 'channel',
226 channel: id,
227 subscribed: true
228 })
229 }
230
231 function unsubscribe (id) {
232 api.sbot.async.publish({
233 type: 'channel',
234 channel: id,
235 subscribed: false
236 })
237 }
238 }
239}
240
241function isMentioned (id, list) {
242 if (Array.isArray(list)) {
243 return list.includes(id)
244 } else {
245 return false
246 }
247}
248
249function fromDay (msg, fromTime) {
250 return (fromTime - msg.timestamp) < (24 * 60 * 60e3)
251}
252
253function arrayEq (a, b) {
254 if (Array.isArray(a) && Array.isArray(b) && a.length === b.length && a !== b) {
255 return a.every((value, i) => value === b[i])
256 }
257}
258

Built with git-ssb-web