git ssb

16+

Dominic / patchbay



Tree: 8ae0df05df1185783cf0ee77113ce4bcc4531bdd

Files: 8ae0df05df1185783cf0ee77113ce4bcc4531bdd / about / html / edit.js

5697 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
15function 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
29exports.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
40exports.gives = nest('about.html.edit')
41
42exports.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