Commit 80f4b86c51b90a08b5bad555e074dd466fe1ecd6
Merge branch 'refactor_profile' into master
Dominic Tarr committed on 1/26/2017, 5:14:54 PMParent: 43df95c4d63b7a5a52000171ed8ae86b874f3b5a
Parent: 3b0cb72e5d99d144b36ae7497086b1cc81b13384
Files changed
modules_basic/avatar/edit.js | ||
---|---|---|
@@ -1,31 +1,32 @@ | ||
1 | 1 … | |
2 | -var dataurl = require('dataurl-') | |
3 | -var hyperfile = require('hyperfile') | |
4 | -var hypercrop = require('hypercrop') | |
5 | -var hyperlightbox = require('hyperlightbox') | |
6 | -var h = require('hyperscript') | |
7 | -var pull = require('pull-stream') | |
8 | -var getAvatar = require('ssb-avatar') | |
9 | -var ref = require('ssb-ref') | |
10 | -var visualize = require('visualize-buffer') | |
11 | -var self_id = require('../../keys').id | |
2 … | +const fs = require('fs') | |
3 … | +const dataurl = require('dataurl-') | |
4 … | +const hyperfile = require('hyperfile') | |
5 … | +const hypercrop = require('hypercrop') | |
6 … | +const hyperlightbox = require('hyperlightbox') | |
7 … | +const h = require('../../h') | |
8 … | +const { | |
9 … | + Value, Array: MutantArray, Dict: MutantObject, Struct, | |
10 … | + map, computed, when, dictToCollection | |
11 … | +} = require('@mmckegg/mutant') | |
12 … | +const pull = require('pull-stream') | |
13 … | +const getAvatar = require('ssb-avatar') | |
14 … | +const ref = require('ssb-ref') | |
15 … | +const visualize = require('visualize-buffer') | |
16 … | +const self_id = require('../../keys').id | |
12 | 17 … | |
13 | 18 … | function crop (d, cb) { |
14 | 19 … | var canvas = hypercrop(h('img', {src: d})) |
15 | 20 … | |
16 | - return h('div.column.avatar_pic', | |
21 … | + return h('div.column.avatar_pic', [ | |
17 | 22 … | canvas, |
18 | 23 … | //canvas.selection, |
19 | - h('div.row.avatar_pic__controls', | |
20 | - h('button', 'okay', {onclick: function () { | |
21 | - cb(null, canvas.selection.toDataURL()) | |
22 | - }}), | |
23 | - h('button', 'cancel', {onclick: function () { | |
24 | - cb(new Error('canceled')) | |
25 | - }}) | |
26 | - ) | |
27 | - ) | |
24 … | + h('div.row.avatar_pic__controls', [ | |
25 … | + h('button', {'ev-click': () => cb(null, canvas.selection.toDataURL()) }, 'okay'), | |
26 … | + h('button', {'ev-click': () => cb(new Error('canceled')) }, 'cancel') | |
27 … | + ]) | |
28 … | + ]) | |
28 | 29 … | } |
29 | 30 … | |
30 | 31 … | exports.needs = { |
31 | 32 … | message_confirm: 'first', |
@@ -34,110 +35,164 @@ | ||
34 | 35 … | sbot_links: 'first', |
35 | 36 … | avatar_name: 'first' |
36 | 37 … | } |
37 | 38 … | |
38 | -exports.gives = 'avatar_edit' | |
39 … | +exports.gives = { | |
40 … | + avatar_edit: true, | |
41 … | + mcss: true | |
42 … | +} | |
39 | 43 … | |
40 | 44 … | exports.create = function (api) { |
41 | - return function (id) { | |
45 … | + return { | |
46 … | + avatar_edit, | |
47 … | + mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') | |
48 … | + } | |
42 | 49 … | |
43 | - var img = visualize(new Buffer(id.substring(1), 'base64'), 256) | |
44 | - img.classList.add('avatar--large') | |
50 … | + function avatar_edit (id) { | |
45 | 51 … | |
46 | - var lb = hyperlightbox() | |
47 | - var name_input = h('input', {placeholder: 'rename'}) | |
48 | - var name = api.avatar_name(id) | |
49 | - var selected = null | |
52 … | + var avatar = Struct({ | |
53 … | + original: Value(visualize(new Buffer(id.substring(1), 'base64'), 256).src), | |
54 … | + new: MutantObject() | |
55 … | + }) | |
50 | 56 … | |
51 | - getAvatar({links: api.sbot_links}, self_id, id, function (err, avatar) { | |
57 … | + getAvatar({links: api.sbot_links}, self_id, id, (err, _avatar) => { | |
52 | 58 … | if (err) return console.error(err) |
53 | 59 … | //don't show user has already selected an avatar. |
54 | - if(selected) return | |
55 | - if(ref.isBlob(avatar.image)) | |
56 | - img.src = api.blob_url(avatar.image) | |
60 … | + if(ref.isBlob(_avatar.image)) | |
61 … | + avatar.original.set(api.blob_url(_avatar.image)) | |
57 | 62 … | }) |
58 | 63 … | |
59 | - var also_pictured = h('div.profile__alsopicturedas.wrap') | |
64 … | + var name = Struct({ | |
65 … | + original: Value(api.avatar_name(id)), | |
66 … | + new: Value() | |
67 … | + }) | |
60 | 68 … | |
69 … | + var images = MutantArray() | |
61 | 70 … | pull( |
62 | 71 … | api.sbot_links({dest: id, rel: 'about', values: true}), |
63 | - pull.map(function (e) { | |
64 | - return e.value.content.image | |
65 | - }), | |
66 | - pull.filter(function (e) { | |
67 | - return e && 'string' == typeof e.link | |
68 | - }), | |
72 … | + pull.map(e => e.value.content.image), | |
73 … | + pull.filter(e => e && 'string' == typeof e.link), | |
69 | 74 … | pull.unique('link'), |
70 | - pull.drain(function (image) { | |
71 | - also_pictured.appendChild( | |
72 | - h('a', {href:'#', onclick: function (ev) { | |
73 | - ev.stopPropagation() | |
74 | - ev.preventDefault() | |
75 | - selected = image | |
76 | - img.src = api.blob_url(image.link || image) | |
77 | - }}, | |
78 | - h('img.avatar--thumbnail', {src: api.blob_url(image)}) | |
79 | - ) | |
80 | - ) | |
75 … | + pull.drain(image => images.push(image) ) | |
76 … | + ) | |
77 … | + | |
78 … | + var namesRecord = MutantObject() | |
79 … | + pull( | |
80 … | + api.sbot_links({dest: id, rel: 'about', values: true}), | |
81 … | + pull.map(e => e.value.content.name), | |
82 … | + pull.filter(Boolean), | |
83 … | + pull.drain(name => { | |
84 … | + var n = namesRecord.get(name) || 0 | |
85 … | + namesRecord.put(name, n+1) | |
81 | 86 … | }) |
82 | 87 … | ) |
88 … | + var names = dictToCollection(namesRecord) | |
83 | 89 … | |
84 | - return h('div.row.profile', | |
85 | - lb, | |
86 | - img, | |
87 | - h('div.column.profile__info', | |
88 | - h('strong', name), | |
89 | - name_input, | |
90 … | + var lb = hyperlightbox() | |
91 … | + | |
92 … | + | |
93 … | + var description = '' //TODO load this in, make this editable | |
90 | 94 … | |
91 | - hyperfile.asDataURL(function (data) { | |
92 | - var el = crop(data, function (err, data) { | |
93 | - if(data) { | |
94 | - img.src = data | |
95 | - var _data = dataurl.parse(data) | |
96 | - pull( | |
97 | - pull.once(_data.data), | |
98 | - api.sbot_blobs_add(function (err, hash) { | |
99 | - //TODO. Alerts are EVIL. | |
100 | - //I use them only in a moment of weakness. | |
95 … | + var isPossibleUpdate = computed([name.new, avatar.new], (name, avatar) => { | |
96 … | + return name || avatar.link | |
97 … | + }) | |
101 | 98 … | |
102 | - if(err) return alert(err.stack) | |
103 | - selected = { | |
104 | - link: hash, | |
105 | - size: _data.data.length, | |
106 | - type: _data.mimetype, | |
107 | - width: 512, | |
108 | - height: 512 | |
109 | - } | |
99 … | + var avatarSrc = computed([avatar], avatar => { | |
100 … | + if (avatar.new.link) return api.blob_url(avatar.new.link) | |
101 … | + else return avatar.original | |
102 … | + }) | |
110 | 103 … | |
111 | - }) | |
112 | - ) | |
113 | - } | |
114 | - lb.close() | |
115 | - }) | |
116 | - lb.show(el) | |
117 | - }), | |
118 | - h('button', 'update', {onclick: function () { | |
119 | - if(name_input.value) | |
120 | - name.textContent = name_input.value | |
104 … | + var displayedName = computed([name], name => { | |
105 … | + if (name.new) return '@'+name.new | |
106 … | + else return name.original | |
107 … | + }) | |
121 | 108 … | |
122 | - if(selected) | |
123 | - api.message_confirm({ | |
124 | - type: 'about', | |
125 | - about: id, | |
126 | - name: name_input.value || undefined, | |
127 | - image: selected | |
109 … | + return h('ProfileEdit', [ | |
110 … | + h('section.lightbox', lb), | |
111 … | + h('section.avatar', [ | |
112 … | + h('section', [ | |
113 … | + h('img', { src: avatarSrc }), | |
114 … | + ]), | |
115 … | + h('footer', displayedName), | |
116 … | + ]), | |
117 … | + h('section.description', description), | |
118 … | + h('section.aliases', [ | |
119 … | + h('header', 'Aliases'), | |
120 … | + h('section.avatars', [ | |
121 … | + h('header', 'Avatars'), | |
122 … | + map(images, image => h('img', { | |
123 … | + 'src': api.blob_url(image), | |
124 … | + 'ev-click': () => avatar.new.set(image) | |
125 … | + })), | |
126 … | + h('div.file-upload', [ | |
127 … | + hyperfile.asDataURL(dataUrlCallback) | |
128 … | + ]) | |
129 … | + ]), | |
130 … | + h('section.names', [ | |
131 … | + h('header', 'Names'), | |
132 … | + h('section', [ | |
133 … | + map(names, n => h('div', { 'ev-click': () => name.new.set(n.key()) }, [ | |
134 … | + h('div.name', n.key), | |
135 … | + h('div.count', n.value) | |
136 … | + ])), | |
137 … | + h('input', { | |
138 … | + placeholder: ' + another name', | |
139 … | + 'ev-keyup': e => name.new.set(e.target.value) | |
128 | 140 … | }) |
129 | - else if(name_input.value) //name only | |
130 | - api.message_confirm({ | |
131 | - type: 'about', | |
132 | - about: id, | |
133 | - name: name_input.value || undefined, | |
141 … | + ]) | |
142 … | + ]), | |
143 … | + when(isPossibleUpdate, h('section.action', [ | |
144 … | + h('button.cancel', { 'ev-click': handleCancelClick }, 'cancel'), | |
145 … | + h('button.confirm', { 'ev-click': handleUpdateClick }, 'confirm changes') | |
146 … | + ])) | |
147 … | + ]) | |
148 … | + ]) | |
149 … | + | |
150 … | + function dataUrlCallback (data) { | |
151 … | + var el = crop(data, (err, data) => { | |
152 … | + if(data) { | |
153 … | + var _data = dataurl.parse(data) | |
154 … | + pull( | |
155 … | + pull.once(_data.data), | |
156 … | + api.sbot_blobs_add((err, hash) => { | |
157 … | + //TODO. Alerts are EVIL. | |
158 … | + //I use them only in a moment of weakness. | |
159 … | + | |
160 … | + if(err) return alert(err.stack) | |
161 … | + avatar.new.set({ | |
162 … | + link: hash, | |
163 … | + size: _data.data.length, | |
164 … | + type: _data.mimetype, | |
165 … | + width: 512, | |
166 … | + height: 512 | |
167 … | + }) | |
134 | 168 … | }) |
135 | - else | |
136 | - //another moment of weakness | |
137 | - alert('must select a name or image') | |
138 | - }}), | |
139 | - also_pictured | |
140 | - ) | |
141 | - ) | |
169 … | + ) | |
170 … | + } | |
171 … | + lb.close() | |
172 … | + }) | |
173 … | + lb.show(el) | |
174 … | + } | |
175 … | + | |
176 … | + function handleCancelClick () { | |
177 … | + name.new.set(null) | |
178 … | + avatar.new.set({}) | |
179 … | + } | |
180 … | + | |
181 … | + function handleUpdateClick () { | |
182 … | + const newName = name.new() | |
183 … | + const newAvatar = avatar.new() | |
184 … | + | |
185 … | + const msg = { | |
186 … | + type: 'about', | |
187 … | + about: id | |
188 … | + } | |
189 … | + | |
190 … | + if (newName) msg.name = newName | |
191 … | + if (newAvatar.link) msg.image = newAvatar | |
192 … | + | |
193 … | + api.message_confirm(msg) | |
194 … | + } | |
142 | 195 … | } |
196 … | + | |
143 | 197 … | } |
198 … | + |
modules_basic/avatar/profile.js | ||
---|---|---|
@@ -1,79 +1,86 @@ | ||
1 | -var h = require('hyperscript') | |
2 | -var pull = require('pull-stream') | |
1 … | +const fs = require('fs') | |
2 … | +const h = require('../../h') | |
3 … | +const pull = require('pull-stream') | |
4 … | +const { unique, drain } = pull | |
5 … | +const { | |
6 … | + Array: MutantArray, | |
7 … | + map, computed, when, dictToCollection | |
8 … | +} = require('@mmckegg/mutant') | |
3 | 9 … | |
10 … | + | |
4 | 11 … | exports.needs = { |
5 | 12 … | avatar_image_link: 'first', |
6 | 13 … | avatar_action: 'map', |
7 | 14 … | avatar_edit: 'first', |
8 | 15 … | follows: 'first', |
9 | 16 … | followers: 'first' |
10 | 17 … | } |
11 | 18 … | |
12 | -exports.gives = 'avatar_profile' | |
13 | - | |
14 | -function streamToList(stream, el) { | |
15 | - pull( | |
16 | - stream, | |
17 | - pull.drain(function (item) { | |
18 | - if(item) el.appendChild(item) | |
19 | - }) | |
20 | - ) | |
21 | - return el | |
19 … | +exports.gives = { | |
20 … | + avatar_profile: true, | |
21 … | + mcss: true | |
22 | 22 … | } |
23 | 23 … | |
24 | 24 … | exports.create = function (api) { |
25 | - | |
26 | - function image_link (id) { | |
27 | - return api.avatar_image_link(id, 'thumbnail') | |
25 … | + return { | |
26 … | + avatar_profile, | |
27 … | + mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') | |
28 | 28 … | } |
29 | 29 … | |
30 | - return function (id) { | |
30 … | + function avatar_profile (id) { | |
31 | 31 … | |
32 | - var follows_el = h('div.profile__follows.wrap') | |
33 | - var friends_el = h('div.profile__friendss.wrap') | |
34 | - var followers_el = h('div.profile__followers.wrap') | |
35 | - var a, b | |
32 … | + var rawFollows = MutantArray() | |
33 … | + var rawFollowers = MutantArray() | |
34 … | + var friends = computed([rawFollows, rawFollowers], (follows, followers) => { | |
35 … | + return follows.filter(follow => followers.includes(follow)) | |
36 … | + }) | |
36 | 37 … | |
37 | - pull(api.follows(id), pull.unique(), pull.collect(function (err, ary) { | |
38 | - a = ary || []; next() | |
39 | - })) | |
40 | - pull(api.followers(id), pull.unique(), pull.collect(function (err, ary) { | |
41 | - b = ary || {}; next() | |
42 | - })) | |
38 … | + var follows = computed([rawFollows, friends], (follows, friends) => { | |
39 … | + return follows.filter(follow => !friends.includes(follow)) | |
40 … | + }) | |
41 … | + var followers = computed([rawFollowers, friends], (followers, friends) => { | |
42 … | + return followers.filter(follower => !friends.includes(follower)) | |
43 … | + }) | |
43 | 44 … | |
44 | - function next () { | |
45 | - if(!(a && b)) return | |
46 | - var _c = [], _a = [], _b = [] | |
47 | - | |
48 | - a.forEach(function (id) { | |
49 | - if(!~b.indexOf(id)) _a.push(id) | |
50 | - else _c.push(id) | |
51 | - }) | |
52 | - b.forEach(function (id) { | |
53 | - if(!~_c.indexOf(id)) _b.push(id) | |
54 | - }) | |
55 | - function add (ary, el) { | |
56 | - ary.forEach(function (id) { el.appendChild(image_link(id)) }) | |
57 | - } | |
58 | - | |
59 | - add(_a, follows_el) | |
60 | - add(_c, friends_el) | |
61 | - add(_b, followers_el) | |
62 | - } | |
63 | - | |
64 | - | |
65 | - return h('div.column.profile', | |
66 | - api.avatar_edit(id), | |
67 | - api.avatar_action(id), | |
68 | - h('div.profile__relationships.column', | |
69 | - h('strong', 'follows'), | |
70 | - follows_el, | |
71 | - h('strong', 'friends'), | |
72 | - friends_el, | |
73 | - h('strong', 'followers'), | |
74 | - followers_el | |
45 … | + pull( | |
46 … | + api.follows(id), | |
47 … | + unique(), | |
48 … | + drain( | |
49 … | + peer => rawFollows.push(peer), | |
50 … | + (err, data) => console.log('follows drain done', err, data) | |
75 | 51 … | ) |
76 | 52 … | ) |
53 … | + pull( | |
54 … | + api.followers(id), | |
55 … | + unique(), | |
56 … | + drain( | |
57 … | + peer => rawFollowers.push(peer), | |
58 … | + (err, data) => console.log('followers drain done', err, data) | |
59 … | + ) | |
60 … | + ) | |
61 … | + | |
62 … | + return h('Profile', [ | |
63 … | + h('section.edit', api.avatar_edit(id)), | |
64 … | + h('section.relationships', [ | |
65 … | + h('header', 'Relationships'), | |
66 … | + h('div.your-status', [ | |
67 … | + h('header', 'Your status'), | |
68 … | + h('section.action', api.avatar_action(id)) | |
69 … | + ]), | |
70 … | + h('div.friends', [ | |
71 … | + h('header', 'Friends'), | |
72 … | + h('section', map(friends, id => api.avatar_image_link(id))) | |
73 … | + ]), | |
74 … | + h('div.follows', [ | |
75 … | + h('header', 'Follows'), | |
76 … | + h('section', map(follows, id => api.avatar_image_link(id))) | |
77 … | + ]), | |
78 … | + h('div.followers', [ | |
79 … | + h('header', 'Followers'), | |
80 … | + h('section', map(followers, id => api.avatar_image_link(id))) | |
81 … | + ]) | |
82 … | + ]) | |
83 … | + ]) | |
77 | 84 … | } |
78 | 85 … | |
79 | 86 … | } |
modules_basic/avatar/edit.mcss | ||
---|---|---|
@@ -1,0 +1,161 @@ | ||
1 … | +ProfileEdit { | |
2 … | + display: flex | |
3 … | + flex-wrap: wrap | |
4 … | + justify-content: space-between | |
5 … | + | |
6 … | + margin-bottom: 2rem | |
7 … | + | |
8 … | + section.lightbox { | |
9 … | + position: absolute | |
10 … | + } | |
11 … | + | |
12 … | + section.avatar { | |
13 … | + margin-right: 1rem | |
14 … | + | |
15 … | + section img { | |
16 … | + width: 256px | |
17 … | + height: 256px | |
18 … | + } | |
19 … | + | |
20 … | + footer { | |
21 … | + font-size: 1.2rem | |
22 … | + } | |
23 … | + } | |
24 … | + | |
25 … | + section.description { | |
26 … | + flex-basis: 40% | |
27 … | + flex-grow: 1 | |
28 … | + | |
29 … | + margin-top: 1rem | |
30 … | + } | |
31 … | + | |
32 … | + section.aliases { | |
33 … | + flex-basis: 100% | |
34 … | + | |
35 … | + margin-top: 1rem | |
36 … | + | |
37 … | + header { | |
38 … | + margin-bottom: .8rem | |
39 … | + border-bottom: 1px gainsboro solid | |
40 … | + } | |
41 … | + | |
42 … | + section { | |
43 … | + display: flex | |
44 … | + flex-wrap: wrap | |
45 … | + align-content: flex-start | |
46 … | + | |
47 … | + margin-bottom: 1rem | |
48 … | + | |
49 … | + header { | |
50 … | + flex-basis: 100% | |
51 … | + | |
52 … | + font-size: .9rem | |
53 … | + $textSubtle | |
54 … | + | |
55 … | + margin-bottom: .2rem | |
56 … | + } | |
57 … | + | |
58 … | + input { | |
59 … | + } | |
60 … | + } | |
61 … | + | |
62 … | + section.avatars { | |
63 … | + img { | |
64 … | + $avatar-large | |
65 … | + margin: 0 .15rem 0.2rem 0 | |
66 … | + | |
67 … | + cursor: pointer | |
68 … | + } | |
69 … | + | |
70 … | + div.file-upload { | |
71 … | + position: relative | |
72 … | + | |
73 … | + input[type="file"] { | |
74 … | + $avatar-large | |
75 … | + color: transparent | |
76 … | + | |
77 … | + ::-webkit-file-upload-button { | |
78 … | + visibility: hidden | |
79 … | + } | |
80 … | + | |
81 … | + ::before { | |
82 … | + position: absolute | |
83 … | + | |
84 … | + background: #fff | |
85 … | + color: #666 | |
86 … | + border: 1px solid #bbb | |
87 … | + border-radius: .2rem | |
88 … | + padding: .5rem | |
89 … | + cursor: pointer | |
90 … | + | |
91 … | + margin: 0 | |
92 … | + padding: 10% 20% | |
93 … | + top: 12% | |
94 … | + left: 18% | |
95 … | + | |
96 … | + content: '+' | |
97 … | + font-size: 1.4rem | |
98 … | + | |
99 … | + outline: none | |
100 … | + white-space: nowrap | |
101 … | + -webkit-user-select: none | |
102 … | + } | |
103 … | + | |
104 … | + :active, :focus { | |
105 … | + outline: none | |
106 … | + box-shadow: none | |
107 … | + } | |
108 … | + } | |
109 … | + } | |
110 … | + } | |
111 … | + | |
112 … | + section.names { | |
113 … | + header { | |
114 … | + } | |
115 … | + | |
116 … | + section { | |
117 … | + display: flex | |
118 … | + flex-wrap: wrap | |
119 … | + | |
120 … | + div { | |
121 … | + display: flex | |
122 … | + cursor: pointer | |
123 … | + | |
124 … | + border: 1px gainsboro solid | |
125 … | + margin: 0 .4rem .5rem 0 | |
126 … | + | |
127 … | + div { padding: .3rem } | |
128 … | + | |
129 … | + div.name { | |
130 … | + border-right: 1px gainsboro solid | |
131 … | + } | |
132 … | + | |
133 … | + div.count { | |
134 … | + font-size: .9rem | |
135 … | + background-color: #eeeeee | |
136 … | + } | |
137 … | + } | |
138 … | + | |
139 … | + input { | |
140 … | + border: 1px gainsboro solid | |
141 … | + font-size: 1rem | |
142 … | + height: 1.7rem | |
143 … | + } | |
144 … | + } | |
145 … | + | |
146 … | + } | |
147 … | + | |
148 … | + section.action { | |
149 … | + button.cancel { | |
150 … | + margin-left: 0 | |
151 … | + } | |
152 … | + | |
153 … | + button.confirm { | |
154 … | + color: #fff | |
155 … | + $backgroundPrimary | |
156 … | + border: none | |
157 … | + } | |
158 … | + } | |
159 … | + } | |
160 … | +} | |
161 … | + |
modules_basic/avatar/profile.mcss | ||
---|---|---|
@@ -1,0 +1,71 @@ | ||
1 … | +Profile { | |
2 … | + | |
3 … | + section.edit { | |
4 … | + | |
5 … | + } | |
6 … | + | |
7 … | + section.relationships { | |
8 … | + header { | |
9 … | + margin-bottom: .8rem | |
10 … | + border-bottom: 1px gainsboro solid | |
11 … | + } | |
12 … | + | |
13 … | + div { | |
14 … | + display: flex | |
15 … | + flex-wrap: wrap | |
16 … | + justify-content: space-between | |
17 … | + align-content: flex-start | |
18 … | + | |
19 … | + min-height: 5rem | |
20 … | + margin-bottom: 2rem | |
21 … | + | |
22 … | + header { | |
23 … | + flex-basis: 100% | |
24 … | + | |
25 … | + $textSubtle | |
26 … | + font-size: .9rem | |
27 … | + | |
28 … | + margin-bottom: .2rem | |
29 … | + } | |
30 … | + | |
31 … | + section a { | |
32 … | + margin-right: .2rem | |
33 … | + | |
34 … | + img { $avatar-small } | |
35 … | + } | |
36 … | + } | |
37 … | + | |
38 … | + div.your-status { | |
39 … | + margin: 0 | |
40 … | + section.action { | |
41 … | + } | |
42 … | + } | |
43 … | + | |
44 … | + div.friends { | |
45 … | + section a { | |
46 … | + margin: 0 .2rem 0.2rem 0 | |
47 … | + | |
48 … | + img { | |
49 … | + $avatar-large | |
50 … | + } | |
51 … | + } | |
52 … | + } | |
53 … | + | |
54 … | + div.follows { | |
55 … | + } | |
56 … | + | |
57 … | + div.followers { | |
58 … | + } | |
59 … | + } | |
60 … | +} | |
61 … | + | |
62 … | +$avatar-large { | |
63 … | + width: 56px | |
64 … | + height: 56px | |
65 … | +} | |
66 … | + | |
67 … | +$avatar-small { | |
68 … | + width: 32px | |
69 … | + height: 32px | |
70 … | +} | |
71 … | + |
modules_basic/feed.js | ||
---|---|---|
@@ -25,8 +25,9 @@ | ||
25 | 25 … | var div = h('div.column.scroller', |
26 | 26 … | {style: {'overflow':'auto'}}, |
27 | 27 … | h('div.scroller__wrapper', |
28 | 28 … | h('div', api.avatar_profile(id)), |
29 … | + h('header', 'Activity'), | |
29 | 30 … | content |
30 | 31 … | ) |
31 | 32 … | ) |
32 | 33 … | |
@@ -46,9 +47,9 @@ | ||
46 | 47 … | u.next(api.sbot_user_feed, { |
47 | 48 … | id: id, reverse: true, |
48 | 49 … | limit: 50, live: false |
49 | 50 … | }, ['value', 'sequence']), |
50 | - pull.through(console.log.bind(console)), | |
51 … | + // pull.through(console.log.bind(console)), | |
51 | 52 … | Scroller(div, content, api.message_render, false, false) |
52 | 53 … | ) |
53 | 54 … | |
54 | 55 … | return div |
modules_basic/follow.js | ||
---|---|---|
@@ -1,5 +1,6 @@ | ||
1 | -var h = require('hyperscript') | |
1 … | +const fs = require('fs') | |
2 … | +const h = require('../h') | |
2 | 3 … | |
3 | 4 … | //render a message when someone follows someone, |
4 | 5 … | //so you see new users |
5 | 6 … | function isRelated(value, name) { |
@@ -17,74 +18,92 @@ | ||
17 | 18 … | exports.gives = { |
18 | 19 … | message_content: true, |
19 | 20 … | message_content_mini: true, |
20 | 21 … | avatar_action: true, |
22 … | + mcss: true | |
21 | 23 … | } |
22 | 24 … | |
23 | 25 … | exports.create = function (api) { |
24 | - var exports = {} | |
25 | - exports.message_content = | |
26 | - exports.message_content_mini = function (msg) { | |
27 | - var content = msg.value.content | |
28 | - if(content.type == 'contact' && content.contact) { | |
29 | - var relation = isRelated(content.following, 'follows') | |
30 | - if(content.blocking) relation = 'blocks' | |
26 … | + return { | |
27 … | + message_content_mini, | |
28 … | + message_content, | |
29 … | + avatar_action, | |
30 … | + mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') | |
31 … | + } | |
32 … | + | |
33 … | + function message_content_mini (msg) { | |
34 … | + const { type, contact, following, blocking } = msg.value.content | |
35 … | + if(type == 'contact' && contact) { | |
36 … | + var relation = isRelated(following, 'follows') | |
37 … | + if(blocking) relation = 'blocks' | |
31 | 38 … | return [ |
32 | - relation, ' ', | |
33 | - api.avatar_link(content.contact, api.avatar_name(content.contact), '') | |
39 … | + relation, | |
40 … | + ' ', | |
41 … | + api.avatar_link(contact, api.avatar_name(contact), '') | |
34 | 42 … | ] |
35 | 43 … | } |
36 | 44 … | } |
37 | 45 … | |
38 | - exports.message_content = function (msg) { | |
39 | - | |
40 | - var content = msg.value.content | |
41 | - if(content.type == 'contact' && content.contact) { | |
42 | - var relation = isRelated(content.following, 'follows') | |
43 | - if(content.blocking) relation = 'blocks' | |
44 | - return h('div.contact', relation, api.avatar(msg.value.content.contact, 'thumbnail')) | |
46 … | + function message_content (msg) { | |
47 … | + const { type, contact, following, blocking } = msg.value.content | |
48 … | + if(type == 'contact' && contact) { | |
49 … | + var relation = isRelated(following, 'follows') | |
50 … | + if(blocking) relation = 'blocks' | |
51 … | + return h('div.contact', [ | |
52 … | + relation, | |
53 … | + api.avatar(contact, 'thumbnail') | |
54 … | + ]) | |
45 | 55 … | } |
46 | 56 … | } |
47 | 57 … | |
48 | - exports.avatar_action = function (id) { | |
58 … | + function avatar_action (id) { | |
49 | 59 … | var follows_you, you_follow |
50 | 60 … | |
51 | 61 … | var self_id = require('../keys').id |
52 | - api.follower_of(self_id, id, function (err, f) { | |
62 … | + api.follower_of(self_id, id, (err, f) => { | |
53 | 63 … | you_follow = f |
54 | 64 … | update() |
55 | 65 … | }) |
56 | - api.follower_of(id, self_id, function (err, f) { | |
66 … | + api.follower_of(id, self_id, (err, f) => { | |
57 | 67 … | follows_you = f |
58 | 68 … | update() |
59 | 69 … | }) |
60 | 70 … | |
61 | - var state = h('label') | |
62 | - var label = h('span') | |
71 … | + var followBtn = h('button', { 'ev-click': toggleFollow }, 'loading') | |
72 … | + var state = h('label', 'loading') | |
63 | 73 … | |
64 | 74 … | function update () { |
65 | 75 … | state.textContent = ( |
66 | - follows_you && you_follow ? 'friend' | |
67 | - : follows_you ? 'follows you' | |
68 | - : you_follow ? 'you follow' | |
76 … | + follows_you && you_follow ? '- you are friends' | |
77 … | + : follows_you ? '- they follow you' | |
78 … | + : you_follow ? '- you are following' | |
69 | 79 … | : '' |
70 | 80 … | ) |
81 … | + | |
82 … | + if (you_follow === undefined) return | |
83 … | + followBtn.textContent = you_follow ? 'unfollow' : 'follow' | |
84 … | + } | |
71 | 85 … | |
72 | - label.textContent = you_follow ? 'unfollow' : 'follow' | |
86 … | + return h('Follow', [ | |
87 … | + followBtn, | |
88 … | + state | |
89 … | + ]) | |
90 … | + | |
91 … | + function toggleFollow () { | |
92 … | + if (followBtn.textContent === 'loading') return | |
93 … | + const msg = { | |
94 … | + type: 'contact', | |
95 … | + contact: id, | |
96 … | + following: !you_follow | |
97 … | + } | |
98 … | + | |
99 … | + api.message_confirm(msg, (err, msg) => { | |
100 … | + if (err) return console.error(err) | |
101 … | + | |
102 … | + you_follow = msg.value.content.following | |
103 … | + update() | |
104 … | + }) | |
73 | 105 … | } |
74 | - | |
75 | - return h('div', state, | |
76 | - h('a', {href:'#', onclick: function () { | |
77 | - api.message_confirm({ | |
78 | - type: 'contact', | |
79 | - contact: id, | |
80 | - following: !you_follow | |
81 | - }, function (err, msg) { | |
82 | - if (err) return console.error(err) | |
83 | - you_follow = msg.value.content.following | |
84 | - update() | |
85 | - }) | |
86 | - }}, h('br'), label) | |
87 | - ) | |
106 … | + | |
88 | 107 … | } |
89 | 108 … | return exports |
90 | 109 … | } |
Built with git-ssb-web