git ssb

10+

Matt McKegg / patchwork



Tree: f5bcb588c61e3a7eeab8ac67114e83109756f277

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

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

Built with git-ssb-web