git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: 9a819a51f9e714d07b90b34130d7c67671c60e38

Files: 9a819a51f9e714d07b90b34130d7c67671c60e38 / modules / page / html / render / public.js

7091 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
109 return result
110
111 function getSidebar () {
112 var whoToFollow = computed([following, api.profile.obs.recentlyUpdated(200)], (following, recent) => {
113 return Array.from(recent).filter(x => x !== id && !following.has(x)).slice(0, 10)
114 })
115 return [
116 h('h2', 'Active Channels'),
117 when(loading, [ h('Loading') ]),
118 h('div', {
119 classList: 'ChannelList',
120 hidden: loading
121 }, [
122 map(channels, (channel) => {
123 var subscribed = subscribedChannels.has(channel.id)
124 return h('a.channel', {
125 href: `#${channel.id}`,
126 classList: [
127 when(subscribed, '-subscribed')
128 ]
129 }, [
130 h('span.name', '#' + channel.id),
131 when(subscribed,
132 h('a.-unsubscribe', {
133 'ev-click': send(unsubscribe, channel.id)
134 }, 'Unsubscribe'),
135 h('a.-subscribe', {
136 'ev-click': send(subscribe, channel.id)
137 }, 'Subscribe')
138 )
139 ])
140 }, {maxTime: 5})
141 ]),
142
143 when(computed(localPeers, x => x.length), h('h2', 'Local')),
144 h('div', {
145 classList: 'ProfileList'
146 }, [
147 map(localPeers, (id) => {
148 return h('a.profile', {
149 classList: [
150 when(computed([connectedPeers, id], (p, id) => p.includes(id)), '-connected')
151 ],
152 href: id
153 }, [
154 h('div.avatar', [api.about.html.image(id)]),
155 h('div.main', [
156 h('div.name', [ '@', api.about.obs.name(id) ])
157 ])
158 ])
159 })
160 ]),
161
162 when(computed(whoToFollow, x => x.length), h('h2', 'Who to follow')),
163 when(following.sync,
164 h('div', {
165 classList: 'ProfileList'
166 }, [
167 map(whoToFollow, (id) => {
168 return h('a.profile', {
169 href: id
170 }, [
171 h('div.avatar', [api.about.html.image(id)]),
172 h('div.main', [
173 h('div.name', [ '@', api.about.obs.name(id) ])
174 ])
175 ])
176 })
177 ]),
178 h('div', {classList: 'Loading'})
179 ),
180
181 when(computed(connectedPubs, x => x.length), h('h2', 'Connected Pubs')),
182 h('div', {
183 classList: 'ProfileList'
184 }, [
185 map(connectedPubs, (id) => {
186 return h('a.profile', {
187 classList: [ '-connected' ],
188 href: id
189 }, [
190 h('div.avatar', [api.about.html.image(id)]),
191 h('div.main', [
192 h('div.name', [ '@', api.about.obs.name(id) ])
193 ])
194 ])
195 })
196 ])
197 ]
198 }
199
200 function getFeed (opts) {
201 if (opts.lt && opts.lt < oldest) {
202 opts = extend(opts, {lt: parseInt(opts.lt, 10)})
203 return pull(
204 api.sbot.pull.feed(opts),
205 pull.map((msg) => {
206 if (msg.sync) {
207 return msg
208 } else {
209 return {key: msg.key, value: msg.value, timestamp: msg.value.timestamp}
210 }
211 })
212 )
213 } else {
214 return api.sbot.pull.log(opts)
215 }
216 }
217
218 function getFirstMessage (feedId, cb) {
219 api.sbot.pull.userFeed({id: feedId, gte: 0, limit: 1})(null, cb)
220 }
221
222 function subscribe (id) {
223 api.sbot.async.publish({
224 type: 'channel',
225 channel: id,
226 subscribed: true
227 })
228 }
229
230 function unsubscribe (id) {
231 api.sbot.async.publish({
232 type: 'channel',
233 channel: id,
234 subscribed: false
235 })
236 }
237 }
238}
239
240function isMentioned (id, list) {
241 if (Array.isArray(list)) {
242 return list.includes(id)
243 } else {
244 return false
245 }
246}
247
248function fromDay (msg, fromTime) {
249 return (fromTime - msg.timestamp) < (24 * 60 * 60e3)
250}
251
252function arrayEq (a, b) {
253 if (Array.isArray(a) && Array.isArray(b) && a.length === b.length && a !== b) {
254 return a.every((value, i) => value === b[i])
255 }
256}
257

Built with git-ssb-web