git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: f80296e49e5bd1b20ac5c2df3b224d7b61c2d43d

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

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

Built with git-ssb-web