Files: c5c3dee22f06cfc53a77ee54543cfc8abb5c4a90 / modules_basic / avatar / edit.js
5712 bytesRaw
1 | |
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 |
17 | |
18 | function crop (d, cb) { |
19 | var canvas = hypercrop(h('img', {src: d})) |
20 | |
21 | return h('AvatarEditor', [ |
22 | h('header', 'Click and drag to crop your avatar.'), |
23 | canvas, |
24 | //canvas.selection, |
25 | h('section.actions', [ |
26 | h('button.cancel', {'ev-click': () => cb(new Error('canceled')) }, 'cancel'), |
27 | h('button.okay', {'ev-click': () => cb(null, canvas.selection.toDataURL()) }, 'okay') |
28 | ]) |
29 | ]) |
30 | } |
31 | |
32 | exports.needs = { |
33 | message_confirm: 'first', |
34 | sbot_blobs_add: 'first', |
35 | blob_url: 'first', |
36 | sbot_links: 'first', |
37 | avatar_name: 'first' |
38 | } |
39 | |
40 | exports.gives = { |
41 | avatar_edit: true, |
42 | mcss: true |
43 | } |
44 | |
45 | exports.create = function (api) { |
46 | return { |
47 | avatar_edit, |
48 | mcss: () => fs.readFileSync(__filename.replace(/js$/, 'mcss'), 'utf8') |
49 | } |
50 | |
51 | function avatar_edit (id) { |
52 | |
53 | var avatar = Struct({ |
54 | original: Value(visualize(new Buffer(id.substring(1), 'base64'), 256).src), |
55 | new: MutantObject() |
56 | }) |
57 | |
58 | getAvatar({links: api.sbot_links}, self_id, id, (err, _avatar) => { |
59 | if (err) return console.error(err) |
60 | //don't show user has already selected an avatar. |
61 | if(ref.isBlob(_avatar.image)) |
62 | avatar.original.set(api.blob_url(_avatar.image)) |
63 | }) |
64 | |
65 | var name = Struct({ |
66 | original: Value(api.avatar_name(id)), |
67 | new: Value() |
68 | }) |
69 | |
70 | var images = MutantArray() |
71 | pull( |
72 | api.sbot_links({dest: id, rel: 'about', values: true}), |
73 | pull.map(e => e.value.content.image), |
74 | pull.filter(e => e && 'string' == typeof e.link), |
75 | pull.unique('link'), |
76 | pull.drain(image => images.push(image) ) |
77 | ) |
78 | |
79 | var namesRecord = MutantObject() |
80 | // TODO constrain query to one name per peer? |
81 | pull( |
82 | api.sbot_links({dest: id, rel: 'about', values: true}), |
83 | pull.map(e => e.value.content.name), |
84 | pull.filter(Boolean), |
85 | pull.drain(name => { |
86 | var n = namesRecord.get(name) || 0 |
87 | namesRecord.put(name, n+1) |
88 | }) |
89 | ) |
90 | var names = dictToCollection(namesRecord) |
91 | |
92 | var lb = hyperlightbox() |
93 | |
94 | // TODO load this in, make this editable |
95 | var description = '' |
96 | |
97 | var isPossibleUpdate = computed([name.new, avatar.new], (name, avatar) => { |
98 | return name || avatar.link |
99 | }) |
100 | |
101 | var avatarSrc = computed([avatar], avatar => { |
102 | if (avatar.new.link) return api.blob_url(avatar.new.link) |
103 | else return avatar.original |
104 | }) |
105 | |
106 | var displayedName = computed([name], name => { |
107 | if (name.new) return '@'+name.new |
108 | else return name.original |
109 | }) |
110 | |
111 | return h('ProfileEdit', [ |
112 | h('section.lightbox', lb), |
113 | h('section.avatar', [ |
114 | h('section', [ |
115 | h('img', { src: avatarSrc }), |
116 | ]), |
117 | h('footer', displayedName), |
118 | ]), |
119 | h('section.description', description), |
120 | h('section.aliases', [ |
121 | h('header', 'Aliases'), |
122 | h('section.avatars', [ |
123 | h('header', 'Avatars'), |
124 | map(images, image => h('img', { |
125 | 'src': api.blob_url(image), |
126 | 'ev-click': () => avatar.new.set(image) |
127 | })), |
128 | h('div.file-upload', [ |
129 | hyperfile.asDataURL(dataUrlCallback) |
130 | ]) |
131 | ]), |
132 | h('section.names', [ |
133 | h('header', 'Names'), |
134 | h('section', [ |
135 | map(names, n => h('div', { 'ev-click': () => name.new.set(n.key()) }, [ |
136 | h('div.name', n.key), |
137 | h('div.count', n.value) |
138 | ])), |
139 | h('input', { |
140 | placeholder: ' + another name', |
141 | 'ev-keyup': e => name.new.set(e.target.value) |
142 | }) |
143 | ]) |
144 | ]), |
145 | when(isPossibleUpdate, h('section.action', [ |
146 | h('button.cancel', { 'ev-click': clearNewSelections }, 'cancel'), |
147 | h('button.confirm', { 'ev-click': handleUpdateClick }, 'confirm changes') |
148 | ])) |
149 | ]) |
150 | ]) |
151 | |
152 | function dataUrlCallback (data) { |
153 | var el = crop(data, (err, data) => { |
154 | if(data) { |
155 | var _data = dataurl.parse(data) |
156 | pull( |
157 | pull.once(_data.data), |
158 | api.sbot_blobs_add((err, hash) => { |
159 | //TODO. Alerts are EVIL. |
160 | //I use them only in a moment of weakness. |
161 | |
162 | if(err) return alert(err.stack) |
163 | avatar.new.set({ |
164 | link: hash, |
165 | size: _data.data.length, |
166 | type: _data.mimetype, |
167 | width: 512, |
168 | height: 512 |
169 | }) |
170 | }) |
171 | ) |
172 | } |
173 | lb.close() |
174 | }) |
175 | lb.show(el) |
176 | } |
177 | |
178 | function clearNewSelections () { |
179 | name.new.set(null) |
180 | avatar.new.set({}) |
181 | } |
182 | |
183 | function handleUpdateClick () { |
184 | const newName = name.new() |
185 | const newAvatar = avatar.new() |
186 | |
187 | const msg = { |
188 | type: 'about', |
189 | about: id |
190 | } |
191 | |
192 | if (newName) msg.name = newName |
193 | if (newAvatar.link) msg.image = newAvatar |
194 | |
195 | api.message_confirm(msg, (err, data) => { |
196 | if (err) return console.error(err) |
197 | |
198 | if (newName) name.original.set('@'+newName) |
199 | if (newAvatar.link) avatar.original.set(api.blob_url(newAvatar.link)) |
200 | |
201 | clearNewSelections() |
202 | |
203 | // TODO - update aliases displayed |
204 | }) |
205 | } |
206 | } |
207 | |
208 | } |
209 | |
210 |
Built with git-ssb-web