Files: a55c6ee357ae26944dcd50814406be4d91d90804 / modules / page / html / render / profile.js
5759 bytesRaw
1 | var nest = require('depnest') |
2 | var ref = require('ssb-ref') |
3 | var {Value, h, when, computed, map, send, dictToCollection} = require('mutant') |
4 | var extend = require('xtend') |
5 | |
6 | exports.needs = nest({ |
7 | 'about.obs': { |
8 | name: 'first', |
9 | names: 'first', |
10 | images: 'first', |
11 | color: 'first' |
12 | }, |
13 | 'blob.sync.url': 'first', |
14 | 'about.html.image': 'first', |
15 | 'feed.html.rollup': 'first', |
16 | 'sbot.pull.userFeed': 'first', |
17 | 'sbot.async.publish': 'first', |
18 | 'keys.sync.id': 'first', |
19 | 'profile.obs': { |
20 | followers: 'first', |
21 | following: 'first' |
22 | } |
23 | }) |
24 | exports.gives = nest('page.html.render') |
25 | |
26 | exports.create = function (api) { |
27 | return nest('page.html.render', function profile (id) { |
28 | if (!ref.isFeed(id)) return |
29 | |
30 | var name = api.about.obs.name(id) |
31 | var yourId = api.keys.sync.id() |
32 | var yourFollows = api.profile.obs.following(yourId) |
33 | var rawFollowers = api.profile.obs.followers(id) |
34 | var rawFollowing = api.profile.obs.following(id) |
35 | var doneWaiting = Value(false) |
36 | setTimeout(() => doneWaiting.set(true), 1e3) |
37 | var friendsLoaded = computed([rawFollowers, rawFollowing, doneWaiting], (...x) => x.every(Boolean)) |
38 | |
39 | var friends = computed([rawFollowing, rawFollowers], (following, followers) => { |
40 | return Array.from(following).filter(follow => followers.has(follow)) |
41 | }) |
42 | |
43 | var following = computed([rawFollowing, friends], (following, friends) => { |
44 | return Array.from(following).filter(follow => !friends.includes(follow)) |
45 | }) |
46 | |
47 | var followers = computed([rawFollowers, friends], (followers, friends) => { |
48 | return Array.from(followers).filter(follower => !friends.includes(follower)) |
49 | }) |
50 | |
51 | var isFriends = computed([friends], function (friends) { |
52 | return friends.includes(yourId) |
53 | }) |
54 | |
55 | var followsYou = computed([following], function (followsYou) { |
56 | return followsYou.includes(yourId) |
57 | }) |
58 | |
59 | var youFollow = computed([yourFollows], function (youFollow) { |
60 | return youFollow.has(id) |
61 | }) |
62 | |
63 | var names = api.about.obs.names(id) |
64 | var images = api.about.obs.images(id) |
65 | |
66 | var namePicker = h('div', {className: 'Picker'}, [ |
67 | map(dictToCollection(names), (item) => { |
68 | var isSelf = computed(item.value, (ids) => ids.includes(id)) |
69 | var isAssigned = computed(item.value, (ids) => ids.includes(yourId)) |
70 | return h('a.name', { |
71 | classList: [ |
72 | when(isSelf, '-self'), |
73 | when(isAssigned, '-assigned') |
74 | ], |
75 | title: nameList(when(isSelf, 'Self Assigned', 'Assigned By'), item.value) |
76 | }, [ |
77 | item.key |
78 | ]) |
79 | }) |
80 | ]) |
81 | |
82 | var imagePicker = h('div', {className: 'Picker'}, [ |
83 | map(dictToCollection(images), (item) => { |
84 | var isSelf = computed(item.value, (ids) => ids.includes(id)) |
85 | var isAssigned = computed(item.value, (ids) => ids.includes(yourId)) |
86 | return h('a.name', { |
87 | classList: [ |
88 | when(isSelf, '-self'), |
89 | when(isAssigned, '-assigned') |
90 | ], |
91 | title: nameList(when(isSelf, 'Self Assigned', 'Assigned By'), item.value) |
92 | }, [ |
93 | h('img', { |
94 | className: 'Avatar', |
95 | style: { 'background-color': api.about.obs.color(id) }, |
96 | src: computed(item.key, api.blob.sync.url) |
97 | }) |
98 | ]) |
99 | }) |
100 | ]) |
101 | |
102 | var prepend = h('header', {className: 'ProfileHeader'}, [ |
103 | h('div.image', api.about.html.image(id)), |
104 | h('div.main', [ |
105 | h('div.title', [ |
106 | h('h1', ['@', name]), |
107 | h('div.meta', [ |
108 | when(id === yourId, [ |
109 | h('a.-disabled', 'This is you!') |
110 | ], [ |
111 | when(youFollow, |
112 | h('a.ToggleButton.-unsubscribe', { |
113 | 'href': '#', |
114 | 'title': 'Click to unfollow', |
115 | 'ev-click': send(unfollow, id) |
116 | }, when(isFriends, 'Friends', 'Following')), |
117 | h('a.ToggleButton.-subscribe', { |
118 | 'href': '#', |
119 | 'ev-click': send(follow, id) |
120 | }, when(followsYou, 'Follow Back', 'Follow')) |
121 | ) |
122 | ]) |
123 | ]) |
124 | ]), |
125 | h('section', [ namePicker, imagePicker ]) |
126 | ]) |
127 | ]) |
128 | |
129 | return h('div', {className: 'SplitView'}, [ |
130 | h('div.main', [ |
131 | api.feed.html.rollup((opts) => { |
132 | return api.sbot.pull.userFeed(extend(opts, {id})) |
133 | }, { prepend }) |
134 | ]), |
135 | h('div.side.-right', [ |
136 | when(friendsLoaded, |
137 | h('div', [ |
138 | renderContactBlock('Friends', friends), |
139 | renderContactBlock('Followers', followers), |
140 | renderContactBlock('Following', following) |
141 | ]), |
142 | h('div', {className: 'Loading'}) |
143 | ) |
144 | ]) |
145 | ]) |
146 | }) |
147 | |
148 | function renderContactBlock (title, profiles) { |
149 | return [ |
150 | when(computed(profiles, x => x.length), h('h2', title)), |
151 | h('div', { |
152 | classList: 'ProfileList' |
153 | }, [ |
154 | map(profiles, (id) => { |
155 | return h('a.profile', { |
156 | href: id |
157 | }, [ |
158 | h('div.avatar', [api.about.html.image(id)]), |
159 | h('div.main', [ |
160 | h('div.name', [ '@', api.about.obs.name(id) ]) |
161 | ]) |
162 | ]) |
163 | }, { |
164 | idle: true |
165 | }) |
166 | ]) |
167 | ] |
168 | } |
169 | |
170 | function follow (id) { |
171 | api.sbot.async.publish({ |
172 | type: 'contact', |
173 | contact: id, |
174 | following: true |
175 | }) |
176 | } |
177 | |
178 | function unfollow (id) { |
179 | api.sbot.async.publish({ |
180 | type: 'contact', |
181 | contact: id, |
182 | following: false |
183 | }) |
184 | } |
185 | |
186 | function nameList (prefix, ids) { |
187 | var items = map(ids, api.about.obs.name) |
188 | return computed([prefix, items], (prefix, names) => { |
189 | return (prefix ? (prefix + '\n') : '') + names.map((n) => `- ${n}`).join('\n') |
190 | }) |
191 | } |
192 | } |
193 |
Built with git-ssb-web