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