git ssb

10+

Matt McKegg / patchwork



Tree: 77e7fa628b43035fa4c24ea1a9eb35529874721d

Files: 77e7fa628b43035fa4c24ea1a9eb35529874721d / modules / page / html / render / public.js

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

Built with git-ssb-web