git ssb

16+

Dominic / patchbay



Tree: d7f7025973a74edf124dc7a5e5cf20aa2a013e9b

Files: d7f7025973a74edf124dc7a5e5cf20aa2a013e9b / about / html / edit.js

7159 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, Dict, Struct,
8 map, computed, when, dictToCollection, onceTrue
9} = require('mutant')
10const pull = require('pull-stream')
11const Mutual = require('ssb-mutual')
12
13exports.gives = nest('about.html.edit')
14
15exports.needs = nest({
16 'about.obs': {
17 name: 'first',
18 imageUrl: 'first',
19 description: 'first',
20 latestValue: 'first',
21 groupedValues: 'first'
22 },
23 'blob.sync.url': 'first',
24 'keys.sync.id': 'first',
25 'message.html.confirm': 'first',
26 'message.html.markdown': 'first',
27 sbot: {
28 'async.addBlob': 'first',
29 'obs.connection': 'first',
30 'pull.links': 'first'
31 }
32})
33
34exports.create = function (api) {
35 return nest({
36 'about.html.edit': edit
37 })
38
39 // TODO refactor this to use obs better
40 function edit (id) {
41 // TODO - get this to wait till the connection is present !
42
43 var isMe = api.keys.sync.id() === id
44
45 var avatar = Struct({
46 current: api.about.obs.imageUrl(id),
47 new: Dict()
48 })
49
50 const links = api.sbot.pull.links
51
52 var name = Struct({
53 current: api.about.obs.name(id),
54 new: Value()
55 })
56
57 const images = computed(api.about.obs.groupedValues(id, 'image'), Object.keys)
58
59 var namesRecord = Dict()
60 // TODO constrain query to one name per peer?
61 pull(
62 links({dest: id, rel: 'about', values: true}),
63 pull.map(e => e.value.content.name),
64 pull.filter(Boolean),
65 pull.drain(name => {
66 var n = namesRecord.get(name) || 0
67 namesRecord.put(name, n + 1)
68 })
69 )
70 var names = dictToCollection(namesRecord)
71
72 var publicWebHosting = Struct({
73 current: api.about.obs.latestValue(id, 'publicWebHosting'),
74 new: Value(api.about.obs.latestValue(id, 'publicWebHosting')())
75 })
76
77 var lightbox = hyperlightbox()
78
79 var isPossibleUpdate = computed([name.new, avatar.new, publicWebHosting.new], (name, avatar, publicWebHostingValue) => {
80 return name || avatar.link || (isMe && publicWebHostingValue !== publicWebHosting.current())
81 })
82
83 var avatarSrc = computed([avatar], avatar => {
84 if (avatar.new.link) return api.blob.sync.url(avatar.new.link)
85 return avatar.current
86 })
87
88 var displayedName = computed([name], name => {
89 if (name.new) return name.new
90 else return name.current
91 })
92
93 var balances = Dict()
94 onceTrue(api.sbot.obs.connection, sbot => {
95 if (!sbot.links) throw new Error('where ma sbot.links at?!')
96 var mutual = Mutual.init(sbot)
97 mutual.getAccountBalances(id, (err, data) => {
98 if (err) console.log(err)
99 if (data == null) return
100
101 balances.set(data)
102 })
103 })
104
105 return h('AboutEditor', [
106 lightbox,
107 h('section.avatar', [
108 h('section', [
109 h('img', { src: avatarSrc })
110 ]),
111 h('footer', displayedName)
112 ]),
113 h('section.description', computed(api.about.obs.description(id), (descr) => {
114 if (descr == null) return '' // TODO: should be in patchcore, I think...
115 return api.message.html.markdown(descr)
116 })),
117 h('section.credit', map(dictToCollection(balances), balance => {
118 return h('div', ['💰 ', balance.value, ' ', balance.key])
119 })),
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.sync.url(image),
126 'ev-click': () => avatar.new.set({ link: 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 isMe
146 ? h('section.viewer', [
147 h('header', 'Public viewers'),
148 h('section', [
149 h('span', 'Show my posts on public viewers'),
150 h('input', {
151 type: 'checkbox',
152 checked: publicWebHosting.current,
153 'ev-change': e => publicWebHosting.new.set(e.target.checked)
154 })
155 ])
156 ]) : '',
157 when(isPossibleUpdate, h('section.action', [
158 h('button.cancel', { 'ev-click': clearNewSelections }, 'cancel'),
159 h('button.confirm', { 'ev-click': handleUpdateClick }, 'confirm changes')
160 ]))
161 ])
162 ])
163
164 function dataUrlCallback (data) {
165 const cropCallback = (err, cropData) => {
166 if (err) throw err
167 if (!cropData) return lightbox.close()
168
169 var _data = dataurl.parse(cropData)
170 api.sbot.async.addBlob(pull.once(_data.data), (err, hash) => {
171 if (err) throw err // TODO check if this is safely caught by error catcher
172
173 avatar.new.set({
174 link: hash,
175 size: _data.data.length,
176 type: _data.mimetype,
177 width: 512,
178 height: 512
179 })
180 })
181 lightbox.close()
182 }
183
184 const cropEl = Crop(data, cropCallback)
185 lightbox.show(cropEl)
186 }
187
188 function Crop (data, cb) {
189 var img = h('img', {src: data})
190
191 var crop = h('div')
192
193 waitForImg()
194
195 return h('div.cropper', [
196 crop,
197 h('div.background')
198 ])
199
200 function waitForImg () {
201 // WEIRDNESS - if you invoke hypecrop before img is ready,
202 // the canvas instantiates and draws nothing
203
204 if (!img.height && !img.width) {
205 return window.setTimeout(waitForImg, 100)
206 }
207
208 var canvas = hypercrop(img)
209 crop = (
210 h('PatchProfileCrop', [
211 h('header', 'click and drag to crop your image'),
212 canvas,
213 h('section.actions', [
214 h('button', { 'ev-click': () => cb() }, 'Cancel'),
215 h('button -primary', { 'ev-click': () => cb(null, canvas.selection.toDataURL()) }, 'Okay')
216 ])
217 ])
218 )
219 }
220 }
221
222 function clearNewSelections () {
223 name.new.set(null)
224 avatar.new.set({})
225 publicWebHosting.new.set(publicWebHosting.current())
226 }
227
228 function handleUpdateClick () {
229 const newName = name.new()
230 const newAvatar = avatar.new()
231
232 const msg = {
233 type: 'about',
234 about: id
235 }
236
237 if (newName) msg.name = newName
238 if (newAvatar.link) msg.image = newAvatar
239 if (publicWebHosting.new() !== publicWebHosting.current()) msg.publicWebHosting = publicWebHosting.new()
240
241 api.message.html.confirm(msg, (err, data) => {
242 if (err) return console.error(err)
243
244 clearNewSelections()
245
246 // TODO - update aliases displayed
247 })
248 }
249 }
250}
251

Built with git-ssb-web