git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: 0e7c0899affc876a69d822ae746c7c358f8c2906

Files: 0e7c0899affc876a69d822ae746c7c358f8c2906 / modules / page / html / render / public.js

7280 bytesRaw
1var nest = require('depnest')
2var extend = require('xtend')
3var pull = require('pull-stream')
4var normalizeChannel = require('../../../../lib/normalize-channel')
5var { h, send, when, computed, map } = require('mutant')
6
7exports.needs = nest({
8 sbot: {
9 obs: {
10 connectedPeers: 'first',
11 localPeers: 'first'
12 }
13 },
14 'sbot.pull.stream': 'first',
15 'feed.pull.public': 'first',
16 'about.html.image': 'first',
17 'about.obs.name': 'first',
18 'invite.sheet': 'first',
19
20 'message.html.compose': 'first',
21 'message.async.publish': 'first',
22 'progress.html.peer': 'first',
23
24 'feed.html.rollup': 'first',
25 'profile.obs.recentlyUpdated': 'first',
26 'contact.obs.following': 'first',
27 'channel.obs': {
28 subscribed: 'first',
29 recent: 'first'
30 },
31 'keys.sync.id': 'first',
32 'settings.obs.get': 'first'
33})
34
35exports.gives = nest({
36 'page.html.render': true
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 recentChannels = api.channel.obs.recent()
49 var loading = computed([subscribedChannels.sync, recentChannels.sync], (...args) => !args.every(Boolean))
50 var channels = computed(recentChannels, 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 getStream = (opts) => {
60 if (opts.lt != null && !opts.lt.marker) {
61 // if an lt has been specified that is not a marker, assume stream is finished
62 return pull.empty()
63 } else {
64 return api.sbot.pull.stream(sbot => sbot.patchwork.roots(extend(opts, { ids: [id] })))
65 }
66 }
67
68 var filters = api.settings.obs.get('filters')
69 var feedView = api.feed.html.rollup(getStream, {
70 prepend,
71 updateStream: api.sbot.pull.stream(sbot => sbot.patchwork.latest({ids: [id]})),
72 bumpFilter: function (msg) {
73 if (msg.value && msg.value.content && typeof msg.value.content === 'object') {
74 var type = msg.value.content.type
75 if (type === 'vote') return false
76
77 var author = msg.value.author
78 var channel = normalizeChannel(msg.value.content.channel)
79 var tagged = checkTag(msg.value.content.mentions)
80 var isSubscribed = channel ? subscribedChannels().has(channel) : false
81 return isSubscribed || id === author || following().includes(author) || tagged
82 }
83 },
84 rootFilter: function (msg) {
85 if (!filters()) return true
86 return !(filters().following && getType(msg) === 'contact')
87 },
88 waitFor: computed([
89 following.sync,
90 subscribedChannels.sync
91 ], (...x) => x.every(Boolean))
92 })
93
94 // call reload whenever filters changes (equivalent to the refresh from inside rollup)
95 filters(feedView.reload)
96
97 var result = h('div.SplitView', [
98 h('div.side', [
99 getSidebar()
100 ]),
101 h('div.main', feedView)
102 ])
103
104 result.pendingUpdates = feedView.pendingUpdates
105 result.reload = feedView.reload
106
107 return result
108
109 function checkTag (mentions) {
110 if (Array.isArray(mentions)) {
111 return mentions.some((mention) => {
112 if (mention && typeof mention.link === 'string' && mention.link.startsWith('#')) {
113 var channel = normalizeChannel(mention.link.slice(1))
114 return channel ? subscribedChannels().has(channel) : false
115 }
116 })
117 }
118 }
119
120 function getSidebar () {
121 var whoToFollow = computed([following, api.profile.obs.recentlyUpdated(), localPeers], (following, recent, peers) => {
122 return recent.filter(x => x !== id && !following.includes(x) && !peers.includes(x)).slice(0, 10)
123 })
124 return [
125 h('button -pub -full', {
126 'ev-click': api.invite.sheet
127 }, '+ Join Pub'),
128 when(loading, [ h('Loading') ], [
129 when(computed(channels, x => x.length), h('h2', 'Active Channels')),
130 h('div', {
131 classList: 'ChannelList',
132 hidden: loading
133 }, [
134 map(channels, (channel) => {
135 var subscribed = subscribedChannels.has(channel)
136 return h('a.channel', {
137 href: `#${channel}`,
138 classList: [
139 when(subscribed, '-subscribed')
140 ]
141 }, [
142 h('span.name', '#' + channel),
143 when(subscribed,
144 h('a.-unsubscribe', {
145 'ev-click': send(unsubscribe, channel)
146 }, 'Unsubscribe'),
147 h('a.-subscribe', {
148 'ev-click': send(subscribe, channel)
149 }, 'Subscribe')
150 )
151 ])
152 }, {maxTime: 5}),
153 h('a.channel -more', {href: '/channels'}, 'More Channels...')
154 ])
155 ]),
156
157 PeerList(localPeers, 'Local'),
158 PeerList(connectedPubs, 'Connected Pubs'),
159
160 when(computed(whoToFollow, x => x.length), h('h2', 'Who to follow')),
161 when(following.sync,
162 h('div', {
163 classList: 'ProfileList'
164 }, [
165 map(whoToFollow, (id) => {
166 return h('a.profile', {
167 href: id
168 }, [
169 h('div.avatar', [api.about.html.image(id)]),
170 h('div.main', [
171 h('div.name', [ api.about.obs.name(id) ])
172 ])
173 ])
174 })
175 ])
176 )
177 ]
178 }
179
180 function PeerList (ids, title) {
181 return [
182 when(computed(ids, x => x.length), h('h2', title)),
183 h('div', {
184 classList: 'ProfileList'
185 }, [
186 map(ids, (id) => {
187 var connected = computed([connectedPeers, id], (peers, id) => peers.includes(id))
188 return h('a.profile', {
189 classList: [
190 when(connected, '-connected')
191 ],
192 href: id
193 }, [
194 h('div.avatar', [api.about.html.image(id)]),
195 h('div.main', [
196 h('div.name', [ api.about.obs.name(id) ])
197 ]),
198 h('div.progress', [
199 api.progress.html.peer(id)
200 ])
201 ])
202 })
203 ])
204 ]
205 }
206
207 function subscribe (id) {
208 api.message.async.publish({
209 type: 'channel',
210 channel: id,
211 subscribed: true
212 })
213 }
214
215 function unsubscribe (id) {
216 api.message.async.publish({
217 type: 'channel',
218 channel: id,
219 subscribed: false
220 })
221 }
222 }
223}
224
225function getType (msg) {
226 return msg && msg.value && msg.value.content && msg.value.content.type
227}
228
229function arrayEq (a, b) {
230 if (Array.isArray(a) && Array.isArray(b) && a.length === b.length && a !== b) {
231 return a.every((value, i) => value === b[i])
232 }
233}
234

Built with git-ssb-web