git ssb

10+

Matt McKegg / patchwork



Tree: ff68049609e2f8564d4c3ef26142370345e55d70

Files: ff68049609e2f8564d4c3ef26142370345e55d70 / modules / page / html / render / public.js

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

Built with git-ssb-web