git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: 54507c122cb5f5523a63be1ede87f5111031d8df

Files: 54507c122cb5f5523a63be1ede87f5111031d8df / modules / page / html / render / public.js

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

Built with git-ssb-web