git ssb

16+

Dominic / patchbay



Tree: f231b14ed22c7cf47457f3dc2841ebd18965086c

Files: f231b14ed22c7cf47457f3dc2841ebd18965086c / about / html / edit.js

5322 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')
11
12exports.gives = nest('about.html.edit')
13
14exports.needs = nest({
15 'about.obs': {
16 name: 'first',
17 imageUrl: 'first',
18 description: 'first'
19 },
20 'blob.sync.url': 'first',
21 'keys.sync.id': 'first',
22 'message.html.confirm': 'first',
23 'message.html.markdown': 'first',
24 sbot: {
25 'async.addBlob': 'first',
26 'pull.links': 'first'
27 }
28})
29
30exports.create = function (api) {
31 return nest({
32 'about.html.edit': edit
33 })
34
35 // TODO refactor this to use obs better
36 function edit (id) {
37 var avatar = Struct({
38 current: api.about.obs.imageUrl(id),
39 new: MutantObject()
40 })
41
42 const links = api.sbot.pull.links
43
44 var name = Struct({
45 current: api.about.obs.name(id),
46 new: Value()
47 })
48
49 // TODO use patchcores observable images + names
50 var images = MutantArray()
51 pull(
52 links({dest: id, rel: 'about', values: true}),
53 pull.map(e => e.value.content.image),
54 pull.filter(e => e && typeof e.link === 'string'),
55 pull.unique('link'),
56 pull.drain(image => images.push(image))
57 )
58
59 var namesRecord = MutantObject()
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 lb = hyperlightbox()
73
74 var isPossibleUpdate = computed([name.new, avatar.new], (name, avatar) => {
75 return name || avatar.link
76 })
77
78 var avatarSrc = computed([avatar], avatar => {
79 if (avatar.new.link) return api.blob.sync.url(avatar.new.link)
80 else return avatar.current
81 })
82
83 var displayedName = computed([name], name => {
84 if (name.new) return name.new
85 else return name.current
86 })
87
88 return h('AboutEditor', [
89 h('section.lightbox', lb),
90 h('section.avatar', [
91 h('section', [
92 h('img', { src: avatarSrc })
93 ]),
94 h('footer', displayedName)
95 ]),
96 h('section.description', computed(api.about.obs.description(id), (descr) => {
97 if (descr == null) return '' // TODO: should be in patchcore, I think...
98 return api.message.html.markdown(descr)
99 })),
100 h('section.aliases', [
101 h('header', 'Aliases'),
102 h('section.avatars', [
103 h('header', 'Avatars'),
104 map(images, image => h('img', {
105 'src': api.blob.sync.url(image.link),
106 'ev-click': () => avatar.new.set(image)
107 })),
108 h('div.file-upload', [
109 hyperfile.asDataURL(dataUrlCallback)
110 ])
111 ]),
112 h('section.names', [
113 h('header', 'Names'),
114 h('section', [
115 map(names, n => h('div', { 'ev-click': () => name.new.set(n.key()) }, [
116 h('div.name', n.key),
117 h('div.count', n.value)
118 ])),
119 h('input', {
120 placeholder: ' + another name',
121 'ev-keyup': e => name.new.set(e.target.value)
122 })
123 ])
124 ]),
125 when(isPossibleUpdate, h('section.action', [
126 h('button.cancel', { 'ev-click': clearNewSelections }, 'cancel'),
127 h('button.confirm', { 'ev-click': handleUpdateClick }, 'confirm changes')
128 ]))
129 ])
130 ])
131
132 function dataUrlCallback (data) {
133 var el = crop(data, (err, data) => {
134 if (err) throw err
135
136 if (data) {
137 var _data = dataurl.parse(data)
138
139 api.sbot.async.addBlob(pull.once(_data.data), (err, hash) => {
140 if (err) throw err // TODO check if this is safely caught by error catcher
141
142 avatar.new.set({
143 link: hash,
144 size: _data.data.length,
145 type: _data.mimetype,
146 width: 512,
147 height: 512
148 })
149 })
150 }
151 lb.close()
152 })
153 lb.show(el)
154 }
155
156 function clearNewSelections () {
157 name.new.set(null)
158 avatar.new.set({})
159 }
160
161 function handleUpdateClick () {
162 const newName = name.new()
163 const newAvatar = avatar.new()
164
165 const msg = {
166 type: 'about',
167 about: id
168 }
169
170 if (newName) msg.name = newName
171 if (newAvatar.link) msg.image = newAvatar
172
173 api.message.html.confirm(msg, (err, data) => {
174 if (err) return console.error(err)
175
176 clearNewSelections()
177
178 // TODO - update aliases displayed
179 })
180 }
181 }
182}
183
184function crop (d, cb) {
185 var canvas = hypercrop(h('img', {src: d}))
186
187 return h('AboutImageEditor', [
188 h('header', 'Click and drag to crop your avatar.'),
189 canvas,
190 // canvas.selection,
191 h('section.actions', [
192 h('button.cancel', { 'ev-click': () => cb(new Error('canceled')) }, 'cancel'),
193 h('button.okay', { 'ev-click': () => cb(null, canvas.selection.toDataURL()) }, 'okay')
194 ])
195 ])
196}
197
198

Built with git-ssb-web