From a1fdd6f43395d2a208100cc07a2769a1b684bedf Mon Sep 17 00:00:00 2001 From: Stephen Whitmore Date: Thu, 28 Dec 2017 11:41:15 -0800 Subject: fix: fail gracefully on jpeg rotation exception --- lib/serve.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/serve.js b/lib/serve.js index 76b3a8c..0c1ede8 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -1210,15 +1210,20 @@ Serve.prototype.image = function (path) { if (err) return heresTheData(err) buffer = Buffer.concat(buffer) - jpeg.rotate(buffer, {}, function (err, rotatedBuffer, orientation) { - if (!err) buffer = rotatedBuffer + try { + jpeg.rotate(buffer, {}, function (err, rotatedBuffer, orientation) { + if (!err) buffer = rotatedBuffer - heresTheData(null, buffer) - pull( - pull.once(buffer), - self.respondSink() - ) - }) + heresTheData(null, buffer) + pull( + pull.once(buffer), + self.respondSink() + ) + }) + } catch (err) { + console.trace(err) + self.respond(500, err.message || err) + } } done(function (err, data, type) { -- cgit v1.2.3 From 2473135971009a727894d210dab0da72569f61a8 Mon Sep 17 00:00:00 2001 From: cel Date: Wed, 27 Dec 2017 12:14:31 -1000 Subject: Fix detecting ssb-ooo when patchfoo is loaded as a plugin before it --- lib/app.js | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/app.js b/lib/app.js index 81f58f5..2606067 100644 --- a/lib/app.js +++ b/lib/app.js @@ -47,9 +47,7 @@ function App(sbot, config) { this.about = new About(this, sbot.id) this.msgCache = lru(100) this.getMsg = memo({cache: this.msgCache}, getMsgWithValue, sbot) - this.getMsgOoo = sbot.ooo - ? memo({cache: this.msgCache}, sbot.ooo.get) - : function (id, cb) { cb(new Error('missing ssb-ooo plugin')) } + this.getMsgOoo = memo({cache: this.msgCache}, this.getMsgOoo) this.getAbout = memo({cache: this.aboutCache = lru(500)}, this._getAbout.bind(this)) this.unboxContent = memo({cache: lru(100)}, sbot.private.unbox) @@ -184,6 +182,12 @@ App.prototype.getMsgDecrypted = function (key, cb) { }) } +App.prototype.getMsgOoo = function (key, cb) { + var ooo = this.sbot.ooo + if (!ooo) return cb(new Error('missing ssb-ooo plugin')) + ooo.get(key, cb) +} + App.prototype.getMsgDecryptedOoo = function (key, cb) { var self = this this.getMsgOoo(key, function (err, msg) { @@ -346,16 +350,6 @@ function getMsgWithValue(sbot, id, cb) { }) } -function getMsgOooWithValueCreate(sbot) { - if (!sbot.ooo) { - var err = new Error('missing ssb-ooo plugin') - return function (id, cb) { - cb(null, err) - } - } - return sbot.ooo.get -} - App.prototype._getAbout = function (id, cb) { var self = this if (!u.isRef(id)) return cb(null, {}) -- cgit v1.2.3 From bcb25299e3ad4af71de348fa62dc6027b80cdc12 Mon Sep 17 00:00:00 2001 From: cel Date: Sat, 30 Dec 2017 13:38:57 -1000 Subject: Serve files in zip blobs --- lib/serve.js | 97 ++++++++++++++++++++++++++++ package-lock.json | 184 +++++++++++++++++++++++++++++++++++++++++++++++++----- package.json | 3 +- 3 files changed, 267 insertions(+), 17 deletions(-) diff --git a/lib/serve.js b/lib/serve.js index 0c1ede8..abf15c3 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -21,6 +21,7 @@ var htime = require('human-time') var ph = require('pull-hyperscript') var emojis = require('emoji-named-characters') var jpeg = require('jpeg-autorotate') +var unzip = require('unzip') module.exports = Serve @@ -335,6 +336,7 @@ Serve.prototype.path = function (url) { case '/npm-prebuilds': return this.npmPrebuilds(m[2]) case '/npm-readme': return this.npmReadme(m[2]) case '/markdown': return this.markdown(m[2]) + case '/zip': return this.zip(m[2]) } return this.respond(404, 'Not found') } @@ -2202,6 +2204,101 @@ Serve.prototype.markdown = function (url) { ) } +Serve.prototype.zip = function (url) { + var self = this + var parts = url.split('/').slice(1) + var id = decodeURIComponent(parts.shift()) + var filename = parts.join('/') + var blobs = self.app.sbot.blobs + var etag = id + filename + var index = filename === '' || /\/$/.test(filename) + var indexFilename = index && (filename + 'index.html') + if (filename === '/' || /\/\/$/.test(filename)) { + // force directory listing if path ends in // + filename = filename.replace(/\/$/, '') + indexFilename = false + } + var files = index && [] + if (self.req.headers['if-none-match'] === etag) return self.respond(304) + blobs.size(id, function (err, size) { + if (size == null) return askWantBlobsForm([id]) + if (err) { + if (/^invalid/.test(err.message)) return self.respond(400, err.message) + else return self.respond(500, err.message || err) + } + var parseUnzip = unzip.Parse() + var gotEntry = false + parseUnzip.on('entry', function (entry) { + if (index) { + if (!gotEntry) { + if (entry.path === indexFilename) { + gotEntry = true + return serveFile(entry) + } else if (entry.path.substr(0, filename.length) === filename) { + files.push({path: entry.path, type: entry.type, props: entry.props}) + } + } + } else { + if (!gotEntry && entry.path === filename) { + gotEntry = true + // if (false && entry.type === 'Directory') return serveDirectory(entry) + return serveFile(entry) + } + } + entry.autodrain() + }) + parseUnzip.on('close', function () { + if (gotEntry) return + if (!index) return self.respond(404, 'Entry not found') + pull( + ph('section', {}, [ + ph('h3', [ + ph('a', {href: self.app.render.toUrl('/links/' + id)}, id.substr(0, 8) + '…'), + ' ', + ph('a', {href: self.app.render.toUrl('/zip/' + encodeURIComponent(id) + '/' + filename)}, filename || '/'), + ]), + pull( + pull.values(files), + pull.map(function (file) { + var path = '/zip/' + encodeURIComponent(id) + '/' + file.path + return ph('li', [ + ph('a', {href: self.app.render.toUrl(path)}, file.path) + ]) + }) + ) + ]), + self.wrapPage(id + filename), + self.respondSink(200) + ) + gotEntry = true // so that the handler on error event does not run + }) + parseUnzip.on('error', function (err) { + if (!gotEntry) return self.respond(400, err.message) + }) + var size + function serveFile(entry) { + size = entry.size + pull( + toPull.source(entry), + ident(gotType), + self.respondSink() + ) + } + pull( + self.app.getBlob(id), + toPull(parseUnzip) + ) + function gotType(type) { + type = type && mime.lookup(type) + if (type) self.res.setHeader('Content-Type', type) + if (size) self.res.setHeader('Content-Length', size) + self.res.setHeader('Cache-Control', 'public, max-age=315360000') + self.res.setHeader('etag', etag) + self.res.writeHead(200) + } + }) +} + // wrap a binary source and render it or turn into an embed Serve.prototype.wrapBinary = function (opts) { var self = this diff --git a/package-lock.json b/package-lock.json index ae79d80..0f5d62f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,15 @@ "resolved": "http://localhost:8989/blobs/get/&tix85Coqflfg1RbOQ/x6cq0E1Rry5rDhGiIHyRdM+8s=.sha256", "integrity": "sha256-tix85Coqflfg1RbOQ/x6cq0E1Rry5rDhGiIHyRdM+8s=" }, + "binary": { + "version": "0.3.0", + "resolved": "http://localhost:8989/blobs/get/&lAvJtA+yqHWHbjyhvj0Fp6QZiB5CAcdsg/+7BVaLaQk=.sha256", + "integrity": "sha256-lAvJtA+yqHWHbjyhvj0Fp6QZiB5CAcdsg/+7BVaLaQk=", + "requires": { + "buffers": "0.1.1", + "chainsaw": "0.1.0" + } + }, "brace-expansion": { "version": "http://localhost:8989/blobs/get/&fdjQQT88BvLg9ynhFsffO5ZCC04SoSTrnmmGGWWxcPo=.sha256", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", @@ -42,6 +51,11 @@ "version": "http://localhost:8989/blobs/get/&u3cvbnn1mLUZCBFO3qRNABW3op1sgtwIK2UCQ/RIYGY=.sha256", "integrity": "sha1-QUGcrvdpdVkp3VGJZ9PuwKYmJ3E=" }, + "buffers": { + "version": "0.1.1", + "resolved": "http://localhost:8989/blobs/get/&+N5Jxg5GcAUYLZaHtdh3dbvOxjhaxjIgt3QTJ0Qd620=.sha256", + "integrity": "sha256-+N5Jxg5GcAUYLZaHtdh3dbvOxjhaxjIgt3QTJ0Qd620=" + }, "builtin-modules": { "version": "http://localhost:8989/blobs/get/&54lxeCToaIJpwkHCA9n2Fc8VKG1iF9dN78fzlbDaXxE=.sha256", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" @@ -60,6 +74,14 @@ "resolved": "http://localhost:8989/blobs/get/&mjv2gAClOLf1jZaoHspyZ5ZbBYFn6imQeCwXDtp1kvU=.sha256", "integrity": "sha256-mjv2gAClOLf1jZaoHspyZ5ZbBYFn6imQeCwXDtp1kvU=" }, + "chainsaw": { + "version": "0.1.0", + "resolved": "http://localhost:8989/blobs/get/&AVhqaqHRF0rgev9pgi0B4HBP4w8jrCIiHJuAS6uXbPs=.sha256", + "integrity": "sha256-AVhqaqHRF0rgev9pgi0B4HBP4w8jrCIiHJuAS6uXbPs=", + "requires": { + "traverse": "0.3.9" + } + }, "chloride": { "version": "2.2.7", "resolved": "http://localhost:8989/blobs/get/&/ahFOC6wlOMnO1HdAIAcuY6NQ9AkKjmR1Os/xnts6N8=.sha256", @@ -216,6 +238,27 @@ "version": "http://localhost:8989/blobs/get/&noDLhxMSWqU9+BopYm97gfJqm+HNQYQLPM3K5NUuj5w=.sha256", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fstream": { + "version": "0.1.31", + "resolved": "http://localhost:8989/blobs/get/&MgatCTGF+duNaFmT4+Nyh0UEi9rjNltsqSTLdeLCKwE=.sha256", + "integrity": "sha256-MgatCTGF+duNaFmT4+Nyh0UEi9rjNltsqSTLdeLCKwE=", + "requires": { + "graceful-fs": "3.0.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + }, + "dependencies": { + "graceful-fs": { + "version": "3.0.11", + "resolved": "http://localhost:8989/blobs/get/&WtP5g2DDTtr9aihenL4LdIICzr9RtKdKV9qRCFH1JQM=.sha256", + "integrity": "sha256-WtP5g2DDTtr9aihenL4LdIICzr9RtKdKV9qRCFH1JQM=", + "requires": { + "natives": "1.1.0" + } + } + } + }, "get-caller-file": { "version": "1.0.2", "resolved": "http://localhost:8989/blobs/get/&FMgvATYUUtPunnh52Q5mgZVX2OhKaewPbHxlxlYwSac=.sha256", @@ -437,6 +480,27 @@ "yallist": "http://localhost:8989/blobs/get/&x7MQhNNSXhcxS0bCjljOts2PEKn/km2vSDA5dvissLI=.sha256" } }, + "match-stream": { + "version": "0.0.2", + "resolved": "http://localhost:8989/blobs/get/&8ifmMRW33QbZzSrMY/n26MvE9tZtdgdmHG2P8iAx9/w=.sha256", + "integrity": "sha256-8ifmMRW33QbZzSrMY/n26MvE9tZtdgdmHG2P8iAx9/w=", + "requires": { + "buffers": "0.1.1", + "readable-stream": "1.0.34" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "http://localhost:8989/blobs/get/&0QEMlyAUePLZCW/tXOEhdYfVI4R19FBv4JyHzdW8VqY=.sha256", + "requires": { + "core-util-is": "http://localhost:8989/blobs/get/&pKRNq2V57ePgat5Y0m+P1kLq4JFT/VnGCPy3lRpJk5g=.sha256", + "inherits": "2.0.3", + "isarray": "http://localhost:8989/blobs/get/&PoREAgaWgArtkqCdTFJgKtx2FAo5tsjpT+fInPcKnwo=.sha256", + "string_decoder": "http://localhost:8989/blobs/get/&Pm5v/q/mFX6yJ4qQmvwLhFI0sTRG3KipUYwrebnCIIY=.sha256" + } + } + } + }, "mem": { "version": "1.1.0", "resolved": "http://localhost:8989/blobs/get/&JPEeBd7nMfhv4111AT1r8bKQHDHp5jbjOlgYO63qN5I=.sha256", @@ -527,6 +591,11 @@ "integrity": "sha256-dF6wPKUaGRuuJwIvKGrSOSZpzkskLzu2eXPH9iZqSKs=", "optional": true }, + "natives": { + "version": "1.1.0", + "resolved": "http://localhost:8989/blobs/get/&Xc7wzjsjGc4j8E08ehRgLsKR8ETg+IRu5fMxRHJryZA=.sha256", + "integrity": "sha256-Xc7wzjsjGc4j8E08ehRgLsKR8ETg+IRu5fMxRHJryZA=" + }, "node-gyp-build": { "version": "3.2.2", "resolved": "http://localhost:8989/blobs/get/&4FlrrtCACsITHl9W+Fo8zZxpVDu6KPKVlqzJERNerJo=.sha256", @@ -597,6 +666,11 @@ "mem": "1.1.0" } }, + "over": { + "version": "0.0.5", + "resolved": "http://localhost:8989/blobs/get/&2SFVsXLBCq9u2cfVbIv27fccQ+MOAk5V8HAOWGbFwK4=.sha256", + "integrity": "sha256-2SFVsXLBCq9u2cfVbIv27fccQ+MOAk5V8HAOWGbFwK4=" + }, "p-finally": { "version": "1.0.0", "resolved": "http://localhost:8989/blobs/get/&7/2E4J4TMFQqhKJD8fTaIacA1Fm4N2HqyhYHDrH7iEE=.sha256", @@ -699,8 +773,8 @@ "integrity": "sha256-+uVE8RHNwJIJa68sQGICGbxnCTCOdLLhSt/Pb4NmgL0=" }, "pull-defer": { - "version": "0.2.2", - "resolved": "http://localhost:8989/blobs/get/&mDI2o/JC9yvFOVbeAhDN+hwTm7cK+dBwISYO49EMY24=.sha256" + "version": "http://localhost:8989/blobs/get/&mDI2o/JC9yvFOVbeAhDN+hwTm7cK+dBwISYO49EMY24=.sha256", + "integrity": "sha1-CIew/7MK8ypW2+z6csFnInHwexM=" }, "pull-git-packidx-parser": { "version": "http://localhost:8989/blobs/get/&ou0MPQZabBgzrHDu54jzLU3Sc6Rf5a/lost0whPQUJ0=.sha256", @@ -777,8 +851,8 @@ } }, "pull-many": { - "version": "1.0.8", - "resolved": "http://localhost:8989/blobs/get/&wBHfPheBEjwjjkNoeM5mWtU/tYDs8+xnt5w6C1+EK3g=.sha256", + "version": "http://localhost:8989/blobs/get/&wBHfPheBEjwjjkNoeM5mWtU/tYDs8+xnt5w6C1+EK3g=.sha256", + "integrity": "sha1-Pa3ZttFWxUVyG9qNAAPdjqoGKT4=", "requires": { "pull-stream": "3.6.1" } @@ -840,6 +914,29 @@ "ws": "1.1.4" } }, + "pullstream": { + "version": "0.4.1", + "resolved": "http://localhost:8989/blobs/get/&dU4CcxT0bjZmQhlL7P9dpSxkrrm+K5JOy8/1SDVpwSs=.sha256", + "integrity": "sha256-dU4CcxT0bjZmQhlL7P9dpSxkrrm+K5JOy8/1SDVpwSs=", + "requires": { + "over": "0.0.5", + "readable-stream": "1.0.34", + "setimmediate": "1.0.5", + "slice-stream": "1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "http://localhost:8989/blobs/get/&0QEMlyAUePLZCW/tXOEhdYfVI4R19FBv4JyHzdW8VqY=.sha256", + "requires": { + "core-util-is": "http://localhost:8989/blobs/get/&pKRNq2V57ePgat5Y0m+P1kLq4JFT/VnGCPy3lRpJk5g=.sha256", + "inherits": "2.0.3", + "isarray": "http://localhost:8989/blobs/get/&PoREAgaWgArtkqCdTFJgKtx2FAo5tsjpT+fInPcKnwo=.sha256", + "string_decoder": "http://localhost:8989/blobs/get/&Pm5v/q/mFX6yJ4qQmvwLhFI0sTRG3KipUYwrebnCIIY=.sha256" + } + } + } + }, "rc": { "version": "1.2.1", "resolved": "http://localhost:8989/blobs/get/&XPry2asXmpIfJLFtlg5hZmO2uM0QlZd5nCr/AJBOLek=.sha256", @@ -895,6 +992,14 @@ "resolved": "http://localhost:8989/blobs/get/&Y6ct0k73eVjQG88nl6Ll/lfhxdEVefvRvci3hMiZmcM=.sha256", "integrity": "sha256-Y6ct0k73eVjQG88nl6Ll/lfhxdEVefvRvci3hMiZmcM=" }, + "rimraf": { + "version": "2.6.2", + "resolved": "http://localhost:8989/blobs/get/&5u4iUQN5Nebmc0+RrO4XpcxAXxAREHuP7ND3+WhZKd4=.sha256", + "integrity": "sha256-5u4iUQN5Nebmc0+RrO4XpcxAXxAREHuP7ND3+WhZKd4=", + "requires": { + "glob": "http://localhost:8989/blobs/get/&zz0+R6Ewi1Ev1wfx31k3N8tWkHnQS/l14fxI3mpintE=.sha256" + } + }, "safe-buffer": { "version": "5.1.1", "resolved": "http://localhost:8989/blobs/get/&DBRHICIwyQWmEc+9TG2DT/zR9Zja3LyNW+dIJuXjsXE=.sha256", @@ -925,6 +1030,11 @@ "version": "http://localhost:8989/blobs/get/&2TSu59ueCdoJ6HckdDMV/+iIEwqm4E+73srJhfauaT0=.sha256", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "setimmediate": { + "version": "1.0.5", + "resolved": "http://localhost:8989/blobs/get/&XLn8ImmDZO1CwC1qo9xQ/+r6aEUq6EaZZy49/XSSLJ4=.sha256", + "integrity": "sha256-XLn8ImmDZO1CwC1qo9xQ/+r6aEUq6EaZZy49/XSSLJ4=" + }, "sha.js": { "version": "2.4.5", "resolved": "http://localhost:8989/blobs/get/&JxjiN22oEOlVmJQn3aII8ykRlfimM2Ph+URrf/2KZhs=.sha256", @@ -948,6 +1058,26 @@ "version": "http://localhost:8989/blobs/get/&2JAO0FDbn45cJ1KdQ8Al0Lu7FnN8oZnYa9UP3Ixb0tg=.sha256", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, + "slice-stream": { + "version": "1.0.0", + "resolved": "http://localhost:8989/blobs/get/&vZJmJejF0Cfij1r3a/BjIvBStHkzidwIh8n5j5k63Us=.sha256", + "integrity": "sha256-vZJmJejF0Cfij1r3a/BjIvBStHkzidwIh8n5j5k63Us=", + "requires": { + "readable-stream": "1.0.34" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "http://localhost:8989/blobs/get/&0QEMlyAUePLZCW/tXOEhdYfVI4R19FBv4JyHzdW8VqY=.sha256", + "requires": { + "core-util-is": "http://localhost:8989/blobs/get/&pKRNq2V57ePgat5Y0m+P1kLq4JFT/VnGCPy3lRpJk5g=.sha256", + "inherits": "2.0.3", + "isarray": "http://localhost:8989/blobs/get/&PoREAgaWgArtkqCdTFJgKtx2FAo5tsjpT+fInPcKnwo=.sha256", + "string_decoder": "http://localhost:8989/blobs/get/&Pm5v/q/mFX6yJ4qQmvwLhFI0sTRG3KipUYwrebnCIIY=.sha256" + } + } + } + }, "smart-buffer": { "version": "1.1.15", "resolved": "http://localhost:8989/blobs/get/&WkLju39M7S7Fo0ag0wBPhdAHagB2f32gvxvGhsfZgfc=.sha256", @@ -1054,11 +1184,11 @@ } }, "ssb-contact": { - "version": "1.2.0", - "resolved": "http://localhost:8989/blobs/get/&V4bNrv533a7pKfPxw7Ok92hNWBN+eCbyybmi8cGMzrM=.sha256", + "version": "http://localhost:8989/blobs/get/&V4bNrv533a7pKfPxw7Ok92hNWBN+eCbyybmi8cGMzrM=.sha256", + "integrity": "sha1-lGp4PIQOWRbVEsRTiJYQlwWQzWw=", "requires": { - "pull-defer": "0.2.2", - "pull-many": "1.0.8", + "pull-defer": "http://localhost:8989/blobs/get/&mDI2o/JC9yvFOVbeAhDN+hwTm7cK+dBwISYO49EMY24=.sha256", + "pull-many": "http://localhost:8989/blobs/get/&wBHfPheBEjwjjkNoeM5mWtU/tYDs8+xnt5w6C1+EK3g=.sha256", "pull-stream": "3.6.1" } }, @@ -1076,14 +1206,6 @@ "version": "http://localhost:8989/blobs/get/&5yyYBZpB4w+X6kW42KMh43Xz9KP3XW79mYOj40/CHA4=.sha256", "integrity": "sha1-Fg4kETeCqcpegGByqnpl58hl2/I=" }, - "ssb-mentions": { - "version": "http://localhost:8989/blobs/get/&GKwsJ3ykvOXc24wyNuWBdmnCFpQ+Ltd9ggjoOuOF/Pg=.sha256", - "integrity": "sha1-en5LsSk2uHwNcjeC+b1ZMfuFef4=", - "requires": { - "ssb-marked": "http://localhost:8989/blobs/get/&5yyYBZpB4w+X6kW42KMh43Xz9KP3XW79mYOj40/CHA4=.sha256", - "ssb-ref": "http://localhost:8989/blobs/get/&wLimOD3785KVj7kBIAbjN6GH8EMhKRkcZSPrEMhdhks=.sha256" - } - }, "ssb-ref": { "version": "http://localhost:8989/blobs/get/&wLimOD3785KVj7kBIAbjN6GH8EMhKRkcZSPrEMhdhks=.sha256", "integrity": "sha1-XU7/xUXsD/1/wVuieCmmQLiir7o=", @@ -1176,6 +1298,11 @@ "resolved": "http://localhost:8989/blobs/get/&2+Rf668b9yZcJXMyQrwOesOLYy22qOGfA0GvR3BCWJk=.sha256", "integrity": "sha256-2+Rf668b9yZcJXMyQrwOesOLYy22qOGfA0GvR3BCWJk=" }, + "traverse": { + "version": "0.3.9", + "resolved": "http://localhost:8989/blobs/get/&eBP2sa38eIi/Jilni5wRnDaHQPA0jLlcxH4Fex4WCUI=.sha256", + "integrity": "sha256-eBP2sa38eIi/Jilni5wRnDaHQPA0jLlcxH4Fex4WCUI=" + }, "tweetnacl": { "version": "0.14.5", "resolved": "http://localhost:8989/blobs/get/&bOoz1nqb2D+L0lBlXHiiyJ6pErzGvpHI5lgHzmnP39Y=.sha256", @@ -1194,6 +1321,31 @@ "resolved": "http://localhost:8989/blobs/get/&x9CnHOGgcWXdpCT6uvXvsVHIbcTZ12GYkjEOgXE31BQ=.sha256", "integrity": "sha256-x9CnHOGgcWXdpCT6uvXvsVHIbcTZ12GYkjEOgXE31BQ=" }, + "unzip": { + "version": "0.1.11", + "resolved": "http://localhost:8989/blobs/get/&lUPDF4rcMYrDcMNc3I0oVd9vWMwOqoEHf7ycdfWAjOo=.sha256", + "integrity": "sha256-lUPDF4rcMYrDcMNc3I0oVd9vWMwOqoEHf7ycdfWAjOo=", + "requires": { + "binary": "0.3.0", + "fstream": "0.1.31", + "match-stream": "0.0.2", + "pullstream": "0.4.1", + "readable-stream": "1.0.34", + "setimmediate": "1.0.5" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "http://localhost:8989/blobs/get/&0QEMlyAUePLZCW/tXOEhdYfVI4R19FBv4JyHzdW8VqY=.sha256", + "requires": { + "core-util-is": "http://localhost:8989/blobs/get/&pKRNq2V57ePgat5Y0m+P1kLq4JFT/VnGCPy3lRpJk5g=.sha256", + "inherits": "2.0.3", + "isarray": "http://localhost:8989/blobs/get/&PoREAgaWgArtkqCdTFJgKtx2FAo5tsjpT+fInPcKnwo=.sha256", + "string_decoder": "http://localhost:8989/blobs/get/&Pm5v/q/mFX6yJ4qQmvwLhFI0sTRG3KipUYwrebnCIIY=.sha256" + } + } + } + }, "validate-npm-package-license": { "version": "http://localhost:8989/blobs/get/&FZFTRu/blzraNxOJt63Ut68JKa3cpNPwb1RBvaWZBW4=.sha256", "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", diff --git a/package.json b/package.json index 76417ae..d4f48d7 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "ssb-marked": "^0.7.1", "ssb-mentions": "http://localhost:8989/blobs/get/&GjuxknqKwJqHznKueFNCyIh52v1woz5PB41vqmoHfyM=.sha256", "ssb-sort": "^1.0.0", - "stream-to-pull-stream": "^1.7.2" + "stream-to-pull-stream": "^1.7.2", + "unzip": "^0.1.11" }, "scripts": { "start": "ssb-client ." -- cgit v1.2.3 From c3d96d538721cc63ce7226a5ccf0bf754ec5054e Mon Sep 17 00:00:00 2001 From: cel Date: Sat, 30 Dec 2017 13:56:04 -1000 Subject: Render links to ssb-blob:// zip files --- lib/render-msg.js | 1 + lib/render.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/lib/render-msg.js b/lib/render-msg.js index 3b498e0..92f8531 100644 --- a/lib/render-msg.js +++ b/lib/render-msg.js @@ -800,6 +800,7 @@ RenderMsg.prototype.valueTable = function (val, depth, cb) { case 'string': if (val[0] === '#') return cb(null, h('a', {href: self.toUrl('/channel/' + val.substr(1))}, val)) if (u.isRef(val)) return self.link1(val, cb) + if (/^ssb-blob:\/\//.test(val)) return cb(), h('a', {href: self.toUrl(val)}, val) return cb(), self.linkify(val) case 'boolean': return cb(), h('input', { diff --git a/lib/render.js b/lib/render.js index 95c8c50..df4f112 100644 --- a/lib/render.js +++ b/lib/render.js @@ -186,6 +186,9 @@ Render.prototype.toUrl = function (href) { var mentions = this._mentions if (mentions && href in this._mentions) href = this._mentions[href] if (/^ssb:\/\//.test(href)) href = href.substr(6) + if (/^ssb-blob:\/\//.test(href)) { + return this.opts.base + 'zip/' + href.substr(11) + } switch (href[0]) { case '%': if (!u.isRef(href)) return false -- cgit v1.2.3 From 0f2d21603df91e7285c4a50da9d9dadf95f600b6 Mon Sep 17 00:00:00 2001 From: cel Date: Sat, 30 Dec 2017 16:54:54 -1000 Subject: Render about-resource messages --- lib/render-msg.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/render-msg.js b/lib/render-msg.js index 92f8531..d4ad670 100644 --- a/lib/render-msg.js +++ b/lib/render-msg.js @@ -286,6 +286,7 @@ RenderMsg.prototype.message = function (cb) { case 'talenet-idea-update': return this.ideaUpdate(cb) case 'talenet-idea-comment': case 'talenet-idea-comment_reply': return this.ideaComment(cb) + case 'about-resource': return this.aboutResource(cb) default: return this.object(cb) } } @@ -1627,3 +1628,13 @@ RenderMsg.prototype.ideaComment = function (cb) { ]), cb) }) } + +RenderMsg.prototype.aboutResource = function (cb) { + var self = this + return self.wrap(h('div', + 'describes resource ', + h('a', {href: self.toUrl(self.c.about)}, self.c.name), + ':', + h('blockquote', {innerHTML: self.render.markdown(self.c.description)}) + ), cb) +} -- cgit v1.2.3 From 240833027314bd38f229bf5f70a92963378c2117 Mon Sep 17 00:00:00 2001 From: cel Date: Wed, 3 Jan 2018 18:25:42 -1000 Subject: Add previewContacts option Allow previewing a contact messages before publishing it --- README.md | 1 + lib/app.js | 1 + lib/serve.js | 11 +++++++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6d59e18..66e95cd 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ To make config options persistent, set them in `~/.ssb/config`, e.g.: - `filter`: Filter setting. `"all"` to show all messages. `"invert"` to show messages that would be hidden by the default setting. Otherwise the default setting applies, which is so to only show messages authored or upvoted by yourself or by a feed that you you follow. Exceptions are that if you navigate to a user feed page, you will see messages authored by that feed, and if you navigate to a message page, you will see that message - regardless of the filter setting. The `filter` setting may also be specified per-request as a query string parameter. - `showPrivates`: Whether or not to show private messages. Default is `true`. Overridden by `filter=all`. - `previewVotes`: Whether to preview creating votes/likes/digs (`true`) or publish them immediately (`false`). default: `false` +- `previewContacts`: Whether to preview creating contact/(un)follow/block messages (`true`) or publish them immediately (`false`). default: `false` - `ooo`: if true, use `ssb-ooo` to try to fetch missing messages in threads. also can set per-request with query string `?ooo=1`. default: `false` ## TODO diff --git a/lib/app.js b/lib/app.js index 2606067..f3b1774 100644 --- a/lib/app.js +++ b/lib/app.js @@ -32,6 +32,7 @@ function App(sbot, config) { this.msgFilter = conf.filter this.showPrivates = conf.showPrivates == null ? true : conf.showPrivates this.previewVotes = conf.previewVotes == null ? false : conf.previewVotes + this.previewContacts = conf.previewContacts == null ? false : conf.previewContacts this.useOoo = conf.ooo == null ? false : conf.ooo var base = conf.base || '/' diff --git a/lib/serve.js b/lib/serve.js index abf15c3..7c42d0c 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -197,7 +197,7 @@ Serve.prototype.publishVote = function (next) { } } -Serve.prototype.publishContact = function (cb) { +Serve.prototype.publishContact = function (next) { var content = { type: 'contact', contact: this.data.contact, @@ -206,7 +206,14 @@ Serve.prototype.publishContact = function (cb) { if (this.data.block) content.blocking = true if (this.data.unfollow) content.following = false if (this.data.unblock) content.blocking = false - this.publish(content, cb) + if (this.app.previewContacts) { + var json = JSON.stringify(content, 0, 2) + var q = qs.stringify({text: json, action: 'preview'}) + var url = this.app.render.toUrl('/compose?' + q) + this.redirect(url) + } else { + this.publish(content, next) + } } Serve.prototype.publishAttend = function (cb) { -- cgit v1.2.3 From a6b3929f8e156244dc22cae546a15079c1754f48 Mon Sep 17 00:00:00 2001 From: cel Date: Wed, 27 Dec 2017 14:35:42 -1000 Subject: Factor out git internals --- lib/app.js | 4 +- lib/git.js | 667 ----------------------------------------------------------- package.json | 4 +- 3 files changed, 3 insertions(+), 672 deletions(-) delete mode 100644 lib/git.js diff --git a/lib/app.js b/lib/app.js index f3b1774..1d9b80f 100644 --- a/lib/app.js +++ b/lib/app.js @@ -11,7 +11,7 @@ var About = require('./about') var Follows = require('./follows') var Serve = require('./serve') var Render = require('./render') -var Git = require('./git') +var Git = require('ssb-git') var cat = require('pull-cat') var proc = require('child_process') var toPull = require('stream-to-pull-stream') @@ -62,7 +62,7 @@ function App(sbot, config) { this.unboxMsg = this.unboxMsg.bind(this) this.render = new Render(this, this.opts) - this.git = new Git(this) + this.git = new Git(this.sbot, this.config) this.contacts = new Contacts(this.sbot) this.follows = new Follows(this.sbot, this.contacts) diff --git a/lib/git.js b/lib/git.js deleted file mode 100644 index cfcea9e..0000000 --- a/lib/git.js +++ /dev/null @@ -1,667 +0,0 @@ -var pull = require('pull-stream') -var paramap = require('pull-paramap') -var lru = require('hashlru') -var memo = require('asyncmemo') -var u = require('./util') -var packidx = require('pull-git-packidx-parser') -var Reader = require('pull-reader') -var toPull = require('stream-to-pull-stream') -var zlib = require('zlib') -var looper = require('looper') -var multicb = require('multicb') -var kvdiff = require('pull-kvdiff') - -var ObjectNotFoundError = u.customError('ObjectNotFoundError') - -var types = { - blob: true, - commit: true, - tree: true, -} -var emptyBlobHash = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391' - -module.exports = Git - -function Git(app) { - this.app = app - - this.findObject = memo({ - cache: lru(5), - asString: function (opts) { - return opts.obj + opts.headMsgId - } - }, this._findObject.bind(this)) - - this.findObjectInMsg = memo({ - cache: lru(5), - asString: function (opts) { - return opts.obj + opts.msg - } - }, this._findObjectInMsg.bind(this)) - - this.getPackIndex = memo({ - cache: lru(4), - asString: JSON.stringify - }, this._getPackIndex.bind(this)) -} - -// open, read, buffer and callback an object -Git.prototype.getObject = function (opts, cb) { - var self = this - self.openObject(opts, function (err, obj) { - if (err) return cb(err) - pull( - self.readObject(obj), - u.pullConcat(cb) - ) - }) -} - -// get a message that pushed an object -Git.prototype.getObjectMsg = function (opts, cb) { - this.findObject(opts, function (err, loc) { - if (err) return cb(err) - cb(null, loc.msg) - }) -} - -Git.prototype.openObject = function (opts, cb) { - var self = this - self.findObjectInMsg(opts, function (err, loc) { - if (err) return cb(err) - self.app.ensureHasBlobs([loc.packLink], function (err) { - if (err) return cb(err) - cb(null, { - type: opts.type, - length: opts.length, - offset: loc.offset, - next: loc.next, - packLink: loc.packLink, - idx: loc.idx, - msg: loc.msg, - }) - }) - }) -} - -Git.prototype.readObject = function (obj) { - if (obj.offset === obj.next) return pull.empty() - return pull( - this.app.readBlobSlice(obj.packLink, {start: obj.offset, end: obj.next}), - this.decodeObject({ - type: obj.type, - length: obj.length, - packLink: obj.packLink, - idx: obj.idx, - }) - ) -} - -// find which packfile contains a git object, and where in the packfile it is -// located -Git.prototype._findObject = function (opts, cb) { - if (!opts.headMsgId) return cb(new TypeError('missing head message id')) - if (!opts.obj) return cb(new TypeError('missing object id')) - var self = this - var objId = opts.obj - if (objId === emptyBlobHash) { - // special case: the empty blob may be found anywhere - self.app.getMsgDecrypted(opts.headMsgId, function (err, msg) { - if (err) return cb(err) - return cb(null, { - offset: 0, - next: 0, - packLink: null, - idx: null, - msg: msg, - }) - }) - } - self.findObjectMsgs(opts, function (err, msgs) { - if (err) return cb(err) - if (msgs.length === 0) - return cb(new ObjectNotFoundError('unable to find git object ' + objId)) - self.findObjectInMsgs(objId, msgs, cb) - }) -} - -Git.prototype._findObjectInMsg = function (opts, cb) { - if (!opts.msg) return cb(new TypeError('missing message id')) - if (!opts.obj) return cb(new TypeError('missing object id')) - var self = this - self.app.getMsgDecrypted(opts.msg, function (err, msg) { - if (err) return cb(err) - self.findObjectInMsgs(opts.obj, [msg], cb) - }) -} - -Git.prototype.findObjectInMsgs = function (objId, msgs, cb) { - var self = this - var objIdBuf = new Buffer(objId, 'hex') - // if blobs may need to be fetched, try to ask the user about as many of them - // at one time as possible - var packidxs = [].concat.apply([], msgs.map(function (msg) { - var c = msg.value.content - var idxs = u.toArray(c.indexes).map(u.toLink) - return u.toArray(c.packs).map(u.toLink).map(function (pack, i) { - var idx = idxs[i] - if (pack && idx) return { - msg: msg, - packLink: pack, - idxLink: idx, - } - }) - })).filter(Boolean) - var blobLinks = packidxs.length === 1 - ? [packidxs[0].idxLink, packidxs[0].packLink] - : packidxs.map(function (packidx) { - return packidx.idxLink - }) - self.app.ensureHasBlobs(blobLinks, function (err) { - if (err) return cb(err) - pull( - pull.values(packidxs), - paramap(function (pack, cb) { - self.getPackIndex(pack.idxLink, function (err, idx) { - if (err) return cb(err) - var offset = idx.find(objIdBuf) - if (!offset) return cb() - cb(null, { - offset: offset.offset, - next: offset.next, - packLink: pack.packLink, - idx: idx, - msg: pack.msg, - }) - }) - }, 4), - pull.filter(), - pull.take(1), - pull.collect(function (err, offsets) { - if (err) return cb(err) - if (offsets.length === 0) - return cb(new ObjectNotFoundError('unable to find git object ' - + objId + ' in ' + msgs.length + ' messages')) - cb(null, offsets[0]) - }) - ) - }) -} - -// given an object id and ssb msg id, get a set of messages of which at least one pushed the object. -Git.prototype.findObjectMsgs = function (opts, cb) { - var self = this - var id = opts.obj - var headMsgId = opts.headMsgId - var ended = false - var waiting = 0 - var maybeMsgs = [] - - function cbOnce(err, msgs) { - if (ended) return - ended = true - cb(err, msgs) - } - - function objectMatches(commit) { - return commit && (commit === id || commit.sha1 === id) - } - - if (!headMsgId) return cb(new TypeError('missing head message id')) - if (!u.isRef(headMsgId)) - return cb(new TypeError('bad head message id \'' + headMsgId + '\'')) - - ;(function getMsg(id) { - waiting++ - self.app.getMsgDecrypted(id, function (err, msg) { - waiting-- - if (ended) return - if (err && err.name == 'NotFoundError') - return cbOnce(new Error('missing message ' + headMsgId)) - if (err) return cbOnce(err) - var c = msg.value.content - if (typeof c === 'string') - return cbOnce(new Error('unable to decrypt message ' + msg.key)) - if ((u.toArray(c.object_ids).some(objectMatches)) - || (u.toArray(c.tags).some(objectMatches)) - || (u.toArray(c.commits).some(objectMatches))) { - // found the object - return cbOnce(null, [msg]) - } else if (!c.object_ids) { - // the object might be here - maybeMsgs.push(msg) - } - // traverse the DAG to keep looking for the object - u.toArray(c.repoBranch).filter(u.isRef).forEach(getMsg) - if (waiting === 0) { - cbOnce(null, maybeMsgs) - } - }) - })(headMsgId) -} - -Git.prototype._getPackIndex = function (idxBlobLink, cb) { - pull(this.app.readBlob(idxBlobLink), packidx(cb)) -} - -var objectTypes = [ - 'none', 'commit', 'tree', 'blob', - 'tag', 'unused', 'ofs-delta', 'ref-delta' -] - -function readTypedVarInt(reader, cb) { - var type, value, shift - reader.read(1, function (end, buf) { - if (ended = end) return cb(end) - var firstByte = buf[0] - type = objectTypes[(firstByte >> 4) & 7] - value = firstByte & 15 - shift = 4 - checkByte(firstByte) - }) - - function checkByte(byte) { - if (byte & 0x80) - reader.read(1, gotByte) - else - cb(null, type, value) - } - - function gotByte(end, buf) { - if (ended = end) return cb(end) - var byte = buf[0] - value += (byte & 0x7f) << shift - shift += 7 - checkByte(byte) - } -} - -function readVarInt(reader, cb) { - var value = 0, shift = 0 - reader.read(1, function gotByte(end, buf) { - if (ended = end) return cb(end) - var byte = buf[0] - value += (byte & 0x7f) << shift - shift += 7 - if (byte & 0x80) - reader.read(1, gotByte) - else - cb(null, value) - }) -} - -function inflate(read) { - return toPull(zlib.createInflate())(read) -} - -Git.prototype.decodeObject = function (opts) { - var self = this - var packLink = opts.packLink - return function (read) { - var reader = Reader() - reader(read) - return u.readNext(function (cb) { - readTypedVarInt(reader, function (end, type, length) { - if (end === true) cb(new Error('Missing object type')) - else if (end) cb(end) - else if (type === 'ref-delta') getObjectFromRefDelta(length, cb) - else if (opts.type && type !== opts.type) - cb(new Error('expected type \'' + opts.type + '\' ' + - 'but found \'' + type + '\'')) - else if (opts.length && length !== opts.length) - cb(new Error('expected length ' + opts.length + ' ' + - 'but found ' + length)) - else cb(null, inflate(reader.read())) - }) - }) - - function getObjectFromRefDelta(length, cb) { - reader.read(20, function (end, sourceHash) { - if (end) return cb(end) - var inflatedReader = Reader() - pull(reader.read(), inflate, inflatedReader) - readVarInt(inflatedReader, function (err, expectedSourceLength) { - if (err) return cb(err) - readVarInt(inflatedReader, function (err, expectedTargetLength) { - if (err) return cb(err) - var offset = opts.idx.find(sourceHash) - if (!offset) return cb(null, 'missing source object ' + - sourcehash.toString('hex')) - var readSource = pull( - self.app.readBlobSlice(opts.packLink, { - start: offset.offset, - end: offset.next - }), - self.decodeObject({ - type: opts.type, - length: expectedSourceLength, - packLink: opts.packLink, - idx: opts.idx - }) - ) - cb(null, patchObject(inflatedReader, length, readSource, expectedTargetLength)) - }) - }) - }) - } - } -} - -function readOffsetSize(cmd, reader, readCb) { - var offset = 0, size = 0 - - function addByte(bit, outPos, cb) { - if (cmd & (1 << bit)) - reader.read(1, function (err, buf) { - if (err) readCb(err) - else cb(buf[0] << (outPos << 3)) - }) - else - cb(0) - } - - addByte(0, 0, function (val) { - offset = val - addByte(1, 1, function (val) { - offset |= val - addByte(2, 2, function (val) { - offset |= val - addByte(3, 3, function (val) { - offset |= val - addSize() - }) - }) - }) - }) - function addSize() { - addByte(4, 0, function (val) { - size = val - addByte(5, 1, function (val) { - size |= val - addByte(6, 2, function (val) { - size |= val - readCb(null, offset, size || 0x10000) - }) - }) - }) - } -} - -function patchObject(deltaReader, deltaLength, readSource, targetLength) { - var srcBuf - var ended - - return u.readNext(function (cb) { - pull(readSource, u.pullConcat(function (err, buf) { - if (err) return cb(err) - srcBuf = buf - cb(null, read) - })) - }) - - function read(abort, cb) { - if (ended) return cb(ended) - deltaReader.read(1, function (end, dBuf) { - if (ended = end) return cb(end) - var cmd = dBuf[0] - if (cmd & 0x80) - // skip a variable amount and then pass through a variable amount - readOffsetSize(cmd, deltaReader, function (err, offset, size) { - if (err) return earlyEnd(err) - var buf = srcBuf.slice(offset, offset + size) - cb(end, buf) - }) - else if (cmd) - // insert `cmd` bytes from delta - deltaReader.read(cmd, cb) - else - cb(new Error("unexpected delta opcode 0")) - }) - - function earlyEnd(err) { - cb(err === true ? new Error('stream ended early') : err) - } - } -} - -var gitNameRegex = /^(.*) <(([^>@]*)(@[^>]*)?)> (.*) (.*)$/ -function parseName(line) { - var m = gitNameRegex.exec(line) - if (!m) return null - return { - name: m[1], - email: m[2], - localpart: m[3], - feed: u.isRef(m[4]) && m[4] || undefined, - date: new Date(m[5] * 1000), - tz: m[6], - } -} - -Git.prototype.getCommit = function (obj, cb) { - pull(this.readObject(obj), u.pullConcat(function (err, buf) { - if (err) return cb(err) - var commit = { - msg: obj.msg, - parents: [], - } - var authorLine, committerLine - var lines = buf.toString('utf8').split('\n') - for (var line; (line = lines.shift()); ) { - var parts = line.split(' ') - var prop = parts.shift() - var value = parts.join(' ') - switch (prop) { - case 'tree': - commit.tree = value - break - case 'parent': - commit.parents.push(value) - break - case 'author': - authorLine = value - break - case 'committer': - committerLine = value - break - case 'gpgsig': - var sigLines = [value] - while (lines[0] && lines[0][0] == ' ') - sigLines.push(lines.shift().slice(1)) - commit.gpgsig = sigLines.join('\n') - break - default: - return cb(new TypeError('unknown git object property ' + prop)) - } - } - commit.committer = parseName(committerLine) - if (authorLine !== committerLine) commit.author = parseName(authorLine) - commit.body = lines.join('\n') - cb(null, commit) - })) -} - -Git.prototype.getTag = function (obj, cb) { - pull(this.readObject(obj), u.pullConcat(function (err, buf) { - if (err) return cb(err) - var tag = { - msg: obj.msg, - } - var authorLine, tagterLine - var lines = buf.toString('utf8').split('\n') - for (var line; (line = lines.shift()); ) { - var parts = line.split(' ') - var prop = parts.shift() - var value = parts.join(' ') - switch (prop) { - case 'object': - tag.object = value - break - case 'type': - if (!types[value]) - return cb(new TypeError('unknown git object type ' + type)) - tag.type = value - break - case 'tag': - tag.tag = value - break - case 'tagger': - tag.tagger = parseName(value) - break - default: - return cb(new TypeError('unknown git object property ' + prop)) - } - } - tag.body = lines.join('\n') - cb(null, tag) - })) -} - -function readCString(reader, cb) { - var chars = [] - var loop = looper(function () { - reader.read(1, next) - }) - function next(err, ch) { - if (err) return cb(err) - if (ch[0] === 0) return cb(null, Buffer.concat(chars).toString('utf8')) - chars.push(ch) - loop() - } - loop() -} - -Git.prototype.readTree = function (obj) { - var self = this - var reader = Reader() - reader(this.readObject(obj)) - return function (abort, cb) { - if (abort) return reader.abort(abort, cb) - readCString(reader, function (err, str) { - if (err) return cb(err) - var parts = str.split(' ') - var mode = parseInt(parts[0], 8) - var name = parts.slice(1).join(' ') - reader.read(20, function (err, hash) { - if (err) return cb(err) - cb(null, { - name: name, - mode: mode, - hash: hash.toString('hex'), - type: mode === 0040000 ? 'tree' : - mode === 0160000 ? 'commit' : 'blob', - }) - }) - }) - } -} - -Git.prototype.readCommitChanges = function (commit) { - var self = this - return u.readNext(function (cb) { - var done = multicb({pluck: 1}) - commit.parents.forEach(function (rev) { - var cb = done() - self.getObjectMsg({ - obj: rev, - headMsgId: commit.msg.key, - type: 'commit', - }, function (err, msg) { - if (err) return cb(err) - self.openObject({ - obj: rev, - msg: msg.key, - }, function (err, obj) { - if (err) return cb(err) - self.getCommit(obj, cb) - }) - }) - }) - done()(null, commit) - done(function (err, commits) { - if (err) return cb(err) - var done = multicb({pluck: 1}) - commits.forEach(function (commit) { - var cb = done() - if (!commit.tree) return cb(null, pull.empty()) - self.getObjectMsg({ - obj: commit.tree, - headMsgId: commit.msg.key, - type: 'tree', - }, function (err, msg) { - if (err) return cb(err) - self.openObject({ - obj: commit.tree, - msg: commit.msg.key, - }, cb) - }) - }) - done(function (err, trees) { - if (err) return cb(err) - cb(null, self.diffTreesRecursive(trees)) - }) - }) - }) -} - -Git.prototype.diffTrees = function (objs) { - var self = this - return pull( - kvdiff(objs.map(function (obj) { - return self.readTree(obj) - }), 'name'), - pull.map(function (item) { - var diff = item.diff || {} - var head = item.values[item.values.length-1] - var created = true - for (var k = 0; k < item.values.length-1; k++) - if (item.values[k]) created = false - return { - name: item.key, - hash: item.values.map(function (val) { return val.hash }), - mode: diff.mode, - type: item.values.map(function (val) { return val.type }), - deleted: !head, - created: created - } - }) - ) -} - -Git.prototype.diffTreesRecursive = function (objs) { - var self = this - return pull( - self.diffTrees(objs), - paramap(function (item, cb) { - if (!item.type.some(function (t) { return t === 'tree' })) - return cb(null, [item]) - var done = multicb({pluck: 1}) - item.type.forEach(function (type, i) { - var cb = done() - if (type !== 'tree') return cb(null, pull.once(item)) - var hash = item.hash[i] - self.getObjectMsg({ - obj: hash, - headMsgId: objs[i].msg.key, - }, function (err, msg) { - if (err) return cb(err) - self.openObject({ - obj: hash, - msg: msg.key, - }, cb) - }) - }) - done(function (err, objs) { - if (err) return cb(err) - cb(null, pull( - self.diffTreesRecursive(objs), - pull.map(function (f) { - f.name = item.name + '/' + f.name - return f - }) - )) - }) - }, 4), - pull.flatten() - ) -} diff --git a/package.json b/package.json index d4f48d7..9a27bc1 100644 --- a/package.json +++ b/package.json @@ -13,21 +13,19 @@ "human-time": "^0.0.1", "hyperscript": "^2.0.2", "jpeg-autorotate": "^3.0.0", - "looper": "^4.0.0", "mime-types": "^2.1.12", "multicb": "^1.2.1", "pull-box-stream": "^1.0.12", "pull-cat": "^1.1.11", - "pull-git-packidx-parser": "^1.0.0", "pull-hyperscript": "^0.2.2", "pull-identify-filetype": "^1.1.0", - "pull-kvdiff": "^0.0.1", "pull-paginate": "^1.0.0", "pull-paramap": "^1.2.1", "pull-reader": "^1.2.9", "pull-stream": "^3.5.0", "ssb-client": "http://localhost:8989/blobs/get/&EAaUpI+wrJM5/ly1RqZW0GAEF4PmCAmABBj7e6UIrL0=.sha256", "ssb-contact": "^1.2.0", + "ssb-git": "^0.6.0", "ssb-marked": "^0.7.1", "ssb-mentions": "http://localhost:8989/blobs/get/&GjuxknqKwJqHznKueFNCyIh52v1woz5PB41vqmoHfyM=.sha256", "ssb-sort": "^1.0.0", -- cgit v1.2.3 From bc7454fa1db558e89c8e8069d527c7b74a4ce60a Mon Sep 17 00:00:00 2001 From: cel Date: Mon, 8 Jan 2018 15:48:04 -1000 Subject: Use readTreeFull and pull-catch --- lib/serve.js | 32 ++++++++++++++------------------ package-lock.json | 46 +++++++++++++++++++++++++++++++--------------- package.json | 1 + 3 files changed, 46 insertions(+), 33 deletions(-) diff --git a/lib/serve.js b/lib/serve.js index 7c42d0c..eb8610c 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -22,6 +22,7 @@ var ph = require('pull-hyperscript') var emojis = require('emoji-named-characters') var jpeg = require('jpeg-autorotate') var unzip = require('unzip') +var Catch = require('pull-catch') module.exports = Serve @@ -1788,7 +1789,12 @@ Serve.prototype.gitCommit = function (rev) { : file.mode ? 'mode changed' : JSON.stringify(file)) ]) - }) + }), + Catch(function (err) { + if (err && err.name === 'ObjectNotFoundError') return + if (err && err.name === 'BlobNotFoundError') return self.askWantBlobsForm(err.links) + return false + }) )) ] ]), @@ -1909,23 +1915,8 @@ Serve.prototype.gitTree = function (rev) { ]), missingBlobs ? self.askWantBlobsForm(missingBlobs) : ph('table', [ pull( - self.app.git.readTree(obj), - paramap(function (file, cb) { - self.app.git.getObjectMsg({ - obj: file.hash, - headMsgId: obj.msg.key, - }, function (err, msg) { - if (err && err.name === 'ObjectNotFoundError') return cb(null, file) - if (err && err.name === 'BlobNotFoundError') return cb(null, {missingBlobs: err.links}) - if (err) return cb(err) - file.msg = msg - cb(null, file) - }) - }, 8), + self.app.git.readTreeFull(obj), pull.map(function (item) { - if (item.missingBlobs) { - return self.askWantBlobsForm(item.missingBlobs) - } if (!item.msg) return ph('tr', [ ph('td', u.escapeHTML(item.name) + (item.type === 'tree' ? '/' : '')), @@ -1950,7 +1941,12 @@ Serve.prototype.gitTree = function (rev) { }, htime(fileDate)) ), ]) - }) + }), + Catch(function (err) { + if (err && err.name === 'ObjectNotFoundError') return + if (err && err.name === 'BlobNotFoundError') return self.askWantBlobsForm(err.links) + return false + }) ) ]), ]), diff --git a/package-lock.json b/package-lock.json index 0f5d62f..8d381d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -492,6 +492,7 @@ "readable-stream": { "version": "1.0.34", "resolved": "http://localhost:8989/blobs/get/&0QEMlyAUePLZCW/tXOEhdYfVI4R19FBv4JyHzdW8VqY=.sha256", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "requires": { "core-util-is": "http://localhost:8989/blobs/get/&pKRNq2V57ePgat5Y0m+P1kLq4JFT/VnGCPy3lRpJk5g=.sha256", "inherits": "2.0.3", @@ -772,17 +773,15 @@ "resolved": "http://localhost:8989/blobs/get/&+uVE8RHNwJIJa68sQGICGbxnCTCOdLLhSt/Pb4NmgL0=.sha256", "integrity": "sha256-+uVE8RHNwJIJa68sQGICGbxnCTCOdLLhSt/Pb4NmgL0=" }, + "pull-catch": { + "version": "1.0.0", + "resolved": "http://localhost:8989/blobs/get/&K8t9klRhYJkdveaxSpBMFwzMtmNyLwNRTQUeCVnUvYU=.sha256", + "integrity": "sha256-K8t9klRhYJkdveaxSpBMFwzMtmNyLwNRTQUeCVnUvYU=" + }, "pull-defer": { "version": "http://localhost:8989/blobs/get/&mDI2o/JC9yvFOVbeAhDN+hwTm7cK+dBwISYO49EMY24=.sha256", "integrity": "sha1-CIew/7MK8ypW2+z6csFnInHwexM=" }, - "pull-git-packidx-parser": { - "version": "http://localhost:8989/blobs/get/&ou0MPQZabBgzrHDu54jzLU3Sc6Rf5a/lost0whPQUJ0=.sha256", - "integrity": "sha1-LYvwr+SCSJfuA4QL/k9ahq/syiE=", - "requires": { - "pull-stream": "3.6.1" - } - }, "pull-goodbye": { "version": "0.0.2", "resolved": "http://localhost:8989/blobs/get/&MQhN9Pp3BYaj0TzxK39JfAK7CjVceg1EPfNhhFnZ9sU=.sha256", @@ -842,14 +841,6 @@ } } }, - "pull-kvdiff": { - "version": "0.0.1", - "resolved": "http://localhost:8989/blobs/get/&xxfT88LrbK4FSIhpaNmIAt4jzsaBgVugeaDPVwC1gCU=.sha256", - "integrity": "sha256-xxfT88LrbK4FSIhpaNmIAt4jzsaBgVugeaDPVwC1gCU=", - "requires": { - "multicb": "1.2.2" - } - }, "pull-many": { "version": "http://localhost:8989/blobs/get/&wBHfPheBEjwjjkNoeM5mWtU/tYDs8+xnt5w6C1+EK3g=.sha256", "integrity": "sha1-Pa3ZttFWxUVyG9qNAAPdjqoGKT4=", @@ -928,6 +919,7 @@ "readable-stream": { "version": "1.0.34", "resolved": "http://localhost:8989/blobs/get/&0QEMlyAUePLZCW/tXOEhdYfVI4R19FBv4JyHzdW8VqY=.sha256", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "requires": { "core-util-is": "http://localhost:8989/blobs/get/&pKRNq2V57ePgat5Y0m+P1kLq4JFT/VnGCPy3lRpJk5g=.sha256", "inherits": "2.0.3", @@ -1069,6 +1061,7 @@ "readable-stream": { "version": "1.0.34", "resolved": "http://localhost:8989/blobs/get/&0QEMlyAUePLZCW/tXOEhdYfVI4R19FBv4JyHzdW8VqY=.sha256", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "requires": { "core-util-is": "http://localhost:8989/blobs/get/&pKRNq2V57ePgat5Y0m+P1kLq4JFT/VnGCPy3lRpJk5g=.sha256", "inherits": "2.0.3", @@ -1206,6 +1199,28 @@ "version": "http://localhost:8989/blobs/get/&5yyYBZpB4w+X6kW42KMh43Xz9KP3XW79mYOj40/CHA4=.sha256", "integrity": "sha1-Fg4kETeCqcpegGByqnpl58hl2/I=" }, + "ssb-mentions": { + "version": "http://localhost:8989/blobs/get/&GjuxknqKwJqHznKueFNCyIh52v1woz5PB41vqmoHfyM=.sha256", + "integrity": "sha1-AMd4SslTCsJi2h5902MlCCA1UEY=", + "requires": { + "ssb-marked": "http://localhost:8989/blobs/get/&5yyYBZpB4w+X6kW42KMh43Xz9KP3XW79mYOj40/CHA4=.sha256", + "ssb-ref": "2.7.1" + }, + "dependencies": { + "is-valid-domain": { + "version": "0.0.2", + "resolved": "http://localhost:8989/blobs/get/&phjiap+k1lGC5LPVba/w4Caomw5o0NQp2Hol+u/YAzE=.sha256" + }, + "ssb-ref": { + "version": "2.7.1", + "resolved": "http://localhost:8989/blobs/get/&wLimOD3785KVj7kBIAbjN6GH8EMhKRkcZSPrEMhdhks=.sha256", + "requires": { + "ip": "1.1.5", + "is-valid-domain": "0.0.2" + } + } + } + }, "ssb-ref": { "version": "http://localhost:8989/blobs/get/&wLimOD3785KVj7kBIAbjN6GH8EMhKRkcZSPrEMhdhks=.sha256", "integrity": "sha1-XU7/xUXsD/1/wVuieCmmQLiir7o=", @@ -1337,6 +1352,7 @@ "readable-stream": { "version": "1.0.34", "resolved": "http://localhost:8989/blobs/get/&0QEMlyAUePLZCW/tXOEhdYfVI4R19FBv4JyHzdW8VqY=.sha256", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "requires": { "core-util-is": "http://localhost:8989/blobs/get/&pKRNq2V57ePgat5Y0m+P1kLq4JFT/VnGCPy3lRpJk5g=.sha256", "inherits": "2.0.3", diff --git a/package.json b/package.json index 9a27bc1..62ec8d9 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "multicb": "^1.2.1", "pull-box-stream": "^1.0.12", "pull-cat": "^1.1.11", + "pull-catch": "^1.0.0", "pull-hyperscript": "^0.2.2", "pull-identify-filetype": "^1.1.0", "pull-paginate": "^1.0.0", -- cgit v1.2.3 From 27f1966a6512bd07a9fe91717a46d1a95281da32 Mon Sep 17 00:00:00 2001 From: cel Date: Mon, 8 Jan 2018 16:10:21 -1000 Subject: Render markdown files in git repos --- lib/serve.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/serve.js b/lib/serve.js index eb8610c..f146c49 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -2332,6 +2332,16 @@ Serve.prototype.wrapBinary = function (opts) { src: opts.rawUrl }) } + if (type === 'text/markdown') { + // TODO: rewrite links to files/images to be correct + return ph('blockquote', u.readNext(function (cb) { + pull.collect(function (err, bufs) { + if (err) return cb(pull.error(err)) + var text = Buffer.concat(bufs).toString('utf8') + return cb(null, pull.once(self.app.render.markdown(text))) + })(read) + })) + } return ph('pre', pull.map(function (buf) { return self.app.render.highlight(buf.toString('utf8'), ext) })(read)) -- cgit v1.2.3 From f752c762185f7493a5420f461bc8e766db84fc60 Mon Sep 17 00:00:00 2001 From: cel Date: Mon, 8 Jan 2018 17:17:18 -1000 Subject: Render git diffs --- lib/serve.js | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++- package-lock.json | 4 ++ package.json | 1 + static/styles.css | 3 + 4 files changed, 187 insertions(+), 2 deletions(-) diff --git a/lib/serve.js b/lib/serve.js index f146c49..de2757e 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -23,6 +23,7 @@ var emojis = require('emoji-named-characters') var jpeg = require('jpeg-autorotate') var unzip = require('unzip') var Catch = require('pull-catch') +var Diff = require('diff') module.exports = Serve @@ -1648,6 +1649,7 @@ Serve.prototype.git = function (url) { case 'tree': return this.gitTree(m[2]) case 'blob': return this.gitBlob(m[2]) case 'raw': return this.gitRaw(m[2]) + case 'diff': return this.gitDiff(m[2]) default: return this.respond(404, 'Not found') } } @@ -1782,10 +1784,14 @@ Serve.prototype.gitCommit = function (rev) { pull.map(function (file) { return ph('tr', [ ph('td', ph('code', u.escapeHTML(file.name))), - // ph('td', ph('code', u.escapeHTML(JSON.stringify(file.msg)))), ph('td', file.deleted ? 'deleted' : file.created ? 'created' - : file.hash ? 'changed' + : file.hash ? + ph('a', {href: + self.app.render.toUrl('/git/diff/' + + file.hash[0] + '..' + file.hash[1] + + '?msg=' + encodeURIComponent(obj.msg.key)) + }, 'changed') : file.mode ? 'mode changed' : JSON.stringify(file)) ]) @@ -2016,6 +2022,177 @@ Serve.prototype.gitBlob = function (rev) { }) } +Serve.prototype.gitDiff = function (revs) { + var self = this + var parts = revs.split('..') + if (parts.length !== 2) return pull( + ph('div.error', 'revs should be ..'), + self.wrapPage('git diff'), + self.respondSink(400) + ) + var rev1 = parts[0] + var rev2 = parts[1] + if (!/[0-9a-f]{24}/.test(rev1)) return pull( + ph('div.error', 'rev 1 is not a git object id'), + self.wrapPage('git diff'), + self.respondSink(400) + ) + if (!/[0-9a-f]{24}/.test(rev2)) return pull( + ph('div.error', 'rev 2 is not a git object id'), + self.wrapPage('git diff'), + self.respondSink(400) + ) + + if (!u.isRef(self.query.msg)) return pull( + ph('div.error', 'missing message id'), + self.wrapPage('git diff'), + self.respondSink(400) + ) + + var done = multicb({pluck: 1, spread: true}) + // the msg qs param should point to the message for rev2 object. the msg for + // rev1 object we will have to look up. + self.app.git.getObjectMsg({ + obj: rev1, + headMsgId: self.query.msg, + type: 'blob', + }, done()) + self.getMsgDecryptedMaybeOoo(self.query.msg, done()) + done(function (err, msg1, msg2) { + if (err) return pull( + pull.once(u.renderError(err).outerHTML), + self.wrapPage('git diff ' + revs), + self.respondSink(400) + ) + var msg1Date = new Date(msg1.value.timestamp) + var msg2Date = new Date(msg2.value.timestamp) + var revsShort = rev1.substr(0, 8) + '..' + rev2.substr(0, 8) + pull( + ph('section', [ + ph('h3', ph('a', {href: ''}, revsShort)), + ph('div', [ + ph('a', { + href: self.app.render.toUrl('/git/blob/' + rev1 + '?msg=' + encodeURIComponent(msg1.key)) + }, rev1), ' ', + self.phIdLink(msg1.value.author), ' ', + ph('a', { + href: self.app.render.toUrl(msg1.key), + title: msg1Date.toLocaleString(), + }, htime(msg1Date)) + ]), + ph('div', [ + ph('a', { + href: self.app.render.toUrl('/git/blob/' + rev2 + '?msg=' + encodeURIComponent(msg2.key)) + }, rev2), ' ', + self.phIdLink(msg2.value.author), ' ', + ph('a', { + href: self.app.render.toUrl(msg2.key), + title: msg2Date.toLocaleString(), + }, htime(msg2Date)) + ]), + u.readNext(function (cb) { + var done = multicb({pluck: 1, spread: true}) + self.app.git.openObject({ + obj: rev1, + msg: msg1.key, + }, done()) + self.app.git.openObject({ + obj: rev2, + msg: msg2.key, + }, done()) + done(function (err, obj1, obj2) { + if (err && err.name === 'BlobNotFoundError') + return cb(null, self.askWantBlobsForm(err.links)) + if (err) return cb(err) + + var done = multicb({pluck: 1, spread: true}) + pull.collect(done())(self.app.git.readObject(obj1)) + pull.collect(done())(self.app.git.readObject(obj2)) + done(function (err, bufs1, bufs2) { + if (err) return cb(err) + var str1 = Buffer.concat(bufs1, obj1.length).toString('utf8') + var str2 = Buffer.concat(bufs2, obj2.length).toString('utf8') + var diff = Diff.structuredPatch('', '', str1, str2) + cb(null, self.gitDiffTable(diff)) + }) + }) + }) + ]), + self.wrapPage('git diff'), + self.respondSink(200) + ) + }) +} + +Serve.prototype.gitDiffTable = function (diff) { + var self = this + return pull( + ph('table', [ + ph('tr', [ + ]), + pull( + pull.values(diff.hunks), + pull.map(function (hunk) { + var oldLine = hunk.oldStart + var newLine = hunk.newStart + return [ + ph('tr', [ + ph('td', {colspan: 2}), + ph('td', ph('pre', + '@@ -' + oldLine + ',' + hunk.oldLines + ' ' + + '+' + newLine + ',' + hunk.newLines + ' @@')) + ]), + pull( + pull.values(hunk.lines), + pull.map(function (line) { + var s = line[0] + if (s == '\\') return + var html = self.app.render.highlight(line) + var lineNums = [s == '+' ? '' : oldLine++, s == '-' ? '' : newLine++] + // var id = [filename].concat(lineNums).join('-') + var newLineNum = lineNums[lineNums.length-1] + return ph('tr', { + class: s == '+' ? 'diff-new' : s == '-' ? 'diff-old' : '' + }, [ + lineNums.map(function (num, i) { + return ph('td', String(num)) + // TODO: allow linking to comments + /* + var idEnc = encodeURIComponent(id) + return '' + + (num ? '' + + num + '' + + (updateId && i === lineNums.length-1 && s !== '-' ? + ' ' + : '') + : '') + '' + } + */ + }), + ph('td', ph('pre', u.escapeHTML(html))) + ]) + + // TODO: line-comments + /* + (lineCommentThreads[newLineNum] ? + '' + + lineCommentThreads[newLineNum] + + '' + : commit && query.comment === id ? + '' + + forms.lineComment(req, repo, updateId, commit, filename, newLineNum) + + '' + : '') + */ + }) + ) + ] + }) + ) + ]) + ) +} + Serve.prototype.gitObjectLinks = function (headMsgId, type) { var self = this return paramap(function (id, cb) { diff --git a/package-lock.json b/package-lock.json index 8d381d4..3a9bf62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -180,6 +180,10 @@ "streamsearch": "0.1.2" } }, + "diff": { + "version": "3.3.1", + "resolved": "http://localhost:8989/blobs/get/&+6G9mvp4Q/5bujMKlbFdbrub2IXMPleXIqTqyyvY5Ww=.sha256" + }, "ed2curve": { "version": "0.1.4", "resolved": "http://localhost:8989/blobs/get/&X6VtEiGqSVFiIHAZAXFZXB5q9iUd3gULIU9c0xyynNo=.sha256", diff --git a/package.json b/package.json index 62ec8d9..65ae820 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "asyncmemo": "^1.1.0", "base64-url": "^2.0.0", "busboy": "^0.2.14", + "diff": "^3.3.1", "emoji-named-characters": "^1.0.2", "emoji-server": "^1.0.0", "hashlru": "^2.1.0", diff --git a/static/styles.css b/static/styles.css index c26cf59..b22074f 100644 --- a/static/styles.css +++ b/static/styles.css @@ -193,3 +193,6 @@ table.ssb-object td { .chess-square-dark { background-color: #ccc; } + +.diff-old { background-color: #ffe2dd; } +.diff-new { background-color: #d1ffd6; } -- cgit v1.2.3 From 498eaa3edf1241abdc6180410db97254c70e2185 Mon Sep 17 00:00:00 2001 From: cel Date: Mon, 8 Jan 2018 17:26:36 -1000 Subject: Linkify newly created git blobs --- lib/serve.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/serve.js b/lib/serve.js index de2757e..fb57bf7 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -1785,7 +1785,12 @@ Serve.prototype.gitCommit = function (rev) { return ph('tr', [ ph('td', ph('code', u.escapeHTML(file.name))), ph('td', file.deleted ? 'deleted' - : file.created ? 'created' + : file.created ? + ph('a', {href: + self.app.render.toUrl('/git/blob/' + + file.hash[0] + + '?msg=' + encodeURIComponent(obj.msg.key)) + }, 'created') : file.hash ? ph('a', {href: self.app.render.toUrl('/git/diff/' -- cgit v1.2.3 From cbf9a1d87eb43eaea73e6c019e7b4a2dca3bbaa6 Mon Sep 17 00:00:00 2001 From: cel Date: Mon, 8 Jan 2018 19:11:15 -1000 Subject: Render line-comment messages --- lib/app.js | 2 +- lib/render-msg.js | 36 ++++++++++++++++++++++++++++++++++++ lib/serve.js | 1 + 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/app.js b/lib/app.js index 1d9b80f..21f45a4 100644 --- a/lib/app.js +++ b/lib/app.js @@ -101,7 +101,7 @@ App.prototype.error = console.error.bind(console, logPrefix) App.prototype.unboxMsg = function (msg, cb) { var self = this - var c = msg.value && msg.value.content + var c = msg && msg.value && msg.value.content if (typeof c !== 'string') cb(null, msg) else self.unboxContent(c, function (err, content) { if (err) { diff --git a/lib/render-msg.js b/lib/render-msg.js index d4ad670..0835fee 100644 --- a/lib/render-msg.js +++ b/lib/render-msg.js @@ -10,6 +10,7 @@ function RenderMsg(render, app, msg, opts) { this.render = render this.app = app this.msg = msg + this.serve = opts.serve this.value = msg && msg.value || {} var content = this.value.content this.c = content || {} @@ -20,6 +21,13 @@ function RenderMsg(render, app, msg, opts) { this.shouldWrap = this.opts.wrap !== false } +RenderMsg.prototype.getMsg = function (id, cb) { + if (!id) return cb() + return this.serve + ? this.serve.getMsgDecryptedMaybeOoo(id, cb) + : this.app.getMsgDecryptedOoo(id, cb) +} + RenderMsg.prototype.toUrl = function (href) { return this.render.toUrl(href) } @@ -287,6 +295,7 @@ RenderMsg.prototype.message = function (cb) { case 'talenet-idea-comment': case 'talenet-idea-comment_reply': return this.ideaComment(cb) case 'about-resource': return this.aboutResource(cb) + case 'line-comment': return this.lineComment(cb) default: return this.object(cb) } } @@ -1638,3 +1647,30 @@ RenderMsg.prototype.aboutResource = function (cb) { h('blockquote', {innerHTML: self.render.markdown(self.c.description)}) ), cb) } + +RenderMsg.prototype.lineComment = function (cb) { + var self = this + var done = multicb({pluck: 1, spread: true}) + self.link(self.c.repo, done()) + self.getMsg(self.c.updateId, done()) + done(function (err, repoLink, updateMsg) { + if (err) return cb(err) + return self.wrap(h('div', + h('div', h('small', '> ', + repoLink, ' ', + h('a', { + href: self.toUrl(self.c.updateId) + }, + updateMsg + ? htime(new Date(updateMsg.value.timestamp)) + : String(self.c.updateId) + ), ' ', + h('a', { + href: self.toUrl('/git/commit/' + self.c.commitId + '?msg=' + encodeURIComponent(self.c.updateId)) + }, String(self.c.commitId).substr(0, 8)), ' ', + h('code', self.c.filePath + ':' + self.c.line) + )), + self.c.text ? + h('div', {innerHTML: self.render.markdown(self.c.text)}) : ''), cb) + }) +} diff --git a/lib/serve.js b/lib/serve.js index fb57bf7..f191b77 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -1276,6 +1276,7 @@ Serve.prototype.renderThread = function (opts) { msgId: opts && opts.msgId, filter: this.query.filter, limit: Number(this.query.limit), + serve: this, }), pull.map(u.toHTML) ) -- cgit v1.2.3 From 09cf04663fe2a772d5a06bbcf86b9014dc4f5228 Mon Sep 17 00:00:00 2001 From: cel Date: Mon, 8 Jan 2018 22:03:51 -1000 Subject: Render line comments on diffs --- lib/app.js | 75 ++++++++++++++++++++++++++++++ lib/render-msg.js | 5 +- lib/serve.js | 133 ++++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 174 insertions(+), 39 deletions(-) diff --git a/lib/app.js b/lib/app.js index 21f45a4..9166eb2 100644 --- a/lib/app.js +++ b/lib/app.js @@ -905,3 +905,78 @@ App.prototype.expandOoo = function (opts, cb) { } } } + +App.prototype.getLineComments = function (opts, cb) { + // get line comments for a git-update message and git object id. + // line comments include message id, commit id and path + // but we have message id and git object hash. + // look up the git object hash for each line-comment + // to verify that it is for the git object file we want + var updateId = opts.obj.msg.key + var objId = opts.hash + var self = this + var lineComments = {} + pull( + self.sbot.backlinks ? self.sbot.backlinks.read({ + query: [ + {$filter: { + dest: updateId, + value: { + content: { + type: 'line-comment', + updateId: updateId, + } + } + }} + ] + }) : pull( + self.sbot.links({ + dest: updateId, + rel: 'updateId', + values: true + }), + pull.filter(function (msg) { + var c = msg && msg.value && msg.value.content + return c && c.type === 'line-comment' + && c.updateId === updateId + }) + ), + paramap(function (msg, cb) { + var c = msg.value.content + self.git.getObjectAtPath({ + msg: updateId, + obj: c.commitId, + path: c.filePath, + }, function (err, info) { + if (err) return cb(err) + cb(null, { + obj: info.obj, + hash: info.hash, + msg: msg, + }) + }) + }, 4), + pull.filter(function (info) { + return info.hash === objId + }), + pull.drain(function (info) { + lineComments[info.msg.value.content.line] = info + }, function (err) { + cb(err, lineComments) + }) + ) +} + +App.prototype.getThread = function (msg) { + return cat([ + pull.once(msg), + this.sbot.backlinks ? this.sbot.backlinks.read({ + query: [ + {$filter: {dest: msg.key}} + ] + }) : this.sbot.links({ + dest: msg.key, + values: true + }) + ]) +} diff --git a/lib/render-msg.js b/lib/render-msg.js index 0835fee..78c7d5c 100644 --- a/lib/render-msg.js +++ b/lib/render-msg.js @@ -1668,7 +1668,10 @@ RenderMsg.prototype.lineComment = function (cb) { h('a', { href: self.toUrl('/git/commit/' + self.c.commitId + '?msg=' + encodeURIComponent(self.c.updateId)) }, String(self.c.commitId).substr(0, 8)), ' ', - h('code', self.c.filePath + ':' + self.c.line) + h('a', { + href: self.toUrl('/git/line-comment/' + + encodeURIComponent(self.msg.key)) + }, h('code', self.c.filePath + ':' + self.c.line)) )), self.c.text ? h('div', {innerHTML: self.render.markdown(self.c.text)}) : ''), cb) diff --git a/lib/serve.js b/lib/serve.js index f191b77..78ff104 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -1651,6 +1651,7 @@ Serve.prototype.git = function (url) { case 'blob': return this.gitBlob(m[2]) case 'raw': return this.gitRaw(m[2]) case 'diff': return this.gitDiff(m[2]) + case 'line-comment': return this.gitLineComment(m[2]) default: return this.respond(404, 'Not found') } } @@ -2114,12 +2115,16 @@ Serve.prototype.gitDiff = function (revs) { var done = multicb({pluck: 1, spread: true}) pull.collect(done())(self.app.git.readObject(obj1)) pull.collect(done())(self.app.git.readObject(obj2)) - done(function (err, bufs1, bufs2) { + self.app.getLineComments({obj: obj2, hash: rev2}, done()) + done(function (err, bufs1, bufs2, lineComments) { if (err) return cb(err) var str1 = Buffer.concat(bufs1, obj1.length).toString('utf8') var str2 = Buffer.concat(bufs2, obj2.length).toString('utf8') var diff = Diff.structuredPatch('', '', str1, str2) - cb(null, self.gitDiffTable(diff)) + cb(null, self.gitDiffTable(diff, lineComments, { + obj: obj2, + hash: rev2, + })) }) }) }) @@ -2130,12 +2135,10 @@ Serve.prototype.gitDiff = function (revs) { }) } -Serve.prototype.gitDiffTable = function (diff) { +Serve.prototype.gitDiffTable = function (diff, lineComments, lineCommentInfo) { var self = this return pull( ph('table', [ - ph('tr', [ - ]), pull( pull.values(diff.hunks), pull.map(function (hunk) { @@ -2157,39 +2160,42 @@ Serve.prototype.gitDiffTable = function (diff) { var lineNums = [s == '+' ? '' : oldLine++, s == '-' ? '' : newLine++] // var id = [filename].concat(lineNums).join('-') var newLineNum = lineNums[lineNums.length-1] - return ph('tr', { - class: s == '+' ? 'diff-new' : s == '-' ? 'diff-old' : '' - }, [ - lineNums.map(function (num, i) { - return ph('td', String(num)) - // TODO: allow linking to comments - /* - var idEnc = encodeURIComponent(id) - return '' + - (num ? '' + - num + '' + - (updateId && i === lineNums.length-1 && s !== '-' ? - ' ' - : '') - : '') + '' - } - */ - }), - ph('td', ph('pre', u.escapeHTML(html))) - ]) - - // TODO: line-comments - /* - (lineCommentThreads[newLineNum] ? - '' + - lineCommentThreads[newLineNum] + - '' - : commit && query.comment === id ? - '' + - forms.lineComment(req, repo, updateId, commit, filename, newLineNum) + - '' - : '') - */ + return [ + ph('tr', { + class: s == '+' ? 'diff-new' : s == '-' ? 'diff-old' : '' + }, [ + lineNums.map(function (num, i) { + return ph('td', String(num)) + // TODO: allow linking to comments + /* + var idEnc = encodeURIComponent(id) + return '' + + (num ? '' + + num + '' + + (updateId && i === lineNums.length-1 && s !== '-' ? + ' ' + : '') + : '') + '' + } + */ + }), + ph('td', ph('pre', u.escapeHTML(html))) + ]), + (lineComments[newLineNum] ? + ph('tr', + ph('td', {colspan: 4}, + self.renderLineCommentThread(lineComments[newLineNum]) + ) + ) + : /*commit && query.comment === id ? + ph('tr', + ph('td', {colspan: 4}, + // self.renderLineCommentForm(req, repo, updateId, commit, filename, newLineNum) + self.renderLineCommentForm(lineCommentInfo) + ) + ) + :*/ '') + ] }) ) ] @@ -2199,6 +2205,57 @@ Serve.prototype.gitDiffTable = function (diff) { ) } +Serve.prototype.renderLineCommentThread = function (lineComment) { + var self = this + return ph('table', {class: 'ssb-msgs'}, pull( + this.app.getThread(lineComment.msg), + self.renderThread() + )) +} + +Serve.prototype.renderLineCommentForm = function (info) { + var obj = info.obj + var hash = info.hash + return ph('pre', JSON.stringify(info, 0, 2)) +} + +Serve.prototype.gitLineComment = function (path) { + var self = this + var id = decodeURIComponent(String(path)) + self.getMsgDecryptedMaybeOoo(id, function (err, msg) { + if (err) return pull( + pull.once(u.renderError(err).outerHTML), + self.respondSink(400, {'Content-Type': ctype('html')}) + ) + var c = msg && msg.value && msg.value.content + if (!c) return pull( + pull.once('Missing message ' + id), + self.respondSink(500, {'Content-Type': ctype('html')}) + ) + self.app.git.getObjectAtPath({ + msg: c.updateId, + obj: c.commitId, + path: c.filePath, + }, function (err, obj) { + if (err && err.name === 'BlobNotFoundError') + return self.askWantBlobs(err.links) + if (err) return pull( + pull.once(err.stack), + self.respondSink(400, {'Content-Type': 'text/plain'}) + ) + + self.redirect(self.app.render.toUrl(publishedMsg.key)) + + /* + pull( + ph('pre', JSON.stringify(obj, 0, 2)), + self.respondSink(200) + ) + */ + }) + }) +} + Serve.prototype.gitObjectLinks = function (headMsgId, type) { var self = this return paramap(function (id, cb) { -- cgit v1.2.3 From 095093482e485e936b29ec0e42f5e51ca9545ac6 Mon Sep 17 00:00:00 2001 From: cel Date: Tue, 9 Jan 2018 10:35:28 -1000 Subject: whitespace --- lib/serve.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/serve.js b/lib/serve.js index 78ff104..6c8941c 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -1803,11 +1803,11 @@ Serve.prototype.gitCommit = function (rev) { : JSON.stringify(file)) ]) }), - Catch(function (err) { - if (err && err.name === 'ObjectNotFoundError') return - if (err && err.name === 'BlobNotFoundError') return self.askWantBlobsForm(err.links) - return false - }) + Catch(function (err) { + if (err && err.name === 'ObjectNotFoundError') return + if (err && err.name === 'BlobNotFoundError') return self.askWantBlobsForm(err.links) + return false + }) )) ] ]), -- cgit v1.2.3 From 3995d586a0d20ef009f8a98587482c5f7f47ff58 Mon Sep 17 00:00:00 2001 From: cel Date: Tue, 9 Jan 2018 18:07:30 -1000 Subject: Don't load unzip --- lib/serve.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/serve.js b/lib/serve.js index 7c42d0c..77cfdb4 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -21,7 +21,6 @@ var htime = require('human-time') var ph = require('pull-hyperscript') var emojis = require('emoji-named-characters') var jpeg = require('jpeg-autorotate') -var unzip = require('unzip') module.exports = Serve @@ -2233,6 +2232,7 @@ Serve.prototype.zip = function (url) { if (/^invalid/.test(err.message)) return self.respond(400, err.message) else return self.respond(500, err.message || err) } + var unzip = require('unzip') var parseUnzip = unzip.Parse() var gotEntry = false parseUnzip.on('entry', function (entry) { -- cgit v1.2.3 From 3c8e69899bcd403ed82bee2599bdd592150e5956 Mon Sep 17 00:00:00 2001 From: cel Date: Tue, 9 Jan 2018 19:04:39 -1000 Subject: Render line-comments and diffs better --- lib/render-msg.js | 2 +- lib/serve.js | 145 +++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 106 insertions(+), 41 deletions(-) diff --git a/lib/render-msg.js b/lib/render-msg.js index 78c7d5c..1603d24 100644 --- a/lib/render-msg.js +++ b/lib/render-msg.js @@ -1670,7 +1670,7 @@ RenderMsg.prototype.lineComment = function (cb) { }, String(self.c.commitId).substr(0, 8)), ' ', h('a', { href: self.toUrl('/git/line-comment/' + - encodeURIComponent(self.msg.key)) + encodeURIComponent(self.msg.key || JSON.stringify(self.msg))) }, h('code', self.c.filePath + ':' + self.c.line)) )), self.c.text ? diff --git a/lib/serve.js b/lib/serve.js index 6c8941c..6e3b451 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -1790,7 +1790,7 @@ Serve.prototype.gitCommit = function (rev) { : file.created ? ph('a', {href: self.app.render.toUrl('/git/blob/' - + file.hash[0] + + (file.hash[1] || file.hash[0]) + '?msg=' + encodeURIComponent(obj.msg.key)) }, 'created') : file.hash ? @@ -1798,6 +1798,8 @@ Serve.prototype.gitCommit = function (rev) { self.app.render.toUrl('/git/diff/' + file.hash[0] + '..' + file.hash[1] + '?msg=' + encodeURIComponent(obj.msg.key)) + + '&commit=' + rev + + '&path=' + encodeURIComponent(file.name) }, 'changed') : file.mode ? 'mode changed' : JSON.stringify(file)) @@ -2107,7 +2109,13 @@ Serve.prototype.gitDiff = function (revs) { obj: rev2, msg: msg2.key, }, done()) - done(function (err, obj1, obj2) { + /* + self.app.git.guessCommitAndPath({ + obj: rev2, + msg: msg2.key, + }, done()) + */ + done(function (err, obj1, obj2/*, info2*/) { if (err && err.name === 'BlobNotFoundError') return cb(null, self.askWantBlobsForm(err.links)) if (err) return cb(err) @@ -2124,6 +2132,8 @@ Serve.prototype.gitDiff = function (revs) { cb(null, self.gitDiffTable(diff, lineComments, { obj: obj2, hash: rev2, + commit: self.query.commit, // info2.commit, + path: self.query.path, // info2.path, })) }) }) @@ -2136,6 +2146,7 @@ Serve.prototype.gitDiff = function (revs) { } Serve.prototype.gitDiffTable = function (diff, lineComments, lineCommentInfo) { + var updateMsg = lineCommentInfo.obj.msg var self = this return pull( ph('table', [ @@ -2146,7 +2157,7 @@ Serve.prototype.gitDiffTable = function (diff, lineComments, lineCommentInfo) { var newLine = hunk.newStart return [ ph('tr', [ - ph('td', {colspan: 2}), + ph('td', {colspan: 3}), ph('td', ph('pre', '@@ -' + oldLine + ',' + hunk.oldLines + ' ' + '+' + newLine + ',' + hunk.newLines + ' @@')) @@ -2158,27 +2169,34 @@ Serve.prototype.gitDiffTable = function (diff, lineComments, lineCommentInfo) { if (s == '\\') return var html = self.app.render.highlight(line) var lineNums = [s == '+' ? '' : oldLine++, s == '-' ? '' : newLine++] - // var id = [filename].concat(lineNums).join('-') + var hash = lineCommentInfo.hash var newLineNum = lineNums[lineNums.length-1] + var id = hash + '-' + (newLineNum || (lineNums[0] + '-')) + var idEnc = encodeURIComponent(id) + var allowComment = s !== '-' + && self.query.commit && self.query.path return [ ph('tr', { class: s == '+' ? 'diff-new' : s == '-' ? 'diff-old' : '' }, [ lineNums.map(function (num, i) { - return ph('td', String(num)) - // TODO: allow linking to comments - /* - var idEnc = encodeURIComponent(id) - return '' + - (num ? '' + - num + '' + - (updateId && i === lineNums.length-1 && s !== '-' ? - ' ' - : '') - : '') + '' - } - */ + return ph('td', [ + ph('a', { + name: i === 0 ? idEnc : undefined, + href: '#' + idEnc + }, String(num)) + ]) }), + ph('td', + allowComment ? ph('a', { + href: '?msg=' + + encodeURIComponent(self.query.msg) + + '&comment=' + idEnc + + '&commit=' + encodeURIComponent(self.query.commit) + + '&path=' + encodeURIComponent(self.query.path) + + '#' + idEnc + }, '…') : '' + ), ph('td', ph('pre', u.escapeHTML(html))) ]), (lineComments[newLineNum] ? @@ -2187,14 +2205,20 @@ Serve.prototype.gitDiffTable = function (diff, lineComments, lineCommentInfo) { self.renderLineCommentThread(lineComments[newLineNum]) ) ) - : /*commit && query.comment === id ? + : newLineNum && lineCommentInfo && self.query.comment === id ? ph('tr', ph('td', {colspan: 4}, - // self.renderLineCommentForm(req, repo, updateId, commit, filename, newLineNum) - self.renderLineCommentForm(lineCommentInfo) + self.renderLineCommentForm({ + id: id, + line: newLineNum, + updateId: updateMsg.key, + repoId: updateMsg.value.content.repo, + commitId: lineCommentInfo.commit, + filePath: lineCommentInfo.path, + }) ) ) - :*/ '') + : '') ] }) ) @@ -2213,16 +2237,42 @@ Serve.prototype.renderLineCommentThread = function (lineComment) { )) } -Serve.prototype.renderLineCommentForm = function (info) { - var obj = info.obj - var hash = info.hash - return ph('pre', JSON.stringify(info, 0, 2)) +Serve.prototype.renderLineCommentForm = function (opts) { + return [ + this.phComposer({ + placeholder: 'comment on this line', + id: opts.id, + lineComment: opts + }) + ] +} + +// return a composer, pull-hyperscript style +Serve.prototype.phComposer = function (opts) { + var self = this + return u.readNext(function (cb) { + self.composer(opts, function (err, composer) { + if (err) return cb(err) + cb(null, pull.once(composer.outerHTML)) + }) + }) } Serve.prototype.gitLineComment = function (path) { var self = this - var id = decodeURIComponent(String(path)) - self.getMsgDecryptedMaybeOoo(id, function (err, msg) { + var id + try { + id = decodeURIComponent(String(path)) + if (id[0] === '%') { + return self.getMsgDecryptedMaybeOoo(id, gotMsg) + } else { + msg = JSON.parse(id) + } + } catch(e) { + return gotMsg(e) + } + gotMsg(null, msg) + function gotMsg(err, msg) { if (err) return pull( pull.once(u.renderError(err).outerHTML), self.respondSink(400, {'Content-Type': ctype('html')}) @@ -2232,28 +2282,35 @@ Serve.prototype.gitLineComment = function (path) { pull.once('Missing message ' + id), self.respondSink(500, {'Content-Type': ctype('html')}) ) - self.app.git.getObjectAtPath({ + self.app.git.diffFile({ msg: c.updateId, - obj: c.commitId, + commit: c.commitId, path: c.filePath, - }, function (err, obj) { + }, function (err, file) { if (err && err.name === 'BlobNotFoundError') return self.askWantBlobs(err.links) if (err) return pull( pull.once(err.stack), self.respondSink(400, {'Content-Type': 'text/plain'}) ) - - self.redirect(self.app.render.toUrl(publishedMsg.key)) - - /* - pull( - ph('pre', JSON.stringify(obj, 0, 2)), - self.respondSink(200) - ) - */ + var path + if (file.created) { + path = '/git/blob/' + file.hash[1] + + '?msg=' + encodeURIComponent(c.updateId) + + '&commit=' + c.commitId + + '&path=' + encodeURIComponent(c.filePath) + + '#' + file.hash[1] + '-' + c.line + } else { + path = '/git/diff/' + file.hash[0] + '..' + file.hash[1] + + '?msg=' + encodeURIComponent(c.updateId) + + '&commit=' + c.commitId + + '&path=' + encodeURIComponent(c.filePath) + + '#' + file.hash[1] + '-' + c.line + } + var url = self.app.render.toUrl(path) + self.redirect(url) }) - }) + } } Serve.prototype.gitObjectLinks = function (headMsgId, type) { @@ -2958,6 +3015,14 @@ Serve.prototype.composer = function (opts, cb) { type: 'post', text: String(data.text).replace(/\r\n/g, '\n'), } + if (opts.lineComment) { + content.type = 'line-comment' + content.updateId = opts.lineComment.updateId + content.repo = opts.lineComment.repoId + content.commitId = opts.lineComment.commitId + content.filePath = opts.lineComment.filePath + content.line = opts.lineComment.line + } var mentions = ssbMentions(data.text, {bareFeedNames: true, emoji: true}) .filter(function (mention) { if (mention.emoji) { -- cgit v1.2.3 From 074639bcbf2859a2973d0d3857eafe2fe9dd45a0 Mon Sep 17 00:00:00 2001 From: cel Date: Tue, 9 Jan 2018 19:47:33 -1000 Subject: Include composer in line comment threads --- lib/serve.js | 130 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 76 insertions(+), 54 deletions(-) diff --git a/lib/serve.js b/lib/serve.js index 6e3b451..aaf558f 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -1016,57 +1016,71 @@ function threadHeads(msgs, rootId) { })) } -Serve.prototype.id = function (id, path) { +Serve.prototype.streamThreadWithComposer = function (opts) { var self = this - if (self.query.raw != null) return self.rawId(id) - - this.getMsgDecryptedMaybeOoo(id, function (err, rootMsg) { - if (err && err.name === 'NotFoundError') err = null, rootMsg = { - key: id, value: {content: false}} - if (err) return self.respond(500, err.stack || err) - var rootContent = rootMsg && rootMsg.value && rootMsg.value.content - var recps = rootContent && rootContent.recps - || (rootMsg.value.private - ? [rootMsg.value.author, self.app.sbot.id].filter(uniques()) - : undefined) - var threadRootId = rootContent && rootContent.root || id - var channel + var id = opts.root + return ph('table', {class: 'ssb-msgs'}, u.readNext(next)) + function next(cb) { + self.getMsgDecryptedMaybeOoo(id, function (err, rootMsg) { + if (err && err.name === 'NotFoundError') err = null, rootMsg = { + key: id, value: {content: false}} + if (err) return cb(new Error(err.stack)) + if (!rootMsg) { + console.log('id', id, 'opts', opts) + } + var rootContent = rootMsg && rootMsg.value && rootMsg.value.content + var recps = rootContent && rootContent.recps + || (rootMsg.value.private + ? [rootMsg.value.author, self.app.sbot.id].filter(uniques()) + : undefined) + var threadRootId = rootContent && rootContent.root || id + var channel = opts.channel - pull( - cat([pull.once(rootMsg), self.app.sbot.links({dest: id, values: true})]), - pull.unique('key'), - self.app.unboxMessages(), - pull.through(function (msg) { - var c = msg && msg.value.content - if (!channel && c.channel) channel = c.channel - }), - pull.collect(function (err, links) { - if (err) return gotLinks(err) - if (!self.useOoo) return gotLinks(null, links) - self.app.expandOoo({msgs: links, dest: id}, gotLinks) - }) - ) - function gotLinks(err, links) { - if (err) return self.respond(500, err.stack || err) pull( - pull.values(sort(links)), - self.renderThread({ - msgId: id, - }), - self.wrapMessages(), - self.wrapThread({ - recps: recps, - root: threadRootId, - post: id, - branches: threadHeads(links, threadRootId), - postBranches: threadRootId !== id && threadHeads(links, id), - channel: channel, + cat([pull.once(rootMsg), self.app.sbot.links({dest: id, values: true})]), + pull.unique('key'), + self.app.unboxMessages(), + pull.through(function (msg) { + var c = msg && msg.value.content + if (!channel && c.channel) channel = c.channel }), - self.wrapPage(id), - self.respondSink(200) + pull.collect(function (err, links) { + if (err) return gotLinks(err) + if (!self.useOoo) return gotLinks(null, links) + self.app.expandOoo({msgs: links, dest: id}, gotLinks) + }) ) - } - }) + function gotLinks(err, links) { + if (err) return cb(new Error(err.stack)) + cb(null, pull( + pull.values(sort(links)), + self.renderThread({ + msgId: id, + }), + self.wrapMessages(), + self.wrapThread({ + recps: recps, + root: threadRootId, + post: id, + branches: threadHeads(links, threadRootId), + postBranches: threadRootId !== id && threadHeads(links, id), + placeholder: opts.placeholder, + channel: channel, + }) + )) + } + }) + } +} + +Serve.prototype.id = function (id, path) { + var self = this + if (self.query.raw != null) return self.rawId(id) + pull( + self.streamThreadWithComposer({root: id}), + self.wrapPage(id), + self.respondSink(200) + ) } Serve.prototype.userFeed = function (id, path) { @@ -2202,7 +2216,7 @@ Serve.prototype.gitDiffTable = function (diff, lineComments, lineCommentInfo) { (lineComments[newLineNum] ? ph('tr', ph('td', {colspan: 4}, - self.renderLineCommentThread(lineComments[newLineNum]) + self.renderLineCommentThread(lineComments[newLineNum], id) ) ) : newLineNum && lineCommentInfo && self.query.comment === id ? @@ -2229,12 +2243,12 @@ Serve.prototype.gitDiffTable = function (diff, lineComments, lineCommentInfo) { ) } -Serve.prototype.renderLineCommentThread = function (lineComment) { - var self = this - return ph('table', {class: 'ssb-msgs'}, pull( - this.app.getThread(lineComment.msg), - self.renderThread() - )) +Serve.prototype.renderLineCommentThread = function (lineComment, id) { + return this.streamThreadWithComposer({ + root: lineComment.msg.key, + id: id, + placeholder: 'reply to line comment thread' + }) } Serve.prototype.renderLineCommentForm = function (opts) { @@ -2308,6 +2322,13 @@ Serve.prototype.gitLineComment = function (path) { + '#' + file.hash[1] + '-' + c.line } var url = self.app.render.toUrl(path) + /* + return pull( + ph('a', {href: url}, path), + self.wrapPage(id), + self.respondSink(200) + ) + */ self.redirect(url) }) } @@ -2712,7 +2733,8 @@ Serve.prototype.wrapThread = function (opts) { self.app.render.prepareLinks(opts.recps, function (err, recps) { if (err) return cb(er) self.composer({ - placeholder: recps ? 'private reply' : 'reply', + placeholder: opts.placeholder + || (recps ? 'private reply' : 'reply'), id: 'reply', root: opts.root, post: opts.post, -- cgit v1.2.3 From 52170b13bd7c8d5b248761354e13846f204df04a Mon Sep 17 00:00:00 2001 From: cel Date: Tue, 9 Jan 2018 20:38:46 -1000 Subject: Support line comments on blobs as well as diffs --- lib/serve.js | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- package-lock.json | 20 ++++++++++++++-- package.json | 2 ++ 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/lib/serve.js b/lib/serve.js index aaf558f..4923667 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -24,6 +24,8 @@ var jpeg = require('jpeg-autorotate') var unzip = require('unzip') var Catch = require('pull-catch') var Diff = require('diff') +var split = require('pull-split') +var utf8 = require('pull-utf8-decoder') module.exports = Serve @@ -2032,6 +2034,7 @@ Serve.prototype.gitBlob = function (rev) { missingBlobs ? self.askWantBlobsForm(missingBlobs) : pull( self.app.git.readObject(obj), self.wrapBinary({ + obj: obj, rawUrl: self.app.render.toUrl('/git/raw/' + rev + '?msg=' + encodeURIComponent(msg.key)), ext: self.query.ext @@ -2624,6 +2627,7 @@ Serve.prototype.zip = function (url) { Serve.prototype.wrapBinary = function (opts) { var self = this var ext = opts.ext + var hash = opts.obj.hash return function (read) { var readRendered, type read = ident(function (_ext) { @@ -2660,9 +2664,69 @@ Serve.prototype.wrapBinary = function (opts) { })(read) })) } - return ph('pre', pull.map(function (buf) { - return self.app.render.highlight(buf.toString('utf8'), ext) - })(read)) + var i = 0 + var updateMsg = opts.obj.msg + var commitId = self.query.commit + var filePath = self.query.path + var lineComments = opts.lineComments || {} + return u.readNext(function (cb) { + if (commitId && filePath) { + self.app.getLineComments({ + obj: opts.obj, + hash: hash, + }, gotLineComments) + } else { + gotLineComments(null, {}) + } + function gotLineComments(err, lineComments) { + if (err) return cb(err) + cb(null, ph('table', + pull( + read, + utf8(), + split(), + pull.map(function (line) { + var lineNum = i++ + var id = hash + '-' + lineNum + var idEnc = encodeURIComponent(id) + return [ + ph('tr', [ + ph('td', ph('a', { + name: id, + href: '?msg=' + encodeURIComponent(self.query.msg) + + '&commit=' + encodeURIComponent(self.query.commit) + + '&path=' + encodeURIComponent(self.query.path) + + '&comment=' + idEnc + + '#' + idEnc + }, String(lineNum))), + ph('td', ph('pre', self.app.render.highlight(line, ext))) + ]), + (lineComments[lineNum] ? + ph('tr', + ph('td', {colspan: 4}, + self.renderLineCommentThread(lineComments[lineNum], id) + ) + ) + : self.query.comment === id ? + ph('tr', + ph('td', {colspan: 4}, + self.renderLineCommentForm({ + id: id, + line: lineNum, + updateId: updateMsg.key, + repoId: updateMsg.value.content.repo, + commitId: commitId, + filePath: filePath, + }) + ) + ) + : '') + ] + }) + ) + )) + } + }) } } diff --git a/package-lock.json b/package-lock.json index 3a9bf62..905c326 100644 --- a/package-lock.json +++ b/package-lock.json @@ -182,7 +182,8 @@ }, "diff": { "version": "3.3.1", - "resolved": "http://localhost:8989/blobs/get/&+6G9mvp4Q/5bujMKlbFdbrub2IXMPleXIqTqyyvY5Ww=.sha256" + "resolved": "http://localhost:8989/blobs/get/&+6G9mvp4Q/5bujMKlbFdbrub2IXMPleXIqTqyyvY5Ww=.sha256", + "integrity": "sha1-qoVnpu7QPFMfyJ0/cRzQ5SWd7HU=" }, "ed2curve": { "version": "0.1.4", @@ -879,6 +880,14 @@ "resolved": "http://localhost:8989/blobs/get/&fFOPAd5Js7mJkEU4XHeJ6vE6H7nsTOegHpjY0hpTu00=.sha256", "integrity": "sha256-fFOPAd5Js7mJkEU4XHeJ6vE6H7nsTOegHpjY0hpTu00=" }, + "pull-split": { + "version": "0.2.0", + "resolved": "http://localhost:8989/blobs/get/&OWAh7XbI03yih8UH5Vu0qrYM6TGx0q8uah06dm22CJo=.sha256", + "integrity": "sha256-OWAh7XbI03yih8UH5Vu0qrYM6TGx0q8uah06dm22CJo=", + "requires": { + "pull-through": "1.0.18" + } + }, "pull-stream": { "version": "3.6.1", "resolved": "http://localhost:8989/blobs/get/&xEhoJll+9Z5EYr7s7MUgCbBhdF1nekcqnIdIKV4z2SU=.sha256", @@ -899,6 +908,11 @@ } } }, + "pull-utf8-decoder": { + "version": "1.0.2", + "resolved": "http://localhost:8989/blobs/get/&JHBdLm6x7Rsu08/eLVWYKq+YpGna4xnvqM44ucX2tpk=.sha256", + "integrity": "sha256-JHBdLm6x7Rsu08/eLVWYKq+YpGna4xnvqM44ucX2tpk=" + }, "pull-ws": { "version": "3.3.0", "resolved": "http://localhost:8989/blobs/get/&xe26a32xW10OFqKs+xdyUTSobJPCAGdnv7yIAjoqmlk=.sha256", @@ -1213,11 +1227,13 @@ "dependencies": { "is-valid-domain": { "version": "0.0.2", - "resolved": "http://localhost:8989/blobs/get/&phjiap+k1lGC5LPVba/w4Caomw5o0NQp2Hol+u/YAzE=.sha256" + "resolved": "http://localhost:8989/blobs/get/&phjiap+k1lGC5LPVba/w4Caomw5o0NQp2Hol+u/YAzE=.sha256", + "integrity": "sha1-PnqUI/98Oy/hFmOvvW04N6JR+3c=" }, "ssb-ref": { "version": "2.7.1", "resolved": "http://localhost:8989/blobs/get/&wLimOD3785KVj7kBIAbjN6GH8EMhKRkcZSPrEMhdhks=.sha256", + "integrity": "sha1-XU7/xUXsD/1/wVuieCmmQLiir7o=", "requires": { "ip": "1.1.5", "is-valid-domain": "0.0.2" diff --git a/package.json b/package.json index 65ae820..37d3ca4 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,9 @@ "pull-paginate": "^1.0.0", "pull-paramap": "^1.2.1", "pull-reader": "^1.2.9", + "pull-split": "^0.2.0", "pull-stream": "^3.5.0", + "pull-utf8-decoder": "^1.0.2", "ssb-client": "http://localhost:8989/blobs/get/&EAaUpI+wrJM5/ly1RqZW0GAEF4PmCAmABBj7e6UIrL0=.sha256", "ssb-contact": "^1.2.0", "ssb-git": "^0.6.0", -- cgit v1.2.3 From 2cea6dd8bfa1230c2c7aa48fab6a48af6c148a30 Mon Sep 17 00:00:00 2001 From: cel Date: Tue, 9 Jan 2018 20:44:01 -1000 Subject: Handle multiple line comment thread composers --- lib/serve.js | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/lib/serve.js b/lib/serve.js index 4923667..7df2da9 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -2701,26 +2701,23 @@ Serve.prototype.wrapBinary = function (opts) { }, String(lineNum))), ph('td', ph('pre', self.app.render.highlight(line, ext))) ]), - (lineComments[lineNum] ? - ph('tr', - ph('td', {colspan: 4}, - self.renderLineCommentThread(lineComments[lineNum], id) - ) + lineComments[lineNum] ? ph('tr', + ph('td', {colspan: 4}, + self.renderLineCommentThread(lineComments[lineNum], id) ) - : self.query.comment === id ? - ph('tr', - ph('td', {colspan: 4}, - self.renderLineCommentForm({ - id: id, - line: lineNum, - updateId: updateMsg.key, - repoId: updateMsg.value.content.repo, - commitId: commitId, - filePath: filePath, - }) - ) + ) : '', + self.query.comment === id ? ph('tr', + ph('td', {colspan: 4}, + self.renderLineCommentForm({ + id: id, + line: lineNum, + updateId: updateMsg.key, + repoId: updateMsg.value.content.repo, + commitId: commitId, + filePath: filePath, + }) ) - : '') + ) : '' ] }) ) @@ -2941,6 +2938,11 @@ Serve.prototype.composer = function (opts, cb) { var data = self.data var myId = self.app.sbot.id + if (opts.id && data.composer_id && opts.id !== data.composer_id) { + // don't share data between multiple composers + data = {} + } + if (!data.text && self.query.text) data.text = self.query.text if (!data.action && self.query.action) data.action = self.query.action @@ -3034,6 +3036,7 @@ Serve.prototype.composer = function (opts, cb) { enctype: 'multipart/form-data'}, h('input', {type: 'hidden', name: 'blobs', value: JSON.stringify(blobs)}), + h('input', {type: 'hidden', name: 'composer_id', value: opts.id}), opts.recps ? self.app.render.privateLine(opts.recps, done()) : opts.private ? h('div', h('input.recps-input', {name: 'recps', value: data.recps || '', placeholder: 'recipient ids'})) : '', -- cgit v1.2.3 From ebe6b4c381e0ff7ae283f5abeadb2c6b5c2685fc Mon Sep 17 00:00:00 2001 From: cel Date: Tue, 9 Jan 2018 20:51:33 -1000 Subject: whitespace --- lib/serve.js | 172 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/lib/serve.js b/lib/serve.js index 7df2da9..2fa6c6d 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -81,7 +81,7 @@ Serve.prototype.go = function () { if (auth) { var a = auth.split(' ') if (a[0] == 'Basic') { - tok = Buffer.from(a[1],'base64').toString('ascii') + tok = Buffer.from(a[1],'base64').toString('ascii') } } if (tok != authtok) { @@ -542,7 +542,7 @@ Serve.prototype.advsearch = function (ext) { ph('td', ['#', ph('input', {name: 'channel', placeholder: 'channel', class: 'id-input', value: q.channel || ''}) - ]) + ]) ]), ph('tr', [ ph('td', {colspan: 2}, [ @@ -555,8 +555,8 @@ Serve.prototype.advsearch = function (ext) { hasQuery && pull( self.app.advancedSearch(q), self.renderThread({ - feed: q.source, - }), + feed: q.source, + }), self.wrapMessages() ) ]), @@ -582,7 +582,7 @@ Serve.prototype.live = function (ext) { self.app.sbot.createLogStream(opts), self.app.render.renderFeeds({ withGt: true, - filter: q.filter, + filter: q.filter, }), pull.map(u.toHTML) )), @@ -1028,7 +1028,7 @@ Serve.prototype.streamThreadWithComposer = function (opts) { key: id, value: {content: false}} if (err) return cb(new Error(err.stack)) if (!rootMsg) { - console.log('id', id, 'opts', opts) + console.log('id', id, 'opts', opts) } var rootContent = rootMsg && rootMsg.value && rootMsg.value.content var recps = rootContent && rootContent.recps @@ -1053,7 +1053,7 @@ Serve.prototype.streamThreadWithComposer = function (opts) { }) ) function gotLinks(err, links) { - if (err) return cb(new Error(err.stack)) + if (err) return cb(new Error(err.stack)) cb(null, pull( pull.values(sort(links)), self.renderThread({ @@ -1319,7 +1319,7 @@ Serve.prototype.renderThreadPaginated = function (opts, feedId, q) { function onFirst(msg, cb) { var num = feedId ? msg.value.sequence : opts.sortByTimestamp ? msg.value.timestamp : - msg.timestamp || msg.ts + msg.timestamp || msg.ts if (q.forwards) { cb(null, links({ lt: num, @@ -1347,7 +1347,7 @@ Serve.prototype.renderThreadPaginated = function (opts, feedId, q) { function onLast(msg, cb) { var num = feedId ? msg.value.sequence : opts.sortByTimestamp ? msg.value.timestamp : - msg.timestamp || msg.ts + msg.timestamp || msg.ts if (q.forwards) { cb(null, links({ lt: null, @@ -1590,8 +1590,8 @@ Serve.prototype.friendInfo = function (id, myId) { this.app.render.friendsList(), pull.map(function (html) { if (!first) { - first = true - return 'followed by your friends: ' + html + first = true + return 'followed by your friends: ' + html } return html }) @@ -1643,15 +1643,15 @@ Serve.prototype.wrapUserFeed = function (isScrolled, id) { ) ]), isScrolled || id === myId ? '' : [ - ph('tr', [ - ph('td'), - ph('td', {class: 'follow-info'}, self.followInfo(id, myId)) - ]), - ph('tr', [ - ph('td'), - ph('td', self.friendInfo(id, myId)) - ]) - ] + ph('tr', [ + ph('td'), + ph('td', {class: 'follow-info'}, self.followInfo(id, myId)) + ]), + ph('tr', [ + ph('td'), + ph('td', self.friendInfo(id, myId)) + ]) + ] ])), thread ]) @@ -1973,11 +1973,11 @@ Serve.prototype.gitTree = function (rev) { ), ]) }), - Catch(function (err) { - if (err && err.name === 'ObjectNotFoundError') return - if (err && err.name === 'BlobNotFoundError') return self.askWantBlobsForm(err.links) - return false - }) + Catch(function (err) { + if (err && err.name === 'ObjectNotFoundError') return + if (err && err.name === 'BlobNotFoundError') return self.askWantBlobsForm(err.links) + return false + }) ) ]), ]), @@ -2034,7 +2034,7 @@ Serve.prototype.gitBlob = function (rev) { missingBlobs ? self.askWantBlobsForm(missingBlobs) : pull( self.app.git.readObject(obj), self.wrapBinary({ - obj: obj, + obj: obj, rawUrl: self.app.render.toUrl('/git/raw/' + rev + '?msg=' + encodeURIComponent(msg.key)), ext: self.query.ext @@ -2671,57 +2671,57 @@ Serve.prototype.wrapBinary = function (opts) { var lineComments = opts.lineComments || {} return u.readNext(function (cb) { if (commitId && filePath) { - self.app.getLineComments({ - obj: opts.obj, - hash: hash, - }, gotLineComments) + self.app.getLineComments({ + obj: opts.obj, + hash: hash, + }, gotLineComments) } else { - gotLineComments(null, {}) + gotLineComments(null, {}) } function gotLineComments(err, lineComments) { - if (err) return cb(err) - cb(null, ph('table', - pull( - read, - utf8(), - split(), - pull.map(function (line) { - var lineNum = i++ - var id = hash + '-' + lineNum - var idEnc = encodeURIComponent(id) - return [ - ph('tr', [ - ph('td', ph('a', { - name: id, - href: '?msg=' + encodeURIComponent(self.query.msg) - + '&commit=' + encodeURIComponent(self.query.commit) - + '&path=' + encodeURIComponent(self.query.path) - + '&comment=' + idEnc - + '#' + idEnc - }, String(lineNum))), - ph('td', ph('pre', self.app.render.highlight(line, ext))) - ]), - lineComments[lineNum] ? ph('tr', - ph('td', {colspan: 4}, - self.renderLineCommentThread(lineComments[lineNum], id) - ) - ) : '', - self.query.comment === id ? ph('tr', - ph('td', {colspan: 4}, - self.renderLineCommentForm({ - id: id, - line: lineNum, - updateId: updateMsg.key, - repoId: updateMsg.value.content.repo, - commitId: commitId, - filePath: filePath, - }) - ) - ) : '' - ] - }) - ) - )) + if (err) return cb(err) + cb(null, ph('table', + pull( + read, + utf8(), + split(), + pull.map(function (line) { + var lineNum = i++ + var id = hash + '-' + lineNum + var idEnc = encodeURIComponent(id) + return [ + ph('tr', [ + ph('td', ph('a', { + name: id, + href: '?msg=' + encodeURIComponent(self.query.msg) + + '&commit=' + encodeURIComponent(self.query.commit) + + '&path=' + encodeURIComponent(self.query.path) + + '&comment=' + idEnc + + '#' + idEnc + }, String(lineNum))), + ph('td', ph('pre', self.app.render.highlight(line, ext))) + ]), + lineComments[lineNum] ? ph('tr', + ph('td', {colspan: 4}, + self.renderLineCommentThread(lineComments[lineNum], id) + ) + ) : '', + self.query.comment === id ? ph('tr', + ph('td', {colspan: 4}, + self.renderLineCommentForm({ + id: id, + line: lineNum, + updateId: updateMsg.key, + repoId: updateMsg.value.content.repo, + commitId: commitId, + filePath: filePath, + }) + ) + ) : '' + ] + }) + ) + )) } }) } @@ -3215,15 +3215,15 @@ Serve.prototype.composer = function (opts, cb) { }) var draftMsg = { - key: '%0000000000000000000000000000000000000000000=.sha256', - value: { - previous: '%0000000000000000000000000000000000000000000=.sha256', - author: '@0000000000000000000000000000000000000000000=.ed25519', - sequence: 1000, - timestamp: 1000000000000, - hash: 'sha256', - content: content - } + key: '%0000000000000000000000000000000000000000000=.sha256', + value: { + previous: '%0000000000000000000000000000000000000000000=.sha256', + author: '@0000000000000000000000000000000000000000000=.ed25519', + sequence: 1000, + timestamp: 1000000000000, + hash: 'sha256', + content: content + } } var estSize = JSON.stringify(draftMsg, null, 2).length sizeEl.innerHTML = self.app.render.formatSize(estSize) @@ -3238,9 +3238,9 @@ Serve.prototype.composer = function (opts, cb) { pull.once(msg), self.app.unboxMessages(), self.app.render.renderFeeds({ - raw: raw, - filter: self.query.filter, - }), + raw: raw, + filter: self.query.filter, + }), pull.drain(function (el) { msgContainer.appendChild(h('tbody', el)) }, cb) -- cgit v1.2.3 From afc315b47e13e5a98eb37b9b56a0d36571d1c4a9 Mon Sep 17 00:00:00 2001 From: cel Date: Tue, 9 Jan 2018 21:04:51 -1000 Subject: Update package-lock.json --- package-lock.json | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/package-lock.json b/package-lock.json index 905c326..43d981e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -787,6 +787,13 @@ "version": "http://localhost:8989/blobs/get/&mDI2o/JC9yvFOVbeAhDN+hwTm7cK+dBwISYO49EMY24=.sha256", "integrity": "sha1-CIew/7MK8ypW2+z6csFnInHwexM=" }, + "pull-git-packidx-parser": { + "version": "1.0.0", + "resolved": "http://localhost:8989/blobs/get/&ou0MPQZabBgzrHDu54jzLU3Sc6Rf5a/lost0whPQUJ0=.sha256", + "requires": { + "pull-stream": "3.6.1" + } + }, "pull-goodbye": { "version": "0.0.2", "resolved": "http://localhost:8989/blobs/get/&MQhN9Pp3BYaj0TzxK39JfAK7CjVceg1EPfNhhFnZ9sU=.sha256", @@ -846,6 +853,14 @@ } } }, + "pull-kvdiff": { + "version": "0.0.1", + "resolved": "http://localhost:8989/blobs/get/&xxfT88LrbK4FSIhpaNmIAt4jzsaBgVugeaDPVwC1gCU=.sha256", + "integrity": "sha256-xxfT88LrbK4FSIhpaNmIAt4jzsaBgVugeaDPVwC1gCU=", + "requires": { + "multicb": "1.2.2" + } + }, "pull-many": { "version": "http://localhost:8989/blobs/get/&wBHfPheBEjwjjkNoeM5mWtU/tYDs8+xnt5w6C1+EK3g=.sha256", "integrity": "sha1-Pa3ZttFWxUVyG9qNAAPdjqoGKT4=", @@ -1203,6 +1218,23 @@ "pull-stream": "3.6.1" } }, + "ssb-git": { + "version": "0.6.0", + "resolved": "http://localhost:8989/blobs/get/&tz2WDecaYFjrKmRdZmLW9hu1DF7oYD+rNzAzaObqF5I=.sha256", + "integrity": "sha256-tz2WDecaYFjrKmRdZmLW9hu1DF7oYD+rNzAzaObqF5I=", + "requires": { + "asyncmemo": "1.1.0", + "hashlru": "http://localhost:8989/blobs/get/&EASZ1/L+jHuTVxrgbQrNlTdCenA8etAVuk/TGn92RoY=.sha256", + "looper": "4.0.0", + "multicb": "1.2.2", + "pull-git-packidx-parser": "1.0.0", + "pull-kvdiff": "0.0.1", + "pull-paramap": "1.2.2", + "pull-reader": "1.2.9", + "pull-stream": "3.6.1", + "stream-to-pull-stream": "1.7.2" + } + }, "ssb-keys": { "version": "7.0.10", "resolved": "http://localhost:8989/blobs/get/&/XLsWirGsUv3pQ11HIJwzZgLF7HVKQmd4QCt+nCDf6A=.sha256", -- cgit v1.2.3 From 7052ddce7a33724467c076dff4217c73001d4a8c Mon Sep 17 00:00:00 2001 From: cel Date: Tue, 9 Jan 2018 22:02:17 -1000 Subject: Use correct message ids in commit file diff links --- lib/serve.js | 5 +++-- package-lock.json | 6 +++--- package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/serve.js b/lib/serve.js index 5ba0004..793ad84 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -1799,6 +1799,7 @@ Serve.prototype.gitCommit = function (rev) { ph('table', pull( self.app.git.readCommitChanges(commit), pull.map(function (file) { + var msg = file.msg || obj.msg return ph('tr', [ ph('td', ph('code', u.escapeHTML(file.name))), ph('td', file.deleted ? 'deleted' @@ -1806,13 +1807,13 @@ Serve.prototype.gitCommit = function (rev) { ph('a', {href: self.app.render.toUrl('/git/blob/' + (file.hash[1] || file.hash[0]) - + '?msg=' + encodeURIComponent(obj.msg.key)) + + '?msg=' + encodeURIComponent(msg.key)) }, 'created') : file.hash ? ph('a', {href: self.app.render.toUrl('/git/diff/' + file.hash[0] + '..' + file.hash[1] - + '?msg=' + encodeURIComponent(obj.msg.key)) + + '?msg=' + encodeURIComponent(msg.key)) + '&commit=' + rev + '&path=' + encodeURIComponent(file.name) }, 'changed') diff --git a/package-lock.json b/package-lock.json index 43d981e..8533e05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1219,9 +1219,9 @@ } }, "ssb-git": { - "version": "0.6.0", - "resolved": "http://localhost:8989/blobs/get/&tz2WDecaYFjrKmRdZmLW9hu1DF7oYD+rNzAzaObqF5I=.sha256", - "integrity": "sha256-tz2WDecaYFjrKmRdZmLW9hu1DF7oYD+rNzAzaObqF5I=", + "version": "0.7.0", + "resolved": "http://localhost:8989/blobs/get/&YaFMXaoXkA4IA5A2FkfPJ/u+8k+3k6MLuDm09yeShdg=.sha256", + "integrity": "sha256-YaFMXaoXkA4IA5A2FkfPJ/u+8k+3k6MLuDm09yeShdg=", "requires": { "asyncmemo": "1.1.0", "hashlru": "http://localhost:8989/blobs/get/&EASZ1/L+jHuTVxrgbQrNlTdCenA8etAVuk/TGn92RoY=.sha256", diff --git a/package.json b/package.json index 37d3ca4..7c04857 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "pull-utf8-decoder": "^1.0.2", "ssb-client": "http://localhost:8989/blobs/get/&EAaUpI+wrJM5/ly1RqZW0GAEF4PmCAmABBj7e6UIrL0=.sha256", "ssb-contact": "^1.2.0", - "ssb-git": "^0.6.0", + "ssb-git": "^0.7.0", "ssb-marked": "^0.7.1", "ssb-mentions": "http://localhost:8989/blobs/get/&GjuxknqKwJqHznKueFNCyIh52v1woz5PB41vqmoHfyM=.sha256", "ssb-sort": "^1.0.0", -- cgit v1.2.3 From f0426f5208e1a3ca3264f7940e4d4edaf072d3fa Mon Sep 17 00:00:00 2001 From: cel Date: Tue, 9 Jan 2018 22:06:54 -1000 Subject: Add hint parameters for 'created' links --- lib/serve.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/serve.js b/lib/serve.js index 793ad84..e8a8c91 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -1808,6 +1808,8 @@ Serve.prototype.gitCommit = function (rev) { self.app.render.toUrl('/git/blob/' + (file.hash[1] || file.hash[0]) + '?msg=' + encodeURIComponent(msg.key)) + + '&commit=' + rev + + '&path=' + encodeURIComponent(file.name) }, 'created') : file.hash ? ph('a', {href: -- cgit v1.2.3 From f34f08104b9613ba5f0ac43eb229ae992116224a Mon Sep 17 00:00:00 2001 From: cel Date: Tue, 9 Jan 2018 22:21:23 -1000 Subject: Include git blob id in new line-comments, for good measure --- lib/serve.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/serve.js b/lib/serve.js index e8a8c91..13d3fc5 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -2231,6 +2231,7 @@ Serve.prototype.gitDiffTable = function (diff, lineComments, lineCommentInfo) { id: id, line: newLineNum, updateId: updateMsg.key, + blobId: hash, repoId: updateMsg.value.content.repo, commitId: lineCommentInfo.commit, filePath: lineCommentInfo.path, @@ -2717,6 +2718,7 @@ Serve.prototype.wrapBinary = function (opts) { updateId: updateMsg.key, repoId: updateMsg.value.content.repo, commitId: commitId, + blobId: hash, filePath: filePath, }) ) @@ -3113,6 +3115,7 @@ Serve.prototype.composer = function (opts, cb) { content.repo = opts.lineComment.repoId content.commitId = opts.lineComment.commitId content.filePath = opts.lineComment.filePath + content.blobId = opts.lineComment.blobId content.line = opts.lineComment.line } var mentions = ssbMentions(data.text, {bareFeedNames: true, emoji: true}) -- cgit v1.2.3 From 964cb82322e65c807382c6a84eba0a8e21c1c70f Mon Sep 17 00:00:00 2001 From: cel Date: Wed, 10 Jan 2018 21:26:06 -1000 Subject: Don't show comment links when we don't have the context to generate one --- lib/serve.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/serve.js b/lib/serve.js index 13d3fc5..8f7d1de 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -2693,15 +2693,21 @@ Serve.prototype.wrapBinary = function (opts) { var lineNum = i++ var id = hash + '-' + lineNum var idEnc = encodeURIComponent(id) + var allowComment = self.query.commit && self.query.path return [ ph('tr', [ + ph('td', + allowComment ? ph('a', { + href: '?msg=' + encodeURIComponent(self.query.msg) + + '&commit=' + encodeURIComponent(self.query.commit) + + '&path=' + encodeURIComponent(self.query.path) + + '&comment=' + idEnc + + '#' + idEnc + }, '…') : '' + ), ph('td', ph('a', { name: id, - href: '?msg=' + encodeURIComponent(self.query.msg) - + '&commit=' + encodeURIComponent(self.query.commit) - + '&path=' + encodeURIComponent(self.query.path) - + '&comment=' + idEnc - + '#' + idEnc + href: '#' + idEnc }, String(lineNum))), ph('td', ph('pre', self.app.render.highlight(line, ext))) ]), -- cgit v1.2.3