git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: ff719d8d11108e133fbe0b77d5e7229f2412dd3e

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

8761 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 'feed.pull.profile': '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(api.feed.pull.profile(id), {
169 prepend,
170 displayFilter: (msg) => msg.value.author === id,
171 bumpFilter: (msg) => msg.value.author === id,
172 })
173
174 var container = h('div', {className: 'SplitView'}, [
175 h('div.main', [
176 feedView
177 ]),
178 h('div.side.-right', [
179 when(friendsLoaded,
180 h('div', [
181 renderContactBlock('Friends', friends, yourFollows),
182 renderContactBlock('Followers', followers, yourFollows),
183 renderContactBlock('Following', following, yourFollows)
184 ]),
185 h('div', {className: 'Loading'})
186 )
187 ])
188 ])
189
190 container.pendingUpdates = feedView.pendingUpdates
191 container.reload = feedView.reload
192 return container
193 })
194
195 function renderContactBlock (title, profiles, yourFollows) {
196 profiles = api.profile.obs.rank(profiles)
197 return [
198 when(computed(profiles, x => x.length), h('h2', title)),
199 h('div', {
200 classList: 'ProfileList'
201 }, [
202 map(profiles, (id) => {
203 var following = computed(yourFollows, f => f.has(id))
204 return h('a.profile', {
205 href: id,
206 classList: [
207 when(following, '-following')
208 ]
209 }, [
210 h('div.avatar', [api.about.html.image(id)]),
211 h('div.main', [
212 h('div.name', [ api.about.obs.name(id) ])
213 ])
214 ])
215 }, {
216 maxTime: 5,
217 idle: true
218 })
219 ])
220 ]
221 }
222
223 function follow (id) {
224 api.sbot.async.publish({
225 type: 'contact',
226 contact: id,
227 following: true
228 })
229 }
230
231 function unfollow (id) {
232 api.sbot.async.publish({
233 type: 'contact',
234 contact: id,
235 following: false
236 })
237 }
238
239 function assignImage (id, image) {
240 api.message.async.publish({
241 type: 'about',
242 about: id,
243 image
244 })
245 }
246
247 function assignName (id, name) {
248 api.message.async.publish({
249 type: 'about',
250 about: id,
251 name
252 })
253 }
254
255 function rename (id) {
256 api.sheet.display(close => {
257 var currentName = api.about.obs.name(id)
258 var input = h('input', {
259 style: {'font-size': '150%'},
260 value: currentName()
261 })
262 setTimeout(() => {
263 input.focus()
264 input.select()
265 }, 5)
266 return {
267 content: h('div', {
268 style: {
269 padding: '20px',
270 'text-align': 'center'
271 }
272 }, [
273 h('h2', {
274 style: {
275 'font-weight': 'normal'
276 }
277 }, ['What whould you like to call ', h('strong', [currentName]), '?']),
278 input
279 ]),
280 footer: [
281 h('button -save', {
282 'ev-click': () => {
283 if (input.value.trim() && input.value !== currentName()) {
284 // no confirm
285 api.sbot.async.publish({
286 type: 'about',
287 about: id,
288 name: input.value.trim()
289 })
290 }
291 close()
292 }
293 }, 'Confirm'),
294 h('button -cancel', {
295 'ev-click': close
296 }, 'Cancel')
297 ]
298 }
299 })
300 }
301
302 function nameList (prefix, ids) {
303 var items = map(ids, api.about.obs.name)
304 return computed([prefix, items], (prefix, names) => {
305 return (prefix ? (prefix + '\n') : '') + names.map((n) => `- ${n}`).join('\n')
306 })
307 }
308}
309

Built with git-ssb-web