git ssb

16+

Dominic / patchbay



Tree: c50d342b5fb946f181fcee2b8be330d21f37c752

Files: c50d342b5fb946f181fcee2b8be330d21f37c752 / about / html / edit.js

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

Built with git-ssb-web