git ssb

16+

Dominic / patchbay



Tree: bea9630a530f26bfccd3c236c760ec44e13813b7

Files: bea9630a530f26bfccd3c236c760ec44e13813b7 / about / html / edit.js

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

Built with git-ssb-web