git ssb

10+

Matt McKegg / patchwork



Tree: 9bb4dd6572b0113dd462049c31d372b6062f9ac5

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

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

Built with git-ssb-web