Merge pull request #673 from yemel/feature/proposal-comments

Add user comment to transaction propossal
This commit is contained in:
Manuel Aráoz 2014-06-13 11:30:12 -03:00
commit 8d3ebcc0e2
8 changed files with 93 additions and 15 deletions

View File

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

View File

@ -189,7 +189,7 @@
src="./img/satoshi.gif"
/>
<div class="size-12 text-center text-gray" stype="margin-top:4px">
{{c.nick || 'NN'}}
{{c.nick}}
</div>
</div>
</div>
@ -456,6 +456,9 @@
</div>
<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">
<figure class="left">
@ -665,6 +668,23 @@
</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="large-5 columns">
<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 amount = (form.amount.$modelValue * 100000000).toFixed(); // satoshi to string
var comment = form.comment.$modelValue;
var w = $rootScope.wallet;
w.createTx(address, amount,function() {
w.createTx(address, amount, comment, function() {
$scope.loading = false;
$rootScope.$flashMessage = { message: 'The transaction proposal has been created', type: 'success'};
$rootScope.$digest();
});
// reset fields
$scope.address = null;
$scope.amount = null;
form.address.$pristine = true;
form.amount.$pristine = true;
$scope.address = $scope.amount = $scope.comment = null;
form.address.$pristine = form.amount.$pristine = form.comment.$pristine = true;
};
// QR code Scanner

View File

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

View File

@ -20,6 +20,7 @@ function TxProposal(opts) {
this.sentTs = opts.sentTs || null;
this.sentTxid = opts.sentTxid || null;
this.inputChainPaths = opts.inputChainPaths || [];
this.comment = opts.comment || null;
}
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;
if (typeof opts === 'function') {
cb = opts;
@ -633,7 +633,7 @@ Wallet.prototype.createTx = function(toAddress, amountSatStr, opts, cb) {
}
this.getUnspent(function(err, safeUnspent) {
var ntxid = self.createTxSync(toAddress, amountSatStr, safeUnspent, opts);
var ntxid = self.createTxSync(toAddress, amountSatStr, comment, safeUnspent, opts);
if (ntxid) {
self.sendIndexes();
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 priv = this.privateKey;
opts = opts || {};
@ -655,6 +655,10 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, utxos, opts) {
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) {
opts.remainderOut = {
address: this._doGenerateAddress(true).toString()
@ -695,6 +699,7 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, utxos, opts) {
creator: myId,
createdTs: now,
builder: b,
comment: comment
};
var ntxid = this.txProposals.add(data);

View File

@ -140,6 +140,7 @@ describe('Wallet model', function() {
var ntxid = w.createTxSync(
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
'123456789',
null,
unspentTest
);
@ -147,11 +148,54 @@ describe('Wallet model', function() {
var txp = t.txps[ntxid];
var tx = txp.builder.build();
should.exist(tx);
chai.expect(txp.comment).to.be.null;
tx.isComplete().should.equal(false);
Object.keys(txp.seenBy).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() {
var w = createW2();
var l = w.getAddressesStr();
@ -178,6 +222,7 @@ describe('Wallet model', function() {
w.createTxSync(
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
'123456789',
null,
unspentTest
);
var t = w.txProposals;
@ -428,7 +473,7 @@ describe('Wallet model', function() {
var w = createW2();
var utxo = createUTXO(w);
w.blockchain.fixUnspent(utxo);
w.createTx(toAddress, amountSatStr, function(ntxid) {
w.createTx(toAddress, amountSatStr, null, function(ntxid) {
ntxid.length.should.equal(64);
done();
});
@ -439,7 +484,7 @@ describe('Wallet model', function() {
w.privateKey = null;
var utxo = createUTXO(w);
w.blockchain.fixUnspent(utxo);
w.createTx(toAddress, amountSatStr, function(ntxid) {
w.createTx(toAddress, amountSatStr, null, function(ntxid) {
w.on('txProposalsUpdated', function() {
w.getTxProposals()[0].signedByUs.should.equal(true);
w.getTxProposals()[0].rejectedByUs.should.equal(false);
@ -456,7 +501,7 @@ describe('Wallet model', function() {
w.privateKey = null;
var utxo = createUTXO(w);
w.blockchain.fixUnspent(utxo);
w.createTx(toAddress, amountSatStr, function(ntxid) {
w.createTx(toAddress, amountSatStr, null, function(ntxid) {
w.on('txProposalsUpdated', function() {
w.getTxProposals()[0].signedByUs.should.equal(false);
w.getTxProposals()[0].rejectedByUs.should.equal(true);
@ -470,7 +515,7 @@ describe('Wallet model', function() {
var w = createW2(null, 1);
var utxo = createUTXO(w);
w.blockchain.fixUnspent(utxo);
w.createTx(toAddress, amountSatStr, function(ntxid) {
w.createTx(toAddress, amountSatStr, null, function(ntxid) {
w.sendTx(ntxid, function(txid) {
txid.length.should.equal(64);
done();

View File

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