git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: b70759f872a7c4a5c73cc3413f10d221633d860c

Files: b70759f872a7c4a5c73cc3413f10d221633d860c / modules / page / html / render / profile.js

8706 bytesRaw
1var nest = require('depnest')
2var ref = require('ssb-ref')
3var {h, when, computed, map, send, dictToCollection, resolve} = require('mutant')
4var extend = require('xtend')
5
6exports.needs = nest({
7 'about.obs': {
8 name: 'first',
9 description: 'first',
10 names: 'first',
11 images: 'first',
12 color: 'first'
13 },
14 'blob.sync.url': 'first',
15 'blob.html.input': 'first',
16 'message.async.publish': 'first',
17 'message.html.markdown': 'first',
18 'about.html.image': 'first',
19 'feed.html.rollup': 'first',
20 'sbot.pull.userFeed': 'first',
21 'sbot.async.publish': 'first',
22 'keys.sync.id': 'first',
23 'sheet.display': 'first',
24 'profile.obs.rank': 'first',
25 'profile.sheet.edit': 'first',
26 'contact.obs': {
27 followers: 'first',
28 following: 'first'
29 }
30})
31exports.gives = nest('page.html.render')
32
33exports.create = function (api) {
34 return nest('page.html.render', function profile (id) {
35 if (!ref.isFeed(id)) return
36
37 var name = api.about.obs.name(id)
38 var description = api.about.obs.description(id)
39 var yourId = api.keys.sync.id()
40 var yourFollows = api.contact.obs.following(yourId)
41 var rawFollowers = api.contact.obs.followers(id)
42 var rawFollowing = api.contact.obs.following(id)
43 var friendsLoaded = computed([rawFollowers.sync, rawFollowing.sync], (...x) => x.every(Boolean))
44
45 var friends = computed([rawFollowing, rawFollowers], (following, followers) => {
46 return Array.from(following).filter(follow => followers.has(follow))
47 })
48
49 var following = computed([rawFollowing, friends], (following, friends) => {
50 return Array.from(following).filter(follow => !friends.includes(follow))
51 })
52
53 var followers = computed([rawFollowers, friends], (followers, friends) => {
54 return Array.from(followers).filter(follower => !friends.includes(follower))
55 })
56
57 var isFriends = computed([friends], function (friends) {
58 return friends.includes(yourId)
59 })
60
61 var followsYou = computed([following], function (followsYou) {
62 return followsYou.includes(yourId)
63 })
64
65 var youFollow = computed([yourFollows], function (youFollow) {
66 return youFollow.has(id)
67 })
68
69 var names = api.about.obs.names(id)
70 var images = api.about.obs.images(id)
71
72 var namePicker = h('div', {className: 'Picker'}, [
73 map(dictToCollection(names), (item) => {
74 var isSelf = computed(item.value, (ids) => ids.includes(id))
75 var isAssigned = computed(item.value, (ids) => ids.includes(yourId))
76 return h('a.name', {
77 'ev-click': () => {
78 if (!isAssigned()) {
79 assignName(id, resolve(item.key))
80 }
81 },
82 href: '#',
83 classList: [
84 when(isSelf, '-self'),
85 when(isAssigned, '-assigned')
86 ],
87 title: nameList(when(isSelf, 'Self Assigned', 'Assigned By'), item.value)
88 }, [
89 item.key
90 ])
91 }),
92 h('a -add', {
93 'ev-click': () => {
94 rename(id)
95 },
96 href: '#',
97 }, ['+'])
98 ])
99
100 var imagePicker = h('div', {className: 'Picker'}, [
101 map(dictToCollection(images), (item) => {
102 var isSelf = computed(item.value, (ids) => ids.includes(id))
103 var isAssigned = computed(item.value, (ids) => ids.includes(yourId))
104 return h('a.name', {
105 'ev-click': () => {
106 if (!isAssigned()) {
107 assignImage(id, resolve(item.key))
108 }
109 },
110 href: '#',
111 classList: [
112 when(isSelf, '-self'),
113 when(isAssigned, '-assigned')
114 ],
115 title: nameList(when(isSelf, 'Self Assigned', 'Assigned By'), item.value)
116 }, [
117 h('img', {
118 className: 'Avatar',
119 style: { 'background-color': api.about.obs.color(id) },
120 src: computed(item.key, api.blob.sync.url)
121 })
122 ])
123 }),
124 h('span.add', [
125 api.blob.html.input(file => {
126 assignImage(id, file.link)
127 }, {
128 accept: 'image/*',
129 resize: { width: 500, height: 500 }
130 })
131 ])
132 ])
133
134 var prepend = h('header', {className: 'ProfileHeader'}, [
135 h('div.image', api.about.html.image(id)),
136 h('div.main', [
137 h('div.title', [
138 h('h1', [name]),
139 h('div.meta', [
140 when(id === yourId, [
141 h('button', {'ev-click': api.profile.sheet.edit}, 'Edit Your Profile')
142 ], [
143 when(youFollow,
144 h('a.ToggleButton.-unsubscribe', {
145 'href': '#',
146 'title': 'Click to unfollow',
147 'ev-click': send(unfollow, id)
148 }, when(isFriends, 'Friends', 'Following')),
149 h('a.ToggleButton.-subscribe', {
150 'href': '#',
151 'ev-click': send(follow, id)
152 }, when(followsYou, 'Follow Back', 'Follow'))
153 )
154 ])
155 ])
156 ]),
157 h('section -description', [
158 computed(description, (text) => {
159 if (typeof text === 'string') {
160 return api.message.html.markdown(text)
161 }
162 })
163 ]),
164 h('section', [ namePicker, imagePicker ])
165 ])
166 ])
167
168 var feedView = api.feed.html.rollup((opts) => {
169 return api.sbot.pull.userFeed(extend(opts, {id}))
170 }, { prepend, getSequence: x => x.value.sequence })
171
172 var container = h('div', {className: 'SplitView'}, [
173 h('div.main', [
174 feedView
175 ]),
176 h('div.side.-right', [
177 when(friendsLoaded,
178 h('div', [
179 renderContactBlock('Friends', friends, yourFollows),
180 renderContactBlock('Followers', followers, yourFollows),
181 renderContactBlock('Following', following, yourFollows)
182 ]),
183 h('div', {className: 'Loading'})
184 )
185 ])
186 ])
187
188 container.pendingUpdates = feedView.pendingUpdates
189 container.reload = feedView.reload
190 return container
191 })
192
193 function renderContactBlock (title, profiles, yourFollows) {
194 profiles = api.profile.obs.rank(profiles)
195 return [
196 when(computed(profiles, x => x.length), h('h2', title)),
197 h('div', {
198 classList: 'ProfileList'
199 }, [
200 map(profiles, (id) => {
201 var following = computed(yourFollows, f => f.has(id))
202 return h('a.profile', {
203 href: id,
204 classList: [
205 when(following, '-following')
206 ]
207 }, [
208 h('div.avatar', [api.about.html.image(id)]),
209 h('div.main', [
210 h('div.name', [ api.about.obs.name(id) ])
211 ])
212 ])
213 }, {
214 idle: true
215 })
216 ])
217 ]
218 }
219
220 function follow (id) {
221 api.sbot.async.publish({
222 type: 'contact',
223 contact: id,
224 following: true
225 })
226 }
227
228 function unfollow (id) {
229 api.sbot.async.publish({
230 type: 'contact',
231 contact: id,
232 following: false
233 })
234 }
235
236 function assignImage (id, image) {
237 api.message.async.publish({
238 type: 'about',
239 about: id,
240 image
241 })
242 }
243
244 function assignName (id, name) {
245 api.message.async.publish({
246 type: 'about',
247 about: id,
248 name
249 })
250 }
251
252 function rename (id) {
253 api.sheet.display(close => {
254 var currentName = api.about.obs.name(id)
255 var input = h('input', {
256 style: {'font-size': '150%'},
257 value: currentName()
258 })
259 setTimeout(() => {
260 input.focus()
261 input.select()
262 }, 5)
263 return {
264 content: h('div', {
265 style: {
266 padding: '20px',
267 'text-align': 'center'
268 }
269 }, [
270 h('h2', {
271 style: {
272 'font-weight': 'normal'
273 }
274 }, ['What whould you like to call ', h('strong', [currentName]), '?']),
275 input
276 ]),
277 footer: [
278 h('button -save', {
279 'ev-click': () => {
280 if (input.value.trim() && input.value !== currentName()) {
281 // no confirm
282 api.sbot.async.publish({
283 type: 'about',
284 about: id,
285 name: input.value.trim()
286 })
287 }
288 close()
289 }
290 }, 'Confirm'),
291 h('button -cancel', {
292 'ev-click': close
293 }, 'Cancel')
294 ]
295 }
296 })
297 }
298
299 function nameList (prefix, ids) {
300 var items = map(ids, api.about.obs.name)
301 return computed([prefix, items], (prefix, names) => {
302 return (prefix ? (prefix + '\n') : '') + names.map((n) => `- ${n}`).join('\n')
303 })
304 }
305}
306

Built with git-ssb-web