From 93184f5b697dabdb599c3383071aa86245106d73 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Thu, 25 Jun 2015 12:58:41 -0500 Subject: [PATCH] added blob 'checkout' on open in new window --- app/index.js | 42 +++++++----------- app/lib/blob-protocol.js | 13 ------ app/lib/blobs.js | 93 ++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 4 files changed, 110 insertions(+), 39 deletions(-) delete mode 100644 app/lib/blob-protocol.js create mode 100644 app/lib/blobs.js diff --git a/app/index.js b/app/index.js index 1f1f50c..ef53167 100644 --- a/app/index.js +++ b/app/index.js @@ -1,26 +1,26 @@ var app = require('app') -var path = require('path') -var Tray = require('tray') var Menu = require('menu') var shell = require('shell') var BrowserWindow = require('browser-window') +var path = require('path') var config = require('ssb-config') -var toPath = require('multiblob/util').toPath // Report crashes to our server. //require('crash-reporter').start(); -var tray var mainWindow app.on('ready', function ready () { + // setup blobs var blobs_dir = path.join(config.path, 'blobs') + var downloads_dir = app.getPath('userDesktop') + var blobs = require('./lib/blobs')(blobs_dir, downloads_dir) // start sbot require('scuttlebot').init(config, function (err, sbot) { // register protocols - require('protocol').registerProtocol('blob', require('./lib/blob-protocol')(config)) + require('protocol').registerProtocol('blob', blobs.protocol) // open the web app mainWindow = new BrowserWindow({width: 1000, height: 720}) @@ -32,8 +32,17 @@ app.on('ready', function ready () { e.preventDefault() // hell naw if (url.indexOf('blob:') === 0) { // open the file - var id = url.split(':')[1] - shell.openItem(toPath(blobs_dir, id)) + blobs.checkout(url, function (err, path) { + if (err) { + if (err.badUrl) + alert('Error: Not a valid file reference') + else if (err.notFound) + alert('Error: This file has not yet been synced. Please try again soon.') // :TODO: show 'search' window + else + console.log(err) // :TODO: something nicer + } else + shell.openItem(path) + }) } else { // open in the browser shell.openExternal(url) @@ -49,24 +58,5 @@ app.on('ready', function ready () { // ] // }])) - // setup tray icon - tray = new Tray(__dirname+'/icon.png') - tray.setContextMenu(Menu.buildFromTemplate([ - // { label: 'Open Web App', click: onopen }, - { label: 'Quit', click: onquit } - ])) - tray.setToolTip('Secure Scuttlebutt: Running on port 8008') - // tray.on('double-clicked', onopen) - - // menu handlers - function onopen () { - shell.openExternal('http://localhost:8008') - } - function onquit () { - tray = null - sbot.close() - process.exit() - } - }) }); \ No newline at end of file diff --git a/app/lib/blob-protocol.js b/app/lib/blob-protocol.js deleted file mode 100644 index 4740396..0000000 --- a/app/lib/blob-protocol.js +++ /dev/null @@ -1,13 +0,0 @@ -var protocol = require('protocol') -var path = require('path') -var toPath = require('multiblob/util').toPath - -module.exports = function (config) { - var dir = path.join(config.path, 'blobs') - return function (request) { - var id = request.url.split(':')[1] - if (request.method == 'GET' && id) { - return new protocol.RequestFileJob(toPath(dir, id)) - } - } -} \ No newline at end of file diff --git a/app/lib/blobs.js b/app/lib/blobs.js new file mode 100644 index 0000000..676c6b2 --- /dev/null +++ b/app/lib/blobs.js @@ -0,0 +1,93 @@ +var path = require('path') +var multicb = require('multicb') +var toPath = require('multiblob/util').toPath +var querystring = require('querystring') +var fs = require('fs') + +// blob url parser +var re = /^blob:([a-z0-9\+\/=]+\.blake2s)\??(.*)$/i +var url_parse = +module.exports.url_parse = function (str) { + var parts = re.exec(str) + if (parts) + return { hash: parts[1], qs: querystring.parse(parts[2]) } +} + +// blob url builder +var url_stringify = +module.exports.url_stringify = function (hash, qs) { + var url = 'blob:'+hash + if (qs && typeof qs == 'object') + url += '?' + querystring.stringify(qs) + return url +} + +module.exports = function (blobs_dir, checkout_dir) { + return { + // behavior for the blob: protocol + protocol: function (request) { + var protocol = require('protocol') // have to require here, doing so before app:ready causes errors + // simple fetch + var parsed = url_parse(request.url) + if (request.method == 'GET' && parsed) { + return new protocol.RequestFileJob(toPath(blobs_dir, parsed.hash)) + } + }, + + // copy file from blobs into given dir with nice name + checkout: function (url, cb) { + var parsed = url_parse(url) + if (!parsed) + return cb({ badUrl: true }) + + var filename = parsed.qs.name || parsed.qs.filename || parsed.hash + + // check if we have the blob, at the same time find an available filename + var done = multicb({ pluck: 1 }) + fs.stat(toPath(blobs_dir, parsed.hash), done()) + findFreeCheckoutPath(filename, done()) + done(function (err, res) { + if (!res[0]) + return cb({ notFound: true }) + + // copy the file + var src = toPath(blobs_dir, parsed.hash) + var dst = res[1] + var read = fs.createReadStream(src) + var write = fs.createWriteStream(dst) + read.on('error', done) + write.on('error', done) + write.on('close', done) + read.pipe(write) + function done (err) { + cb && cb(err, dst) + cb = null + } + }) + } + } + + // helper to create a filename in checkout_dir that isnt already in use + function findFreeCheckoutPath (filename, cb) { + var n = 1 + var parsed = path.parse(filename) + next() + + function gen () { + var name = parsed.name + if (n !== 1) name += ' ('+n+')' + name += parsed.ext + n++ + return path.join(checkout_dir, name) + } + + function next () { + var filepath = gen() + fs.stat(filepath, function (err, stat) { + if (!stat) + return cb(null, filepath) + next() + }) + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index f4292f7..10a7be7 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ }, "dependencies": { "multiblob": "^1.4.3", + "multicb": "^1.1.0", "scuttlebot": "^4.2.3", "ssb-config": "^1.0.3" }