diff --git a/lib/server.js b/lib/server.js index 6ac2dd3..b688871 100644 --- a/lib/server.js +++ b/lib/server.js @@ -36,6 +36,8 @@ function CopayServer() { this.storage = storage; }; +util.inherits(CopayServer, events.EventEmitter); + /** * Initializes global settings for all instances. @@ -143,6 +145,19 @@ CopayServer.prototype._verifySignature = function(text, signature, pubKey) { return SignUtils.verify(text, signature, pubKey); }; + +CopayServer.prototype.notify = function(type, data) { + var self = this; + + var n = new Notification({ + type: type, + data: data, + }); + this.storage.storeNotification(this.walletId, n, function () { + self.emit(n); + }); +}; + /** * Joins a wallet in creation. * @param {Object} opts @@ -181,6 +196,7 @@ CopayServer.prototype.joinWallet = function(opts, cb) { wallet.addCopayer(copayer); self.storage.storeWalletAndUpdateCopayersLookup(wallet, function(err) { + self.notify('newCopayer'); return cb(err, copayer.id); }); }); diff --git a/lib/storage.js b/lib/storage.js index fcba017..de28b18 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -11,6 +11,7 @@ var Wallet = require('./model/wallet'); var Copayer = require('./model/copayer'); var Address = require('./model/address'); var TxProposal = require('./model/txproposal'); +var Notification = require('./model/notification'); var Storage = function(opts) { opts = opts || {}; @@ -44,6 +45,9 @@ var KEY = { TXP: function(walletId, txProposalId) { return walletPrefix(walletId) + '!txp' + opKey(txProposalId); }, + NOTIFICATION: function(walletId, notificationId) { + return walletPrefix(walletId) + '!not' + opKey(notificationId); + }, PENDING_TXP: function(walletId, txProposalId) { return walletPrefix(walletId) + '!ptxp' + opKey(txProposalId); }, @@ -107,6 +111,19 @@ Storage.prototype.fetchTx = function(walletId, txProposalId, cb) { }); }; + +Storage.prototype.fetchNotification = function(walletId, notificationId, cb) { + this.db.get(KEY.NOTIFICATION(walletId, notificationId), function(err, data) { + if (err) { + if (err.notFound) return cb(); + return cb(err); + } + return cb(null, Notification.fromObj(data)); + }); +}; + + + Storage.prototype.fetchPendingTxs = function(walletId, cb) { var txs = []; var key = KEY.PENDING_TXP(walletId); @@ -162,6 +179,49 @@ Storage.prototype.fetchTxs = function(walletId, opts, cb) { }); }; + +/** + * fetchNotifications + * + * @param walletId + * @param opts.minTs + * @param opts.maxTs + * @param opts.limit + */ +Storage.prototype.fetchNotifications = function(walletId, opts, cb) { + var txs = []; + opts = opts || {}; + opts.limit = _.isNumber(opts.limit) ? parseInt(opts.limit) : -1; + opts.minTs = _.isNumber(opts.minTs) ? ('000000000000' + parseInt(opts.minTs)).slice(-12) : 0; + opts.maxTs = _.isNumber(opts.maxTs) ? ('000000000000' + parseInt(opts.maxTs)).slice(-12) : MAX_TS; + + var key = KEY.NOTIFICATION(walletId, opts.minTs); + var endkey = KEY.NOTIFICATION(walletId, opts.maxTs); + + this.db.createReadStream({ + gt: key, + lt: endkey + '~', + reverse: true, + limit: opts.limit, + }) + .on('data', function(data) { + txs.push(TxProposal.fromObj(data.value)); + }) + .on('error', function(err) { + if (err.notFound) return cb(); + return cb(err); + }) + .on('end', function() { + return cb(null, txs); + }); +}; + + +Storage.prototype.storeNotification = function(walletId, notification, cb) { + this.db.put(KEY.NOTIFICATION(walletId, notification.id), notification, cb); +}; + + // TODO should we store only txp.id on keys for indexing // or the whole txp? For now, the entire record makes sense // (faster + easier to access) @@ -261,6 +321,7 @@ Storage.prototype.removeWallet = function(walletId, cb) { var self = this; async.series([ + function(next) { self._removeCopayers(walletId, next); }, diff --git a/package.json b/package.json index 30ce41f..4000b35 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ }, "dependencies": { "async": "^0.9.0", - "bitcore": "*", + "bitcore": "0.10.0", "bitcore-explorers": "^0.9.1", "express": "^4.10.0", "inherits": "^2.0.1",