git ssb

10+

Matt McKegg / patchwork



Tree: 5b9d7ca9bb4a61d94b54a1fc968703c840a856d9

Files: 5b9d7ca9bb4a61d94b54a1fc968703c840a856d9 / modules / page / html / render / public.js

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

Built with git-ssb-web