git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: 4557a7bfe93e3b06fc8154b38538b6b926f8c512

Files: 4557a7bfe93e3b06fc8154b38538b6b926f8c512 / modules / page / html / render / profile.js

8470 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 })
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),
180 renderContactBlock('Followers', followers),
181 renderContactBlock('Following', following)
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) {
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 return h('a.profile', {
202 href: id
203 }, [
204 h('div.avatar', [api.about.html.image(id)]),
205 h('div.main', [
206 h('div.name', [ api.about.obs.name(id) ])
207 ])
208 ])
209 }, {
210 idle: true
211 })
212 ])
213 ]
214 }
215
216 function follow (id) {
217 api.sbot.async.publish({
218 type: 'contact',
219 contact: id,
220 following: true
221 })
222 }
223
224 function unfollow (id) {
225 api.sbot.async.publish({
226 type: 'contact',
227 contact: id,
228 following: false
229 })
230 }
231
232 function assignImage (id, image) {
233 api.message.async.publish({
234 type: 'about',
235 about: id,
236 image
237 })
238 }
239
240 function assignName (id, name) {
241 api.message.async.publish({
242 type: 'about',
243 about: id,
244 name
245 })
246 }
247
248 function rename (id) {
249 api.sheet.display(close => {
250 var currentName = api.about.obs.name(id)
251 var input = h('input', {
252 style: {'font-size': '150%'},
253 value: currentName()
254 })
255 setTimeout(() => {
256 input.focus()
257 input.select()
258 }, 5)
259 return {
260 content: h('div', {
261 style: {
262 padding: '20px',
263 'text-align': 'center'
264 }
265 }, [
266 h('h2', {
267 style: {
268 'font-weight': 'normal'
269 }
270 }, ['What whould you like to call ', h('strong', [currentName]), '?']),
271 input
272 ]),
273 footer: [
274 h('button -save', {
275 'ev-click': () => {
276 if (input.value.trim() && input.value !== currentName()) {
277 // no confirm
278 api.sbot.async.publish({
279 type: 'about',
280 about: id,
281 name: input.value.trim()
282 })
283 }
284 close()
285 }
286 }, 'Confirm'),
287 h('button -cancel', {
288 'ev-click': close
289 }, 'Cancel')
290 ]
291 }
292 })
293 }
294
295 function nameList (prefix, ids) {
296 var items = map(ids, api.about.obs.name)
297 return computed([prefix, items], (prefix, names) => {
298 return (prefix ? (prefix + '\n') : '') + names.map((n) => `- ${n}`).join('\n')
299 })
300 }
301}
302

Built with git-ssb-web