git ssb

9+

cel / ssb-viewer



Commit ee9c39f0499ced79d136914439eba10ccec934df

Merge remote-tracking branches 'origin/hyperscript' and 'angelo/render_rss'

cel committed on 6/11/2017, 12:31:49 AM
Parent: ad9208780b9cbebfde0a0e1f3accf636513b0b9b
Parent: e042fb79339f605bb9bea4d8d2e0c4db84d8ec89
Parent: 4eee4ae72866afa2e09c9d82babd86152644119e

Files changed

index.jschanged
package.jsonchanged
render.jschanged
index.jsView
@@ -15,9 +15,12 @@
1515 renderEmoji,
1616 formatMsgs,
1717 wrapPage,
1818 renderThread,
19- renderAbout
19 + renderAbout,
20 + renderShowAll,
21 + renderRssItem,
22 + wrapRss,
2023 } = require('./render');
2124
2225 var appHash = hash([fs.readFileSync(__filename)])
2326
@@ -89,37 +92,51 @@
8992 default: return servePath(req, res, m[4])
9093 }
9194 }
9295
93- function serveFeed(req, res, feedId) {
96 + function serveFeed(req, res, feedId, ext) {
9497 console.log("serving feed: " + feedId)
9598
96- var showAll = req.url.endsWith("?showAll")
97- var showAllHTML = showAll ? '' : '<br/><a href="' + req.url + '?showAll">Show whole feed</a>'
99 + var showAll = req.url.endsWith("?showAll");
98100
99101 getAbout(feedId, function (err, about) {
100102 if (err) return respond(res, 500, err.stack || err)
101103
102- pull(
103- sbot.createUserStream({ id: feedId, reverse: true, limit: showAll ? -1 : 10 }),
104- pull.collect(function (err, logs) {
105- if (err) return respond(res, 500, err.stack || err)
106- res.writeHead(200, {
107- 'Content-Type': ctype("html")
108- })
109- pull(
110- pull.values(logs),
111- paramap(addAuthorAbout, 8),
112- paramap(addFollowAbout, 8),
113- paramap(addVoteMessage, 8),
114- paramap(addGitLinks, 8),
115- pull(renderAbout(defaultOpts, about, showAllHTML), wrapPage(about.name)),
116- toPull(res, function (err) {
117- if (err) console.error('[viewer]', err)
118- })
119- )
120- })
121- )
104 + function render() {
105 + switch (ext) {
106 + case 'rss':
107 + return pull(
108 + // formatMsgs(feedId, ext, defaultOpts)
109 + renderRssItem(defaultOpts), wrapRss(about.name, defaultOpts)
110 + );
111 + default:
112 + return pull(
113 + renderAbout(defaultOpts, about,
114 + renderShowAll(showAll, req.url)), wrapPage(about.name)
115 + );
116 + }
117 + }
118 +
119 + pull(
120 + sbot.createUserStream({ id: feedId, reverse: true, limit: showAll ? -1 : (ext == 'rss' ? 25 :10) }),
121 + pull.collect(function (err, logs) {
122 + if (err) return respond(res, 500, err.stack || err)
123 + res.writeHead(200, {
124 + 'Content-Type': ctype(ext)
125 + })
126 + pull(
127 + pull.values(logs),
128 + paramap(addAuthorAbout, 8),
129 + paramap(addFollowAbout, 8),
130 + paramap(addVoteMessage, 8),
131 + paramap(addGitLinks, 8),
132 + render(),
133 + toPull(res, function (err) {
134 + if (err) console.error('[viewer]', err)
135 + })
136 + )
137 + })
138 + )
122139 })
123140 }
124141
125142 function serveUserFeed(req, res, url) {
@@ -195,9 +212,8 @@
195212 var channelId = url.substring(url.lastIndexOf('channel/')+8, 100)
196213 console.log("serving channel: " + channelId)
197214
198215 var showAll = req.url.endsWith("?showAll")
199- var showAllHTML = showAll ? '' : '<br/><a href="' + req.url + '?showAll">Show whole feed</a>'
200216
201217 pull(
202218 sbot.query.read({ limit: showAll ? 300 : 10, reverse: true, query: [{$filter: { value: { content: { channel: channelId }}}}]}),
203219 pull.collect(function (err, logs) {
@@ -208,9 +224,11 @@
208224 pull(
209225 pull.values(logs),
210226 paramap(addAuthorAbout, 8),
211227 paramap(addVoteMessage, 8),
212- pull(renderThread(defaultOpts, showAllHTML), wrapPage('#' + channelId)),
228 + pull(renderThread(defaultOpts,
229 + renderShowAll(showAll, req.url)),
230 + wrapPage('#' + channelId)),
213231 toPull(res, function (err) {
214232 if (err) console.error('[viewer]', err)
215233 })
216234 )
@@ -362,8 +380,9 @@
362380 case 'html': return 'text/html'
363381 case 'js': return 'text/javascript'
364382 case 'css': return 'text/css'
365383 case 'json': return 'application/json'
384 + case 'rss': return 'text/xml'
366385 }
367386 }
368387
369388 function servePath(req, res, url) {
package.jsonView
@@ -8,8 +8,9 @@
88 "asyncmemo": "^1.0.0",
99 "emoji-named-characters": "^1.0.2",
1010 "emoji-server": "^1.0.0",
1111 "human-time": "^0.0.1",
12 + "hyperscript": "2.0.2",
1213 "lrucache": "^1.0.2",
1314 "pull-cat": "^1.1.11",
1415 "pull-paramap": "^1.2.1",
1516 "pull-stream": "^3.5.0",
render.jsView
@@ -3,8 +3,9 @@
33 var marked = require("ssb-marked");
44 var htime = require("human-time");
55 var emojis = require("emoji-named-characters");
66 var cat = require("pull-cat");
7 +var h = require('hyperscript');
78
89 var emojiDir = path.join(require.resolve("emoji-named-characters"), "../pngs");
910
1011 exports.wrapPage = wrapPage;
@@ -12,8 +13,11 @@
1213 exports.renderEmoji = renderEmoji;
1314 exports.formatMsgs = formatMsgs;
1415 exports.renderThread = renderThread;
1516 exports.renderAbout = renderAbout;
17 +exports.renderShowAll = renderShowAll;
18 +exports.renderRssItem = renderRssItem;
19 +exports.wrapRss = wrapRss;
1620
1721 function MdRenderer(opts) {
1822 marked.Renderer.call(this, {});
1923 this.opts = opts;
@@ -38,19 +42,13 @@
3842 return href;
3943 };
4044
4145 MdRenderer.prototype.image = function(href, title, text) {
42- return (
43- '<img src="' +
44- this.opts.img_base +
45- escape(href) +
46- '"' +
47- ' alt="' +
48- text +
49- '"' +
50- (title ? ' title="' + title + '"' : "") +
51- (this.options.xhtml ? "/>" : ">")
52- );
46 + return h('img',
47 + { src: this.opts.img_base + href,
48 + alt: text,
49 + title: title
50 + }).outerHTML;
5351 };
5452
5553 function renderEmoji(emoji) {
5654 var opts = this.renderer.opts;
@@ -58,18 +56,14 @@
5856 var url = mentions[emoji]
5957 ? opts.blob_base + encodeURIComponent(mentions[emoji])
6058 : emoji in emojis && opts.emoji_base + escape(emoji) + '.png';
6159 return url
62- ? '<img src="' +
63- url +
64- '"' +
65- ' alt=":' +
66- escape(emoji) +
67- ':"' +
68- ' title=":' +
69- escape(emoji) +
70- ':"' +
71- ' class="ssb-emoji" height="16" width="16">'
60 + ? h('img.ssb-emoji',
61 + { src: url,
62 + alt: ':' + escape(emoji) + ':',
63 + title: ':' + escape(emoji) + ':',
64 + height: 16, width: 16
65 + }).outerHTML
7266 : ":" + emoji + ":";
7367 }
7468
7569 function escape(str) {
@@ -87,8 +81,10 @@
8781 case "js":
8882 return pull(renderThread(opts), wrapJSEmbed(opts));
8983 case "json":
9084 return wrapJSON();
85 + case "rss":
86 + return pull(renderRssItem(opts), wrapRss(id, opts));
9187 default:
9288 return null;
9389 }
9490 }
@@ -98,46 +94,54 @@
9894 return cat([pull.once(before), read, pull.once(after)]);
9995 };
10096 }
10197
98 +function callToAction() {
99 + return h('a.call-to-action',
100 + { href: 'https://www.scuttlebutt.nz' },
101 + 'Join Scuttlebutt now').outerHTML;
102 +}
103 +
104 +function toolTipTop() {
105 + return h('span.top-tip',
106 + 'You are reading content from ',
107 + h('a', { href: 'https://www.scuttlebutt.nz' },
108 + 'Scuttlebutt')).outerHTML;
109 +}
110 +
102111 function renderAbout(opts, about, showAllHTML = "") {
112 + var figCaption = h('figcaption');
113 + figCaption.innerHTML = 'Feed of ' + about.name + '<br>' +
114 + (about.description != undefined ?
115 + marked(about.description, opts.marked) : '');
103116 return pull(
104117 pull.map(renderMsg.bind(this, opts)),
105- wrap(
106- '<span class="top-tip">You are reading content from ' +
107- '<a href="https://www.scuttlebutt.nz">Scuttlebutt</a>' +
108- '</span>' +
109- '<main>' +
110- '<article><header><figure>' +
111- '<img src="' + opts.img_base + escape(about.image) + '" ' +
112- 'height="200" width="200"><figcaption>' +
113- 'Feed of ' + about.name + '<br/>' +
114- (about.description != undefined ?
115- marked(about.description, opts.marked) : '') +
116- '</figcaption></figure></header></article>',
117-
118- showAllHTML + '</main>' +
119- '<a class="call-to-action" href="https://www.scuttlebutt.nz">' +
120- 'Join Scuttlebutt now' +
121- '</a>'
122- )
118 + wrap(toolTipTop() + '<main>' +
119 + h('article',
120 + h('header',
121 + h('figure',
122 + h('img',
123 + { src: opts.img_base + about.image,
124 + height: 200,
125 + width: 200
126 + }),
127 + figCaption)
128 + )).outerHTML,
129 + showAllHTML + '</main>' + callToAction())
123130 );
124131 }
125132
126133 function renderThread(opts, showAllHTML = "") {
127134 return pull(
128135 pull.map(renderMsg.bind(this, opts)),
129- wrap(
130- '<span class="top-tip">You are reading content from ' +
131- '<a href="https://www.scuttlebutt.nz">Scuttlebutt</a>' +
132- '</span>' +
133- '<main>',
136 + wrap(toolTipTop() + '<main>',
137 + showAllHTML + '</main>' + callToAction())
138 + );
139 +}
134140
135- showAllHTML + '</main>' +
136- '<a class="call-to-action" href="https://www.scuttlebutt.nz">' +
137- 'Join Scuttlebutt now' +
138- '</a>'
139- )
141 +function renderRssItem(opts) {
142 + return pull(
143 + pull.map(renderRss.bind(this, opts))
140144 );
141145 }
142146
143147 function wrapPage(id) {
@@ -153,8 +157,20 @@
153157 "</body></html>"
154158 );
155159 }
156160
161 +function wrapRss(id, opts) {
162 + return wrap(
163 + '<?xml version="1.0" encoding="UTF-8" ?>' +
164 + '<rss version="2.0">' +
165 + '<channel>' +
166 + '<title>' + id + ' | ssb-viewer</title>',
167 +
168 + '</channel>'+
169 + '</rss>'
170 + );
171 +}
172 +
157173 var styles = `
158174 <style>
159175 html { background-color: #f1f3f5; }
160176 body {
@@ -319,153 +335,164 @@
319335
320336 function renderMsg(opts, msg) {
321337 var c = msg.value.content || {};
322338 var name = encodeURIComponent(msg.key);
339 + return h('article#' + name,
340 + h('header',
341 + h('figure',
342 + h('img', { alt: '',
343 + src: opts.img_base + msg.author.image,
344 + height: 50, width: 50 }),
345 + h('figcaption',
346 + h('a.ssb-avatar-name',
347 + { href: opts.base + escape(msg.value.author) },
348 + msg.author.name),
349 + msgTimestamp(msg, opts.base + name)))),
350 + render(opts, c)).outerHTML;
351 +}
352 +
353 +function renderRss(opts, msg) {
354 + var c = msg.value.content || {};
355 + var name = encodeURIComponent(msg.key);
356 +
357 + let content = render(opts, c);
358 +
359 + if (!content) {
360 + return null;
361 + }
362 +
323363 return (
324- '<article id="' +
325- name +
326- '">' +
327- '<header>' +
328- '<figure>' +
329- '<img alt="" ' +
330- 'src="' + opts.img_base + escape(msg.author.image) + '" ' +
331- 'height="50" width="50">' +
332- '<figcaption>' +
333- '<a class="ssb-avatar-name"' +
334- ' href="' + opts.base +
335- escape(msg.value.author) +
336- '"' +
337- ">" + msg.author.name + "</a>" +
338- msgTimestamp(msg, opts.base + name) +
339- '</figcaption>' +
340- '</figure>' +
341- '</header>' +
342- render(opts, c) +
343- "</article>"
364 + '<item>' +
365 + '<title>' + msg.author.name + ' | ' + c.type + '</title>' +
366 + '<description><![CDATA[' + content + ']]></description>' +
367 + '<link>' + opts.base + escape(name) + '</link>' +
368 + '<pubDate>' + new Date(msg.value.timestamp).toUTCString() + '</pubDate>' +
369 + '<guid>' + msg.key + '</guid>' +
370 + '</item>'
344371 );
345372 }
346373
347374 function msgTimestamp(msg, link) {
348375 var date = new Date(msg.value.timestamp);
349376 var isoStr = date.toISOString();
350- return (
351- '<time class="ssb-timestamp" datetime="' + isoStr + '">' +
352- '<a ' +
353- 'href="' + link + '" ' +
354- 'title="' + isoStr + '" ' +
355- '>' + formatDate(date) + '</a>' +
356- '</time>'
357- );
377 + return h('time.ssb-timestamp',
378 + { datetime: isoStr },
379 + h('a',
380 + { href: link,
381 + title: isoStr },
382 + formatDate(date)));
358383 }
359384
360385 function formatDate(date) {
361- // return date.toISOString().replace('T', ' ')
362386 return htime(date);
363387 }
364388
365389 function render(opts, c) {
366390 var base = opts.base;
367391 if (c.type === "post") {
368392 var channel = c.channel
369- ? '<div class="top-right"><a href="' + base + 'channel/' + c.channel + '">#' + c.channel + "</a></div>"
370- : "";
371- return channel + renderPost(opts, c);
393 + ? h('div.top-right',
394 + h('a',
395 + { href: base + 'channel/' + c.channel },
396 + '#' + c.channel))
397 + : "";
398 + return [channel, renderPost(opts, c)];
372399 } else if (c.type == "vote" && c.vote.expression == "Dig") {
373400 var channel = c.channel
374- ? ' in <a href="' + base + 'channel/' + c.channel + '">#' + c.channel + "</a>"
375- : "";
401 + ? [' in ',
402 + h('a',
403 + { href: base + 'channel/' + c.channel },
404 + '#' + c.channel)]
405 + : "";
376406 var linkedText = "this";
377407 if (typeof c.vote.linkedText != "undefined")
378- linkedText = c.vote.linkedText.substring(0, 75);
379- return ('<span class="status">' +
380- 'Liked ' +
381- '<a href="' + base +
382- c.vote.link +
383- '">' +
384- linkedText +
385- "</a>" +
386- channel +
387- '</span>'
388- );
408 + linkedText = c.vote.linkedText.substring(0, 75);
409 + return h('span.status',
410 + ['Liked ',
411 + h('a', { href: base + c.vote.link }, linkedText),
412 + channel]);
389413 } else if (c.type == "vote") {
390414 var linkedText = "this";
391415 if (typeof c.vote.linkedText != "undefined")
392416 linkedText = c.vote.linkedText.substring(0, 75);
393- return '<span class="status">' +
394- 'Voted <a href="' + base + c.vote.link + '">' + linkedText + "</a>" +
395- '</span>';
417 + return h('span.status',
418 + ['Voted ',
419 + h('a', { href: base + c.vote.link }, linkedText)]);
396420 } else if (c.type == "contact" && c.following) {
397421 var name = c.contact;
398- if (typeof c.contactAbout != "undefined") name = c.contactAbout.name;
399- return '<span class="status">' +
400- 'Followed <a href="' + base + c.contact + '">' + name + "</a>" +
401- '</span>';
422 + if (typeof c.contactAbout != "undefined")
423 + name = c.contactAbout.name;
424 + return h('span.status',
425 + ['Followed ',
426 + h('a', { href: base + c.contact }, name)]);
402427 } else if (c.type == "contact" && !c.following) {
403428 var name = c.contact;
404- if (typeof c.contactAbout != "undefined") name = c.contactAbout.name;
405- return '<span class="status">' +
406- 'Unfollowed <a href="' + base + c.contact + '">' + name + "</a>" +
407- '</span>';
429 + if (typeof c.contactAbout != "undefined")
430 + name = c.contactAbout.name;
431 + return h('span.status',
432 + ['Unfollowed ',
433 + h('a', { href: base + c.contact }, name)]);
408434 } else if (typeof c == "string") {
409- return '<span class="status">' +
410- "Wrote something private" +
411- '</span>';
435 + return h('span.status', 'Wrote something private')
412436 }
413437 else if (c.type == "about") {
414- return '<span class="status">' +
415- "Changed something in about" +
416- '</span>' +
417- renderDefault(c);
438 + return [h('span.status', 'Changed something in about'),
439 + renderDefault(c)];
418440 }
419441 else if (c.type == "issue") {
420- return '<span class="status">' +
421- "Created a git issue" + (c.repoName != undefined ? " in repo " + c.repoName : "") + renderPost(opts, c) +
422- '</span>';
442 + return [h('span.status',
443 + "Created a git issue" +
444 + (c.repoName != undefined ? " in repo " + c.repoName : ""),
445 + renderPost(opts, c))];
423446 }
424447 else if (c.type == "git-update") {
425- return '<span class="status">' +
426- "Did a git update " + (c.repoName != undefined ? " in repo " + c.repoName : "") +
427- "<br/>" +
428- (c.commits != undefined ? c.commits.map(com => { return "-" +com.title; }).join("<br/>") : "") +
429- '</span>'
448 + return h('span.status',
449 + "Did a git update " +
450 + (c.repoName != undefined ? " in repo " + c.repoName : "") +
451 + '<br>' +
452 + (c.commits != undefined ?
453 + c.commits.map(com => { return "-" +com.title; }).join('<br>') : ""));
430454 }
431455 else if (c.type == "ssb-dns") {
432- return '<span class="status">' +
433- "Updated DNS" +
434- '</span>' +
435- renderDefault(c);
456 + return [h('span.status', 'Updated DNS'), renderDefault(c)];
436457 }
437458 else if (c.type == "pub") {
438- return '<span class="status">' +
439- "Connected to the pub " + c.address.host +
440- '</span>'
459 + return h('span.status', 'Connected to the pub ' + c.address.host);
441460 }
442461 else if (c.type == "channel" && c.subscribed)
443- return '<span class="status">' +
444- 'Subscribed to channel <a href="' + base + 'channel/' +
445- c.channel +
446- '">#' +
447- c.channel +
448- "</a>" +
449- '</span>';
462 + return h('span.status',
463 + 'Subscribed to channel ',
464 + h('a',
465 + { href: base + 'channel/' + c.channel },
466 + '#' + c.channel));
450467 else if (c.type == "channel" && !c.subscribed)
451- return '<span class="status">' +
452- 'Unsubscribed from channel <a href="' + base + 'channel/' +
453- c.channel +
454- '">#' +
455- c.channel +
456- "</a>" +
457- '</span>';
468 + return h('span.status',
469 + 'Unsubscribed from channel ',
470 + h('a',
471 + { href: base + 'channel/' + c.channel },
472 + '#' + c.channel))
458473 else return renderDefault(c);
459474 }
460475
461476 function renderPost(opts, c) {
462477 opts.mentions = {};
463- if (Array.isArray(c.mentions)) c.mentions.forEach(function (link) {
464- if (link && link.name && link.link) opts.mentions[link.name] = link.link;
465- });
466- return '<section>' + marked(String(c.text), opts.marked) + "</section>";
478 + if (Array.isArray(c.mentions)) {
479 + c.mentions.forEach(function (link) {
480 + if (link && link.name && link.link)
481 + opts.mentions[link.name] = link.link;
482 + });
483 + }
484 + var s = h('section');
485 + s.innerHTML = marked(String(c.text), opts.marked);
486 + return s;
467487 }
468488
469489 function renderDefault(c) {
470- return "<pre>" + JSON.stringify(c, 0, 2) + "</pre>";
490 + return h('pre', JSON.stringify(c, 0, 2));
471491 }
492 +
493 +function renderShowAll(showAll, url) {
494 + if (showAll)
495 + return '';
496 + else
497 + return '<br>' + h('a', { href : url + '?showAll' }, 'Show whole feed').outerHTML;
498 +}

Built with git-ssb-web