git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: 271cbdd2add56b26f6e997f982fb76db39f59d06

Files: 271cbdd2add56b26f6e997f982fb76db39f59d06 / modules / page / html / render / profile.js

11045 bytesRaw
1var nest = require('depnest')
2var ref = require('ssb-ref')
3var {h, when, computed, map, send, dictToCollection, resolve} = require('mutant')
4
5exports.needs = nest({
6 'about.obs': {
7 name: 'first',
8 description: 'first',
9 names: 'first',
10 images: 'first',
11 color: 'first'
12 },
13 'blob.sync.url': 'first',
14 'blob.html.input': 'first',
15 'message.async.publish': 'first',
16 'message.html.markdown': 'first',
17 'about.html.image': 'first',
18 'feed.html.rollup': 'first',
19 'feed.pull.profile': 'first',
20 'sbot.async.publish': 'first',
21 'keys.sync.id': 'first',
22 'sheet.display': 'first',
23 'profile.obs.rank': 'first',
24 'profile.sheet.edit': 'first',
25 'app.navigate': 'first',
26 'profile.obs.contact': 'first',
27 'contact.html.followToggle': 'first',
28 'intl.sync.i18n': 'first',
29 'intl.sync.i18n_n': 'first',
30 'sheet.profiles': 'first'
31})
32exports.gives = nest('page.html.render')
33
34exports.create = function (api) {
35 const i18n = api.intl.sync.i18n
36 const plural = api.intl.sync.i18n_n
37 return nest('page.html.render', function profile (id) {
38 if (!ref.isFeed(id)) return
39 var yourId = api.keys.sync.id()
40 var name = api.about.obs.name(id)
41 var description = api.about.obs.description(id)
42 var contact = api.profile.obs.contact(id)
43
44 var friends = computed([contact.following, contact.followers], (following, followers) => {
45 return Array.from(following).filter(follow => followers.includes(follow))
46 })
47
48 var following = computed([contact.following, friends], (following, friends) => {
49 return following.filter(follow => !friends.includes(follow))
50 })
51
52 var followers = computed([contact.followers, friends], (followers, friends) => {
53 return followers.filter(follower => !friends.includes(follower))
54 })
55
56 var names = computed([api.about.obs.names(id), contact.yourFollowing, contact.following, yourId, id], filterByValues)
57 var images = computed([api.about.obs.images(id), contact.yourFollowing, contact.following, yourId, id], filterByValues)
58
59 var namePicker = h('div', {className: 'Picker'}, [
60 map(dictToCollection(names), (item) => {
61 var isSelf = computed(item.value, (ids) => ids.includes(id))
62 var isAssigned = computed(item.value, (ids) => ids.includes(yourId))
63 return h('a.name', {
64 'ev-click': () => {
65 if (!isAssigned()) {
66 assignName(id, resolve(item.key))
67 }
68 },
69 href: '#',
70 classList: [
71 when(isSelf, '-self'),
72 when(isAssigned, '-assigned')
73 ],
74 title: nameList(when(isSelf, i18n('Self Assigned'), i18n('Assigned By')), item.value)
75 }, [
76 item.key
77 ])
78 }),
79 h('a -add', {
80 'ev-click': () => {
81 rename(id)
82 },
83 href: '#'
84 }, ['+'])
85 ])
86
87 var imagePicker = h('div', {className: 'Picker'}, [
88 map(dictToCollection(images), (item) => {
89 var isSelf = computed(item.value, (ids) => ids.includes(id))
90 var isAssigned = computed(item.value, (ids) => ids.includes(yourId))
91 return h('a.name', {
92 'ev-click': () => {
93 if (!isAssigned()) {
94 assignImage(id, resolve(item.key))
95 }
96 },
97 href: '#',
98 classList: [
99 when(isSelf, '-self'),
100 when(isAssigned, '-assigned')
101 ],
102 title: nameList(when(isSelf, i18n('Self Assigned'), i18n('Assigned By')), item.value)
103 }, [
104 h('img', {
105 className: 'Avatar',
106 style: { 'background-color': api.about.obs.color(id) },
107 src: computed(item.key, api.blob.sync.url)
108 })
109 ])
110 }),
111 h('span.add', [
112 api.blob.html.input(file => {
113 assignImage(id, file.link)
114 }, {
115 accept: 'image/*',
116 resize: { width: 500, height: 500 }
117 })
118 ])
119 ])
120
121 var prepend = h('header', {className: 'ProfileHeader'}, [
122 h('div.image', api.about.html.image(id)),
123 h('div.main', [
124 h('div.title', [
125 h('h1', [name]),
126 h('div.meta', [
127 when(id === yourId, [
128 h('button', {'ev-click': api.profile.sheet.edit}, i18n('Edit Your Profile'))
129 ], [
130 api.contact.html.followToggle(id)
131 ])
132 ])
133 ]),
134 h('section -publicKey', [
135 h('pre', {title: i18n('Public key for this profile')}, id)
136 ]),
137
138 when(contact.notFollowing, [
139 when(contact.blockingFriendsCount, h('section -blockWarning', [
140 h('a', {
141 href: '#',
142 'ev-click': send(displayBlockingFriends, contact.blockingFriends)
143 }, [
144 '⚠️ ', computed(['This person is blocked by %s of your friends.', contact.blockingFriendsCount], plural)
145 ])
146 ])),
147
148 when(contact.noIncoming,
149 h('section -distanceWarning', [
150 h('h1', i18n(`You don't follow anyone who follows this person`)),
151 h('p', i18n('You might not be seeing their latest messages. You could try joining a pub that they are a member of.')),
152 when(contact.hasOutgoing,
153 h('p', i18n('However, since they follow someone that follows you, they should be able to see your posts.')),
154 h('p', i18n(`They might not be able to see your posts either.`))
155 )
156 ]),
157 when(contact.noOutgoing,
158 h('section -distanceWarning', [
159 h('h1', i18n('This person does not follow anyone that follows you')),
160 h('p', i18n('They might not receive your private messages or replies. You could try joining a pub that they are a member of.')),
161 h('p', i18n('However, since you follow someone that follows them, you should be able to see their latest posts.'))
162 ]),
163 when(contact.mutualFriendsCount,
164 h('section -mutualFriends', [
165 h('a', {
166 href: '#',
167 'ev-click': send(displayMutualFriends, contact.mutualFriends)
168 }, [
169 '👥 ', computed(['You share %s mutual friends with this person.', contact.mutualFriendsCount], plural)
170 ])
171 ])
172 )
173 )
174 )
175 ]),
176
177 h('section -description', [
178 computed(description, (text) => {
179 if (typeof text === 'string') {
180 return api.message.html.markdown(text)
181 }
182 })
183 ]),
184 h('section', [ namePicker, imagePicker ])
185 ])
186 ])
187
188 var feedView = api.feed.html.rollup(api.feed.pull.profile(id), {
189 prepend,
190 displayFilter: (msg) => msg.value.author === id,
191 rootFilter: (msg) => !contact.youBlock(),
192 bumpFilter: (msg) => msg.value.author === id
193 })
194
195 var container = h('div', {className: 'SplitView'}, [
196 h('div.main', [
197 feedView
198 ]),
199 h('div.side.-right', [
200 h('button PrivateMessageButton', {'ev-click': () => api.app.navigate('/private', {compose: {to: id}})}, i18n('Send Private Message')),
201 when(contact.sync,
202 h('div', [
203 renderContactBlock(i18n('Friends'), friends, contact.yourFollowing),
204 renderContactBlock(i18n('Followers'), followers, contact.yourFollowing),
205 renderContactBlock(i18n('Following'), following, contact.yourFollowing),
206 renderContactBlock(i18n('Blocked by'), contact.blockingFriends, contact.yourFollowing)
207 ]),
208 h('div', {className: 'Loading'})
209 )
210 ])
211 ])
212
213 // refresh feed (to hide all posts) when blocked
214 contact.youBlock(feedView.reload)
215
216 container.pendingUpdates = feedView.pendingUpdates
217 container.reload = feedView.reload
218 return container
219 })
220
221 function displayMutualFriends (profiles) {
222 api.sheet.profiles(profiles, i18n('Mutual Friends'))
223 }
224
225 function displayBlockingFriends (profiles) {
226 api.sheet.profiles(profiles, i18n('Blocked by'))
227 }
228
229 function renderContactBlock (title, profiles, yourFollowing) {
230 profiles = api.profile.obs.rank(profiles)
231 return [
232 when(computed(profiles, x => x.length), h('h2', title)),
233 h('div', {
234 classList: 'ProfileList'
235 }, [
236 map(profiles, (id) => {
237 var following = computed(yourFollowing, f => f.includes(id))
238 return h('a.profile', {
239 href: id,
240 classList: [
241 when(following, '-following')
242 ]
243 }, [
244 h('div.avatar', [api.about.html.image(id)]),
245 h('div.main', [
246 h('div.name', [ api.about.obs.name(id) ])
247 ])
248 ])
249 }, {
250 maxTime: 5,
251 idle: true
252 })
253 ])
254 ]
255 }
256
257 function assignImage (id, image) {
258 api.message.async.publish({
259 type: 'about',
260 about: id,
261 image
262 })
263 }
264
265 function assignName (id, name) {
266 api.message.async.publish({
267 type: 'about',
268 about: id,
269 name
270 })
271 }
272
273 function rename (id) {
274 api.sheet.display(close => {
275 var currentName = api.about.obs.name(id)
276 var input = h('input', {
277 style: {'font-size': '150%'},
278 value: currentName()
279 })
280 setTimeout(() => {
281 input.focus()
282 input.select()
283 }, 5)
284 return {
285 content: h('div', {
286 style: {
287 padding: '20px',
288 'text-align': 'center'
289 }
290 }, [
291 h('h2', {
292 style: {
293 'font-weight': 'normal'
294 }
295 }, [i18n('What whould you like to call '), h('strong', [currentName]), '?']),
296 input
297 ]),
298 footer: [
299 h('button -save', {
300 'ev-click': () => {
301 if (input.value.trim() && input.value !== currentName()) {
302 // no confirm
303 api.sbot.async.publish({
304 type: 'about',
305 about: id,
306 name: input.value.trim()
307 })
308 }
309 close()
310 }
311 }, i18n('Confirm')),
312 h('button -cancel', {
313 'ev-click': close
314 }, i18n('Cancel'))
315 ]
316 }
317 })
318 }
319
320 function nameList (prefix, ids) {
321 var items = map(ids, api.about.obs.name)
322 return computed([prefix, items], (prefix, names) => {
323 return (prefix ? (prefix + '\n') : '') + names.map((n) => `- ${n}`).join('\n')
324 })
325 }
326}
327
328function filterByValues (attributes, ...matchValues) {
329 return Object.keys(attributes).reduce((result, key) => {
330 var values = attributes[key].filter(value => {
331 return matchValues.some(matchValue => {
332 if (Array.isArray(matchValue)) {
333 return matchValue.includes(value)
334 } else {
335 return matchValue === value
336 }
337 })
338 })
339 if (values.length) {
340 result[key] = values
341 }
342 return result
343 }, {})
344}
345

Built with git-ssb-web