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