Files: 90dfad001a91b412a745a9cad0dccf4a3ef49dc2 / mvd / views.js
18062 bytesRaw
1 | var {pull, filter} = require('pull-stream') |
2 | var para = require('pull-paramap') |
3 | var human = require('human-time') |
4 | var sbot = require('./scuttlebot') |
5 | var hyperscroll = require('hyperscroll') |
6 | var hyperfile = require('hyperfile') |
7 | var dataurl = require('dataurl-') |
8 | var More = require('pull-more') |
9 | var stream = require('hyperloadmore/stream') |
10 | var h = require('hyperscript') |
11 | var render = require('./render') |
12 | var ref = require('ssb-ref') |
13 | var client = require('ssb-client') |
14 | var Next = require('pull-next-query') |
15 | var config = require('./config')() |
16 | var tools = require('./tools') |
17 | var avatar = require('./avatar') |
18 | var id = require('./keys').id |
19 | var ssbKeys = require('ssb-keys') |
20 | var keys = require('./keys') |
21 | var compose = require('./compose') |
22 | |
23 | |
24 | var labelStream = function (label){ |
25 | var content = h('div.content') |
26 | var screen = document.getElementById('screen') |
27 | screen.appendChild(hyperscroll(content)) |
28 | content.appendChild(h('div.breadcrumbs.message', h('a', {href: '/'}, 'label'), ' ⯈ ' , h('a', {href: '/#label/' + label}, label))) |
29 | function createStream (opts) { |
30 | return pull( |
31 | Next(sbot.query, opts, ['value', 'timestamp']), |
32 | pull.map(function (msg){ |
33 | if (msg.value) { |
34 | sbot.get(msg.value.content.link, function (err, data) { |
35 | if (data) { |
36 | var message = {} |
37 | message.value = data |
38 | message.key = msg.value.content.link |
39 | content.appendChild(render(message)) |
40 | } |
41 | }) |
42 | } |
43 | }) |
44 | ) |
45 | } |
46 | |
47 | pull( |
48 | createStream({ |
49 | limit: 10, |
50 | reverse: true, |
51 | live: false, |
52 | query: [{$filter: { value: { content: {type: 'label', label: label }, timestamp: { $gt: 0 }}}}] |
53 | }), |
54 | stream.bottom(content) |
55 | ) |
56 | |
57 | pull( |
58 | createStream({ |
59 | limit: 10, |
60 | old: false, |
61 | live: true, |
62 | query: [{$filter: { value: { content: {type: 'label', label: label }, timestamp: { $gt: 0 }}}}] |
63 | }), |
64 | stream.top(content) |
65 | ) |
66 | } |
67 | |
68 | |
69 | var privateStream = function () { |
70 | var screen = document.getElementById('screen') |
71 | var content = h('div.content') |
72 | |
73 | screen.appendChild(hyperscroll(content)) |
74 | |
75 | function createStream (opts) { |
76 | return pull( |
77 | Next(sbot.query, opts, ['value', 'timestamp']), |
78 | pull.filter(function (msg) { |
79 | return ((msg.value.private == true) || ('string' == typeof msg.value.content)) |
80 | }), |
81 | pull.map(function (msg) { |
82 | return render(msg) |
83 | }) |
84 | ) |
85 | } |
86 | |
87 | pull( |
88 | createStream({ |
89 | limit: 100, |
90 | reverse: true, |
91 | live: false, |
92 | query: [ |
93 | { |
94 | $filter: { |
95 | value: { |
96 | content: {type: 'post'}, // only exists in posts i can read |
97 | timestamp: { |
98 | $gt: 0 |
99 | } |
100 | } |
101 | } |
102 | }] |
103 | }), |
104 | stream.bottom(content) |
105 | ) |
106 | |
107 | pull( |
108 | createStream({ |
109 | limit: 100, |
110 | old: false, |
111 | live: true, |
112 | query: [{$filter: { value: { content: {type: 'post'}, timestamp: { $gt: 0 }}}}] |
113 | }), |
114 | stream.top(content) |
115 | ) |
116 | } |
117 | |
118 | var queueStream = function () { |
119 | var content = h('div.content') |
120 | var screen = document.getElementById('screen') |
121 | screen.appendChild(hyperscroll(content)) |
122 | |
123 | pull( |
124 | sbot.query({query: [{$filter: { value: {author: id, content: {type: 'queue'}}}}]}), |
125 | pull.drain(function (msg) { |
126 | if (msg.value) { |
127 | if (ref.isMsg(msg.value.content.message)) { |
128 | if (msg.value.content.queue == true) { |
129 | sbot.get(msg.value.content.message, function (err, data) { |
130 | if (data) { |
131 | var message = {} |
132 | message.value = data |
133 | message.key = msg.value.content.message |
134 | content.appendChild(render(message)) |
135 | } |
136 | }) |
137 | } |
138 | if (msg.value.content.queue == false) { |
139 | setTimeout(function () { |
140 | var gotIt = document.getElementById(msg.value.content.message.substring(0,44)) |
141 | if (gotIt != null) { |
142 | gotIt.outerHTML = '' |
143 | } |
144 | }, 100) |
145 | } |
146 | } |
147 | } |
148 | }) |
149 | ) |
150 | } |
151 | |
152 | var mentionsStream = function (src) { |
153 | var content = h('div.content') |
154 | |
155 | var screen = document.getElementById('screen') |
156 | |
157 | screen.appendChild(hyperscroll(content)) |
158 | |
159 | function createStream (opts) { |
160 | return pull( |
161 | Next(sbot.backlinks, opts, ['value', 'timestamp']), |
162 | pull.map(function (msg) { |
163 | if (msg.value.private == true) return h('div.private') |
164 | return render(msg) |
165 | }) |
166 | ) |
167 | } |
168 | |
169 | pull( |
170 | createStream({ |
171 | limit: 10, |
172 | reverse: true, |
173 | index: 'DTA', |
174 | live: false, |
175 | query: [{$filter: {dest: src}}] |
176 | }), |
177 | stream.bottom(content) |
178 | ) |
179 | |
180 | pull( |
181 | createStream({ |
182 | limit: 10, |
183 | old: false, |
184 | index: 'DTA', |
185 | live: true, |
186 | query: [{$filter: {dest: src}}] |
187 | }), |
188 | stream.top(content) |
189 | ) |
190 | } |
191 | |
192 | var userStream = function (src) { |
193 | var content = h('div.content') |
194 | var screen = document.getElementById('screen') |
195 | |
196 | screen.appendChild(hyperscroll(content)) |
197 | |
198 | function createStream (opts) { |
199 | return pull( |
200 | More(sbot.userStream, opts, ['value', 'sequence']), |
201 | pull.map(function (msg) { |
202 | return render(h('div', msg)) |
203 | }) |
204 | ) |
205 | } |
206 | |
207 | pull( |
208 | createStream({old: false, limit: 10, id: src}), |
209 | stream.top(content) |
210 | ) |
211 | |
212 | pull( |
213 | createStream({reverse: true, live: false, limit: 10, id: src}), |
214 | stream.bottom(content) |
215 | ) |
216 | |
217 | var profile = h('div.content#profile', h('div.message')) |
218 | |
219 | if (screen.firstChild.firstChild) { |
220 | screen.firstChild.insertBefore(profile, screen.firstChild.firstChild) |
221 | } else { |
222 | screen.firstChild.appendChild(profile) |
223 | } |
224 | |
225 | var name = avatar.name(src) |
226 | |
227 | var editname = h('span', |
228 | avatar.name(src), |
229 | h('button.btn', 'New name', { |
230 | onclick: function () { |
231 | var nameput = h('input', {placeholder: name.textContent}) |
232 | var nameedit = |
233 | h('span', nameput, |
234 | h('button.btn', 'Preview', { |
235 | onclick: function () { |
236 | if (nameput.value[0] != '@') |
237 | tobename = nameput.value |
238 | else |
239 | tobename = nameput.value.substring(1, 100) |
240 | var newname = h('span', h('a', {href: '#' + src}, '@' + tobename), h('button.btn', 'Publish', { |
241 | onclick: function () { |
242 | var donename = h('span', h('a', {href: '#' + src}, '@' + tobename)) |
243 | sbot.publish({type: 'about', about: src, name: tobename}) |
244 | localStorage[src + 'name'] = tobename |
245 | newname.parentNode.replaceChild(donename, newname) |
246 | } |
247 | })) |
248 | nameedit.parentNode.replaceChild(newname, nameedit) |
249 | } |
250 | }) |
251 | ) |
252 | editname.parentNode.replaceChild(nameedit, editname) |
253 | } |
254 | }) |
255 | ) |
256 | |
257 | var editimage = h('span', |
258 | h('button.btn', 'New image', { |
259 | onclick: function () { |
260 | var upload = |
261 | h('span', |
262 | hyperfile.asDataURL(function (data) { |
263 | if(data) { |
264 | //img.src = data |
265 | var _data = dataurl.parse(data) |
266 | pull( |
267 | pull.once(_data.data), |
268 | sbot.addblob(function (err, hash) { |
269 | if(err) return alert(err.stack) |
270 | selected = { |
271 | link: hash, |
272 | size: _data.data.length, |
273 | type: _data.mimetype |
274 | } |
275 | }) |
276 | ) |
277 | } |
278 | }), |
279 | h('button.btn', 'Preview image', { |
280 | onclick: function() { |
281 | if (selected) { |
282 | console.log(selected) |
283 | var oldImage = document.getElementById('profileImage') |
284 | var newImage = h('span.avatar--medium', h('img', {src: config.blobsUrl + selected.link})) |
285 | var publish = h('button.btn', 'Publish image', { |
286 | onclick: function () { |
287 | sbot.publish({ |
288 | type: 'about', |
289 | about: src, |
290 | image: selected |
291 | }, function (err, published) { |
292 | console.log(published) |
293 | }) |
294 | } |
295 | }) |
296 | upload.parentNode.replaceChild(publish, upload) |
297 | oldImage.parentNode.replaceChild(newImage, oldImage) |
298 | } |
299 | /*if(selected) { |
300 | api.message_confirm({ |
301 | type: 'about', |
302 | about: id, |
303 | image: selected |
304 | }) |
305 | } else { alert('select an image before hitting preview')}*/ |
306 | } |
307 | }) |
308 | ) |
309 | editimage.parentNode.replaceChild(upload, editimage) |
310 | } |
311 | }) |
312 | ) |
313 | |
314 | var avatars = h('div.avatars', |
315 | h('a', {href: '#' + src}, |
316 | h('span.avatar--medium#profileImage', avatar.image(src)), |
317 | editname, |
318 | h('br'), |
319 | editimage |
320 | ) |
321 | ) |
322 | |
323 | pull( |
324 | sbot.userStream({id: src, reverse: false, limit: 1}), |
325 | pull.drain(function (msg) { |
326 | var howlong = h('span', h('br'), ' arrived ', human(new Date(msg.value.timestamp))) |
327 | avatars.appendChild(howlong) |
328 | console.log(msg) |
329 | }) |
330 | ) |
331 | |
332 | |
333 | var buttons = h('div.buttons') |
334 | |
335 | profile.firstChild.appendChild(avatars) |
336 | profile.firstChild.appendChild(buttons) |
337 | buttons.appendChild(tools.mute(src)) |
338 | |
339 | var writeMessage = h('button.btn', 'Public message ', avatar.name(src), { |
340 | onclick: function () { |
341 | opts = {} |
342 | opts.type = 'post' |
343 | opts.mentions = '[' + name.textContent + '](' + src + ')' |
344 | var composer = h('div#composer', h('div.message', compose(opts))) |
345 | profile.appendChild(composer) |
346 | } |
347 | }) |
348 | |
349 | var writePrivate = h('button.btn', 'Private message ', avatar.name(src), { |
350 | onclick: function () { |
351 | opts = {} |
352 | opts.type = 'post' |
353 | opts.mentions = '[' + name.textContent + '](' + src + ')' |
354 | opts.recps = [src, id] |
355 | var composer = h('div#composer', h('div.message', compose(opts))) |
356 | profile.appendChild(composer) |
357 | } |
358 | }) |
359 | |
360 | buttons.appendChild(writeMessage) |
361 | buttons.appendChild(writePrivate) |
362 | buttons.appendChild(tools.follow(src)) |
363 | buttons.appendChild(tools.block(src)) |
364 | |
365 | buttons.appendChild(h('button.btn', 'Generate follows', { |
366 | onclick: function () { |
367 | profile.firstChild.appendChild(tools.getFollowing(src)) |
368 | profile.firstChild.appendChild(tools.getFollowers(src)) |
369 | } |
370 | })) |
371 | |
372 | buttons.appendChild(h('button.btn', 'Generate blocks', { |
373 | onclick: function () { |
374 | profile.firstChild.appendChild(tools.getBlocks(src)) |
375 | profile.firstChild.appendChild(tools.getBlocked(src)) |
376 | } |
377 | })) |
378 | buttons.appendChild(h('a', {href: '#wall/' + src}, h('button.btn', avatar.name(src), "'s wall"))) |
379 | |
380 | } |
381 | |
382 | var privateMsg = function (src) { |
383 | var content = h('div.content') |
384 | var screen = document.getElementById('screen') |
385 | screen.appendChild(hyperscroll(content)) |
386 | |
387 | sbot.get(src, function (err, data) { |
388 | if (err) { |
389 | var message = h('div.message', 'Missing message!') |
390 | content.appendChild(message) |
391 | } |
392 | if (data) { |
393 | console.log(data) |
394 | data.value = data |
395 | data.key = src |
396 | |
397 | content.appendChild(render(data)) |
398 | } |
399 | |
400 | }) |
401 | } |
402 | |
403 | var msgThread = function (src) { |
404 | |
405 | var content = h('div.content') |
406 | var screen = document.getElementById('screen') |
407 | screen.appendChild(hyperscroll(content)) |
408 | |
409 | pull( |
410 | sbot.query({query: [{$filter: { value: { content: {root: src}, timestamp: { $gt: 1 }}}}], live: true}), |
411 | pull.drain(function (msg) { |
412 | if (msg.value) { |
413 | content.appendChild(render(msg)) |
414 | } |
415 | }) |
416 | ) |
417 | |
418 | sbot.get(src, function (err, data) { |
419 | if (err) { |
420 | var message = h('div.message', 'Missing message!') |
421 | content.appendChild(message) |
422 | } |
423 | if (data) { |
424 | var message = {} |
425 | message.value = data |
426 | message.key = src |
427 | console.log(message) |
428 | var rootMsg = render(message) |
429 | |
430 | if (content.firstChild) { |
431 | content.insertBefore(rootMsg, content.firstChild) |
432 | } else { |
433 | content.appendChild(rootMsg) |
434 | } |
435 | if (message.value.content.type == 'git-repo') { |
436 | pull( |
437 | sbot.backlinks({query: [{$filter: {value: {content: {type: 'git-update'}}, dest: src}}]}), |
438 | pull.drain(function (msg) { |
439 | if (msg.value) { |
440 | content.appendChild(render(msg)) |
441 | } |
442 | }) |
443 | ) |
444 | } |
445 | |
446 | } |
447 | }) |
448 | |
449 | } |
450 | |
451 | var keyPage = function () { |
452 | var screen = document.getElementById('screen') |
453 | |
454 | var importKey = h('textarea.import', {placeholder: 'Import a new public/private key', name: 'textarea', style: 'width: 97%; height: 100px;'}) |
455 | |
456 | var content = h('div.content', |
457 | h('div.message#key', |
458 | h('h1', 'Your Key'), |
459 | h('p', {innerHTML: 'Your public/private key is: <pre><code>' + localStorage[config.caps.shs + '/secret'] + '</code></pre>'}, |
460 | h('button.btn', {onclick: function (e){ |
461 | localStorage[config.caps.shs +'/secret'] = '' |
462 | alert('Your public/private key has been deleted') |
463 | e.preventDefault() |
464 | location.hash = "" |
465 | location.reload() |
466 | }}, 'Delete Key') |
467 | ), |
468 | h('hr'), |
469 | h('form', |
470 | importKey, |
471 | h('button.btn', {onclick: function (e){ |
472 | if(importKey.value) { |
473 | localStorage[config.caps.shs + '/secret'] = importKey.value.replace(/\s+/g, ' ') |
474 | e.preventDefault() |
475 | alert('Your public/private key has been updated') |
476 | } |
477 | location.hash = "" |
478 | location.reload() |
479 | }}, 'Import key'), |
480 | ) |
481 | ) |
482 | ) |
483 | |
484 | screen.appendChild(hyperscroll(content)) |
485 | } |
486 | |
487 | |
488 | function friendsStream (src) { |
489 | |
490 | var screen = document.getElementById('screen') |
491 | var content = h('div.content') |
492 | |
493 | screen.appendChild(hyperscroll(content)) |
494 | |
495 | function createStream (opts) { |
496 | return pull( |
497 | Next(sbot.query, opts, ['value', 'timestamp']), |
498 | para(function (msg, cb) { |
499 | sbot.friends.get({source: src, dest: msg.value.author}, function (err, data) { |
500 | if (err) cb(err) |
501 | if (data === true) { |
502 | return cb(null, render(msg)) |
503 | } else { |
504 | return cb(null, false) |
505 | } |
506 | }) |
507 | }), |
508 | filter(function (data) { |
509 | return data !== false |
510 | }) |
511 | ) |
512 | } |
513 | |
514 | pull( |
515 | createStream({ |
516 | limit: 1000, |
517 | reverse: true, |
518 | live: false, |
519 | query: [{$filter: { value: { timestamp: { $gt: 0 }}}}] |
520 | }), |
521 | stream.bottom(content) |
522 | ) |
523 | |
524 | } |
525 | |
526 | function everythingStream () { |
527 | |
528 | var screen = document.getElementById('screen') |
529 | var content = h('div.content') |
530 | |
531 | screen.appendChild(hyperscroll(content)) |
532 | |
533 | function createStream (opts) { |
534 | return pull( |
535 | Next(sbot.query, opts, ['value', 'timestamp']), |
536 | pull.map(function (msg) { |
537 | if (msg.value) { |
538 | if (msg.value.timestamp > Date.now()) { |
539 | return h('div.future') |
540 | } else { |
541 | var node = render(msg) |
542 | return node |
543 | } |
544 | } |
545 | }) |
546 | ) |
547 | } |
548 | |
549 | pull( |
550 | createStream({ |
551 | limit: 10, |
552 | reverse: true, |
553 | live: false, |
554 | query: [{$filter: { value: { content: { type: 'post' }, timestamp: { $gt: 0 }}}}] |
555 | }), |
556 | stream.bottom(content) |
557 | ) |
558 | |
559 | pull( |
560 | createStream({ |
561 | limit: 10, |
562 | old: false, |
563 | live: true, |
564 | query: [{$filter: { value: { timestamp: { $gt: 0 }}}}] |
565 | }), |
566 | stream.top(content) |
567 | ) |
568 | } |
569 | |
570 | |
571 | |
572 | |
573 | function backchannel () { |
574 | |
575 | var screen = document.getElementById('screen') |
576 | var content = h('div.content') |
577 | |
578 | screen.appendChild(hyperscroll(content)) |
579 | |
580 | var chatbox = h('input', {placeholder: 'Backchannel'}) |
581 | |
582 | var chat = h('div.content') |
583 | |
584 | var publish = h('button.btn', 'Publish', { |
585 | onclick: function () { |
586 | if (chatbox.value) { |
587 | var content = { |
588 | text: chatbox.value, |
589 | type: 'scat_message' |
590 | } |
591 | sbot.publish(content, function (err, msg) { |
592 | if (err) throw err |
593 | chatbox.value = '' |
594 | console.log('Published!', msg) |
595 | }) |
596 | } |
597 | } |
598 | }) |
599 | |
600 | chat.appendChild(h('div.message', chatbox, publish)) |
601 | |
602 | if (screen.firstChild.firstChild) { |
603 | screen.firstChild.insertBefore(chat, screen.firstChild.firstChild) |
604 | } else { |
605 | screen.firstChild.appendChild(chat) |
606 | } |
607 | |
608 | function createStream (opts) { |
609 | return pull( |
610 | Next(sbot.query, opts, ['value', 'timestamp']), |
611 | pull.map(function (msg) { |
612 | if (msg.value) { |
613 | return render(msg) |
614 | } |
615 | }) |
616 | ) |
617 | } |
618 | |
619 | pull( |
620 | createStream({ |
621 | limit: 10, |
622 | reverse: true, |
623 | live: false, |
624 | query: [{$filter: { value: { content: {type: 'scat_message'}, timestamp: { $gt: 0 }}}}] |
625 | }), |
626 | stream.bottom(content) |
627 | ) |
628 | |
629 | pull( |
630 | createStream({ |
631 | limit: 10, |
632 | old: false, |
633 | live: true, |
634 | query: [{$filter: { value: { content: {type: 'scat_message'}, timestamp: { $gt: 0 }}}}] |
635 | }), |
636 | stream.top(content) |
637 | ) |
638 | } |
639 | |
640 | function search (src) { |
641 | console.log('search' + src) |
642 | |
643 | var content = h('div.content') |
644 | var screen = document.getElementById('screen') |
645 | screen.appendChild(hyperscroll(content)) |
646 | |
647 | pull( |
648 | sbot.search.query({query: src, limit: 100}), |
649 | pull.drain(function (search) { |
650 | content.appendChild(render(search)) |
651 | }) |
652 | ) |
653 | |
654 | } |
655 | |
656 | function hash () { |
657 | return window.location.hash.substring(1) |
658 | } |
659 | |
660 | module.exports = function () { |
661 | var src = hash() |
662 | |
663 | if (src.substring(52, 59) == '?unbox=') { |
664 | privateMsg(src) |
665 | } else if (ref.isFeed(src)) { |
666 | userStream(src) |
667 | } else if (ref.isMsg(src)) { |
668 | msgThread(src) |
669 | } else if (ref.isFeed(src.substring(5))) { |
670 | mentionsStream(src.substring(5)) |
671 | } else if (ref.isFeed(src.substring(8))) { |
672 | friendsStream(src.substring(8)) |
673 | } else if (src.substring(0, 6) === 'label/') { |
674 | labelStream(src.substring(6)) |
675 | } else if (src == 'queue') { |
676 | queueStream() |
677 | } else if (src == 'backchannel') { |
678 | backchannel() |
679 | } else if (src == 'private') { |
680 | privateStream() |
681 | } else if (src == 'key') { |
682 | keyPage() |
683 | } else if (src[0] == '?' || (src[0] == '#')) { |
684 | if (src[0] == '#') |
685 | search(src.split('%20').join(' ')) |
686 | else |
687 | search(src.substr(1).split('%20').join(' ')) |
688 | } else { |
689 | everythingStream() |
690 | } |
691 | |
692 | } |
693 |
Built with git-ssb-web