Add user comment to transaction propossal

This commit is contained in:
Yemel Jardi 2014-06-13 10:33:30 -03:00
parent fd7eaca46a
commit bcbb6b54d1
8 changed files with 93 additions and 15 deletions

View File

@ -422,6 +422,14 @@ hr { margin: 2.25rem 0;}
background: #C0392B; background: #C0392B;
} }
.box-note {
text-align: center;
clear: both;
font-style: italic;
color: gray;
margin-bottom: 10px;
}
.box-signin { .box-signin {
padding: 20px 40px; padding: 20px 40px;
border: 1px solid #eee; border: 1px solid #eee;

View File

@ -189,7 +189,7 @@
src="./img/satoshi.gif" src="./img/satoshi.gif"
/> />
<div class="size-12 text-center text-gray" stype="margin-top:4px"> <div class="size-12 text-center text-gray" stype="margin-top:4px">
{{c.nick || 'NN'}} {{c.nick}}
</div> </div>
</div> </div>
</div> </div>
@ -455,6 +455,9 @@
</div> </div>
<div class="tx-copayers"> <div class="tx-copayers">
<div class="box-note large-12" ng-show="tx.comment">
"{{tx.comment}}" - {{$root.wallet.publicKeyRing.nicknameForCopayer(tx.creator)}}
</div>
<div class="box-copayers" ng-repeat="(cId, actions) in tx.peerActions"> <div class="box-copayers" ng-repeat="(cId, actions) in tx.peerActions">
<figure class="left"> <figure class="left">
@ -664,6 +667,23 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row">
<div class="large-12 medium-6 columns">
<div class="row collapse">
<label for="comment">Note
<small ng-hide="!sendForm.comment.$pristine">optional</small>
<small class="is-valid" ng-show="!sendForm.comment.$invalid && !sendForm.comment.$pristine">valid!</small>
<small class="has-error" ng-show="sendForm.comment.$invalid && !sendForm.comment.$pristine">too long!</small>
</label>
<div class="small-12 columns">
<textarea id="comment" ng-disabled="loading"
name="comment" placeholder="Leave a private message to your copayers" ng-model="comment" ng-maxlength="100"></textarea>
</div>
</div>
</div>
</div>
<div class="row"> <div class="row">
<div class="large-5 columns"> <div class="large-5 columns">
<button type="submit" class="button secondary radius text-center" ng-disabled="sendForm.$invalid || loading" loading="Sending"> <button type="submit" class="button secondary radius text-center" ng-disabled="sendForm.$invalid || loading" loading="Sending">

View File

@ -47,19 +47,18 @@ angular.module('copayApp.controllers').controller('SendController',
var address = form.address.$modelValue; var address = form.address.$modelValue;
var amount = (form.amount.$modelValue * 100000000).toFixed(); // satoshi to string var amount = (form.amount.$modelValue * 100000000).toFixed(); // satoshi to string
var comment = form.comment.$modelValue;
var w = $rootScope.wallet; var w = $rootScope.wallet;
w.createTx(address, amount,function() { w.createTx(address, amount, comment, function() {
$scope.loading = false; $scope.loading = false;
$rootScope.$flashMessage = { message: 'The transaction proposal has been created', type: 'success'}; $rootScope.$flashMessage = { message: 'The transaction proposal has been created', type: 'success'};
$rootScope.$digest(); $rootScope.$digest();
}); });
// reset fields // reset fields
$scope.address = null; $scope.address = $scope.amount = $scope.comment = null;
$scope.amount = null; form.address.$pristine = form.amount.$pristine = form.comment.$pristine = true;
form.address.$pristine = true;
form.amount.$pristine = true;
}; };
// QR code Scanner // QR code Scanner

View File

@ -107,7 +107,7 @@ PublicKeyRing.prototype.nicknameForIndex = function(index) {
}; };
PublicKeyRing.prototype.nicknameForCopayer = function(copayerId) { PublicKeyRing.prototype.nicknameForCopayer = function(copayerId) {
return this.nicknameFor[copayerId]; return this.nicknameFor[copayerId] || 'NN';
}; };
PublicKeyRing.prototype.addCopayer = function(newEpk, nickname) { PublicKeyRing.prototype.addCopayer = function(newEpk, nickname) {

View File

@ -20,6 +20,7 @@ function TxProposal(opts) {
this.sentTs = opts.sentTs || null; this.sentTs = opts.sentTs || null;
this.sentTxid = opts.sentTxid || null; this.sentTxid = opts.sentTxid || null;
this.inputChainPaths = opts.inputChainPaths || []; this.inputChainPaths = opts.inputChainPaths || [];
this.comment = opts.comment || null;
} }
TxProposal.prototype.toObj = function() { TxProposal.prototype.toObj = function() {

View File

@ -620,7 +620,7 @@ Wallet.prototype.getUnspent = function(cb) {
}; };
Wallet.prototype.createTx = function(toAddress, amountSatStr, opts, cb) { Wallet.prototype.createTx = function(toAddress, amountSatStr, comment, opts, cb) {
var self = this; var self = this;
if (typeof opts === 'function') { if (typeof opts === 'function') {
cb = opts; cb = opts;
@ -633,7 +633,7 @@ Wallet.prototype.createTx = function(toAddress, amountSatStr, opts, cb) {
} }
this.getUnspent(function(err, safeUnspent) { this.getUnspent(function(err, safeUnspent) {
var ntxid = self.createTxSync(toAddress, amountSatStr, safeUnspent, opts); var ntxid = self.createTxSync(toAddress, amountSatStr, comment, safeUnspent, opts);
if (ntxid) { if (ntxid) {
self.sendIndexes(); self.sendIndexes();
self.sendTxProposals(null, ntxid); self.sendTxProposals(null, ntxid);
@ -644,7 +644,7 @@ Wallet.prototype.createTx = function(toAddress, amountSatStr, opts, cb) {
}); });
}; };
Wallet.prototype.createTxSync = function(toAddress, amountSatStr, utxos, opts) { Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos, opts) {
var pkr = this.publicKeyRing; var pkr = this.publicKeyRing;
var priv = this.privateKey; var priv = this.privateKey;
opts = opts || {}; opts = opts || {};
@ -655,6 +655,10 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, utxos, opts) {
throw new Error('publicKeyRing is not complete'); throw new Error('publicKeyRing is not complete');
} }
if (comment && comment.length > 100) {
throw new Error("comment can't be longer that 100 characters");
}
if (!opts.remainderOut) { if (!opts.remainderOut) {
opts.remainderOut = { opts.remainderOut = {
address: this._doGenerateAddress(true).toString() address: this._doGenerateAddress(true).toString()
@ -695,6 +699,7 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, utxos, opts) {
creator: myId, creator: myId,
createdTs: now, createdTs: now,
builder: b, builder: b,
comment: comment
}; };
var ntxid = this.txProposals.add(data); var ntxid = this.txProposals.add(data);

View File

@ -140,6 +140,7 @@ describe('Wallet model', function() {
var ntxid = w.createTxSync( var ntxid = w.createTxSync(
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
'123456789', '123456789',
null,
unspentTest unspentTest
); );
@ -147,11 +148,54 @@ describe('Wallet model', function() {
var txp = t.txps[ntxid]; var txp = t.txps[ntxid];
var tx = txp.builder.build(); var tx = txp.builder.build();
should.exist(tx); should.exist(tx);
chai.expect(txp.comment).to.be.null;
tx.isComplete().should.equal(false); tx.isComplete().should.equal(false);
Object.keys(txp.seenBy).length.should.equal(1); Object.keys(txp.seenBy).length.should.equal(1);
Object.keys(txp.signedBy).length.should.equal(1); Object.keys(txp.signedBy).length.should.equal(1);
}); });
it('#create with comment', function() {
var w = createW2();
var comment = 'This is a comment';
unspentTest[0].address = w.publicKeyRing.getAddress(1, true).toString();
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true);
var ntxid = w.createTxSync(
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
'123456789',
comment,
unspentTest
);
var t = w.txProposals;
var txp = t.txps[ntxid];
var tx = txp.builder.build();
should.exist(tx);
txp.comment.should.equal(comment);
});
it('#create throw exception on long comment', function() {
var w = createW2();
var comment = 'Lorem ipsum dolor sit amet, suas euismod vis te, velit deleniti vix an. Pri ex suscipit similique, inermis per';
unspentTest[0].address = w.publicKeyRing.getAddress(1, true).toString();
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true);
var badCreate = function() {
w.createTxSync(
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
'123456789',
comment,
unspentTest
);
}
chai.expect(badCreate).to.throw(Error);
});
it('#addressIsOwn', function() { it('#addressIsOwn', function() {
var w = createW2(); var w = createW2();
var l = w.getAddressesStr(); var l = w.getAddressesStr();
@ -178,6 +222,7 @@ describe('Wallet model', function() {
w.createTxSync( w.createTxSync(
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
'123456789', '123456789',
null,
unspentTest unspentTest
); );
var t = w.txProposals; var t = w.txProposals;
@ -428,7 +473,7 @@ describe('Wallet model', function() {
var w = createW2(); var w = createW2();
var utxo = createUTXO(w); var utxo = createUTXO(w);
w.blockchain.fixUnspent(utxo); w.blockchain.fixUnspent(utxo);
w.createTx(toAddress, amountSatStr, function(ntxid) { w.createTx(toAddress, amountSatStr, null, function(ntxid) {
ntxid.length.should.equal(64); ntxid.length.should.equal(64);
done(); done();
}); });
@ -439,7 +484,7 @@ describe('Wallet model', function() {
w.privateKey = null; w.privateKey = null;
var utxo = createUTXO(w); var utxo = createUTXO(w);
w.blockchain.fixUnspent(utxo); w.blockchain.fixUnspent(utxo);
w.createTx(toAddress, amountSatStr, function(ntxid) { w.createTx(toAddress, amountSatStr, null, function(ntxid) {
w.on('txProposalsUpdated', function() { w.on('txProposalsUpdated', function() {
w.getTxProposals()[0].signedByUs.should.equal(true); w.getTxProposals()[0].signedByUs.should.equal(true);
w.getTxProposals()[0].rejectedByUs.should.equal(false); w.getTxProposals()[0].rejectedByUs.should.equal(false);
@ -456,7 +501,7 @@ describe('Wallet model', function() {
w.privateKey = null; w.privateKey = null;
var utxo = createUTXO(w); var utxo = createUTXO(w);
w.blockchain.fixUnspent(utxo); w.blockchain.fixUnspent(utxo);
w.createTx(toAddress, amountSatStr, function(ntxid) { w.createTx(toAddress, amountSatStr, null, function(ntxid) {
w.on('txProposalsUpdated', function() { w.on('txProposalsUpdated', function() {
w.getTxProposals()[0].signedByUs.should.equal(false); w.getTxProposals()[0].signedByUs.should.equal(false);
w.getTxProposals()[0].rejectedByUs.should.equal(true); w.getTxProposals()[0].rejectedByUs.should.equal(true);
@ -470,7 +515,7 @@ describe('Wallet model', function() {
var w = createW2(null, 1); var w = createW2(null, 1);
var utxo = createUTXO(w); var utxo = createUTXO(w);
w.blockchain.fixUnspent(utxo); w.blockchain.fixUnspent(utxo);
w.createTx(toAddress, amountSatStr, function(ntxid) { w.createTx(toAddress, amountSatStr, null, function(ntxid) {
w.sendTx(ntxid, function(txid) { w.sendTx(ntxid, function(txid) {
txid.length.should.equal(64); txid.length.should.equal(64);
done(); done();

View File

@ -80,7 +80,7 @@ for (var n = 1; n <= N_LIMIT; n++) {
'amount': 10.0, 'amount': 10.0,
'confirmations': 2 'confirmations': 2
}]; }];
var ntxid = w.createTxSync(toAddress, amount, utxos); var ntxid = w.createTxSync(toAddress, amount, null, utxos);
var txp = w.txProposals.txps[ntxid]; var txp = w.txProposals.txps[ntxid];
var signTx = function(pk, cb) { var signTx = function(pk, cb) {