mirror of https://github.com/BTCPrivate/copay.git
merge
This commit is contained in:
commit
d8c3d08582
136
css/main.css
136
css/main.css
|
@ -496,6 +496,142 @@ a.loading {
|
||||||
vertical-align:middle
|
vertical-align:middle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* notifications */
|
||||||
|
|
||||||
|
.dr-notification-container {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-container.bottom {
|
||||||
|
bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-container.right {
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-container.left {
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-container.top {
|
||||||
|
top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-container.center {
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -190px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-wrapper {
|
||||||
|
width: 380px;
|
||||||
|
position: relative;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification {
|
||||||
|
width: 380px;
|
||||||
|
background-color: #2C3E50;
|
||||||
|
clear: both;
|
||||||
|
min-height: 80px;
|
||||||
|
max-height: 90px;
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
|
-ms-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #bfe2de;
|
||||||
|
border: 1px solid rgba(4, 94, 123, 0.85);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-close-btn {
|
||||||
|
-webkit-border-radius: 20px;
|
||||||
|
-moz-border-radius: 20px;
|
||||||
|
-ms-border-radius: 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 3px;
|
||||||
|
background-color: #2C3E50;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #adfaff;
|
||||||
|
border: 1px solid rgba(4, 94, 123, 0.85);
|
||||||
|
position: absolute;
|
||||||
|
right: -11px;
|
||||||
|
top: 5px;
|
||||||
|
-webkit-transition: all 0.35s cubic-bezier(0.31, 0.39, 0.21, 1.65);
|
||||||
|
-moz-transition: all 0.35s cubic-bezier(0.31, 0.39, 0.21, 1.65);
|
||||||
|
transition: all 0.35s cubic-bezier(0.31, 0.39, 0.21, 1.65);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.dr-notification-close-btn i {
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
.dr-notification-close-btn:hover {
|
||||||
|
-webkit-transform: scale3d(1.25, 1.25, 1);
|
||||||
|
-moz-transform: scale3d(1.25, 1.25, 1);
|
||||||
|
-ms-transform: scale3d(1.25, 1.25, 1);
|
||||||
|
transform: scale3d(1.25, 1.25, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-image {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-right: 1px solid rgba(4, 94, 123, 0.85);
|
||||||
|
float: left;
|
||||||
|
display: block;
|
||||||
|
font-size: 40px;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.dr-notification-image i {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 25px;
|
||||||
|
}
|
||||||
|
.dr-notification-image img {
|
||||||
|
margin: 15px;
|
||||||
|
max-width: 70px;
|
||||||
|
min-width: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-image.dr-notification-type-info {
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-image.dr-notification-type-warning {
|
||||||
|
color: #FFA226;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-image.dr-notification-type-error {
|
||||||
|
color: #FF4B4F;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-image.dr-notification-type-success {
|
||||||
|
color: #B4D455;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-image.success {
|
||||||
|
color: #B4D455;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-content {
|
||||||
|
padding-left: 100px;
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dr-notification-title {
|
||||||
|
color: white;
|
||||||
|
padding: 0px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.dr-notification-text {
|
||||||
|
margin-top: -5px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
input.ng-invalid-wallet-secret {
|
input.ng-invalid-wallet-secret {
|
||||||
background: #FFB6C1;
|
background: #FFB6C1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,8 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div notifications="middle right"></div>
|
||||||
|
|
||||||
<div id="main" class="row" ng-class="{'main-home': !$root.wallet}">
|
<div id="main" class="row" ng-class="{'main-home': !$root.wallet}">
|
||||||
<div class="large-12 columns" ng-view></div>
|
<div class="large-12 columns" ng-view></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -340,7 +342,7 @@
|
||||||
<div class="transactions" data-ng-controller="TransactionsController">
|
<div class="transactions" data-ng-controller="TransactionsController">
|
||||||
<div class="row" ng-show='$root.wallet.publicKeyRing.isComplete()'>
|
<div class="row" ng-show='$root.wallet.publicKeyRing.isComplete()'>
|
||||||
<div class="large-12 columns">
|
<div class="large-12 columns">
|
||||||
<h4>Transactions proposals <small>({{txs.length}})</small></h4>
|
<h4>Transaction proposals <small>({{txs.length}})</small></h4>
|
||||||
<div class="panel radius pending" ng-repeat="tx in txs | orderBy: 'createdTs':true">
|
<div class="panel radius pending" ng-repeat="tx in txs | orderBy: 'createdTs':true">
|
||||||
<div class="txheader">
|
<div class="txheader">
|
||||||
<div class="row m10">
|
<div class="row m10">
|
||||||
|
@ -362,7 +364,7 @@
|
||||||
<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">
|
||||||
<a href="#/transactions" class="has-tip" tooltip-popup-delay='1000' tooltip="{{cId === $root.wallet.getMyCopayerId() ? 'You' : $root.wallet.publicKeyRing.nicknameForCopayer(cId)}}">
|
<a href="#/transactions" class="has-tip" tooltip-popup-delay='1000' tooltip="{{cId === $root.wallet.getMyCopayerId() ? 'You' : $root.wallet.publicKeyRing.nicknameForCopayer(cId)}}">
|
||||||
<img src="./img/satoshi.gif" alt="cId" width="200">
|
<img src="./img/satoshi.gif" alt="{{cId}}" width="200">
|
||||||
</a>
|
</a>
|
||||||
</figure>
|
</figure>
|
||||||
<div class="box-status">
|
<div class="box-status">
|
||||||
|
@ -682,6 +684,7 @@ on supported browsers please check <a href="http://www.webrtc.org/">http://www.w
|
||||||
<script src="js/services/walletFactory.js"></script>
|
<script src="js/services/walletFactory.js"></script>
|
||||||
<script src="js/services/controllerUtils.js"></script>
|
<script src="js/services/controllerUtils.js"></script>
|
||||||
<script src="js/services/passphrase.js"></script>
|
<script src="js/services/passphrase.js"></script>
|
||||||
|
<script src="js/services/notifications.js"></script>
|
||||||
|
|
||||||
<script src="js/controllers/header.js"></script>
|
<script src="js/controllers/header.js"></script>
|
||||||
<script src="js/controllers/footer.js"></script>
|
<script src="js/controllers/footer.js"></script>
|
||||||
|
|
|
@ -7,6 +7,7 @@ var copayApp = window.copayApp = angular.module('copay',[
|
||||||
'ngRoute',
|
'ngRoute',
|
||||||
'mm.foundation',
|
'mm.foundation',
|
||||||
'monospaced.qrcode',
|
'monospaced.qrcode',
|
||||||
|
'notifications',
|
||||||
'copay.header',
|
'copay.header',
|
||||||
'copay.footer',
|
'copay.footer',
|
||||||
'copay.addresses',
|
'copay.addresses',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copay.header').controller('HeaderController',
|
angular.module('copay.header').controller('HeaderController',
|
||||||
function($scope, $rootScope, $location, walletFactory, controllerUtils) {
|
function($scope, $rootScope, $location, $notification, walletFactory, controllerUtils) {
|
||||||
$scope.menu = [
|
$scope.menu = [
|
||||||
{
|
{
|
||||||
'title': 'Addresses',
|
'title': 'Addresses',
|
||||||
|
@ -27,6 +27,15 @@ angular.module('copay.header').controller('HeaderController',
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Initialize alert notification (not show when init wallet)
|
||||||
|
$rootScope.txAlertCount = 0;
|
||||||
|
$notification.enableHtml5Mode(); // for chrome: if support, enable it
|
||||||
|
$rootScope.$watch('txAlertCount', function(txAlertCount) {
|
||||||
|
if (txAlertCount && txAlertCount > 0) {
|
||||||
|
$notification.info('New Transaction', ($rootScope.txAlertCount == 1) ? 'You have a pending transaction proposal' : 'You have ' + $rootScope.txAlertCount + ' pending transaction proposals', txAlertCount);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$scope.isActive = function(item) {
|
$scope.isActive = function(item) {
|
||||||
if (item.link && item.link.replace('#','') == $location.path()) {
|
if (item.link && item.link.replace('#','') == $location.path()) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -2,8 +2,13 @@
|
||||||
|
|
||||||
angular.module('copay.signin').controller('SigninController',
|
angular.module('copay.signin').controller('SigninController',
|
||||||
function($scope, $rootScope, $location, walletFactory, controllerUtils, Passphrase) {
|
function($scope, $rootScope, $location, walletFactory, controllerUtils, Passphrase) {
|
||||||
|
var cmp = function(o1, o2){
|
||||||
|
var v1 = o1.show.toLowerCase(), v2 = o2.show.toLowerCase();
|
||||||
|
return v1 > v2 ? 1 : ( v1 < v2 ) ? -1 : 0;
|
||||||
|
};
|
||||||
|
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
$scope.wallets = walletFactory.getWallets();
|
$scope.wallets = walletFactory.getWallets().sort(cmp);
|
||||||
$scope.selectedWalletId = $scope.wallets.length ? $scope.wallets[0].id : null;
|
$scope.selectedWalletId = $scope.wallets.length ? $scope.wallets[0].id : null;
|
||||||
$scope.openPassword = '';
|
$scope.openPassword = '';
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ angular.module('copay.transactions').controller('TransactionsController',
|
||||||
|
|
||||||
$scope.send = function (ntxid) {
|
$scope.send = function (ntxid) {
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
|
$rootScope.txAlertCount = 0;
|
||||||
var w = $rootScope.wallet;
|
var w = $rootScope.wallet;
|
||||||
w.sendTx(ntxid, function(txid) {
|
w.sendTx(ntxid, function(txid) {
|
||||||
console.log('[transactions.js.68:txid:] SENTTX CALLBACK',txid); //TODO
|
console.log('[transactions.js.68:txid:] SENTTX CALLBACK',txid); //TODO
|
||||||
|
@ -65,6 +66,7 @@ angular.module('copay.transactions').controller('TransactionsController',
|
||||||
|
|
||||||
$scope.reject = function (ntxid) {
|
$scope.reject = function (ntxid) {
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
|
$rootScope.txAlertCount = 0;
|
||||||
var w = $rootScope.wallet;
|
var w = $rootScope.wallet;
|
||||||
w.reject(ntxid);
|
w.reject(ntxid);
|
||||||
$rootScope.flashMessage = {type:'warning', message: 'Transaction rejected by you'};
|
$rootScope.flashMessage = {type:'warning', message: 'Transaction rejected by you'};
|
||||||
|
|
|
@ -180,8 +180,9 @@ TxProposals.prototype._mergeBuilder = function(myTxps, theirTxps, mergeInfo) {
|
||||||
};
|
};
|
||||||
|
|
||||||
TxProposals.prototype.add = function(data) {
|
TxProposals.prototype.add = function(data) {
|
||||||
var id = data.builder.build().getNormalizedHash().toString('hex');
|
var ntxid = data.builder.build().getNormalizedHash().toString('hex');
|
||||||
this.txps[id] = new TxProposal(data);
|
this.txps[ntxid] = new TxProposal(data);
|
||||||
|
return ntxid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -191,12 +192,20 @@ TxProposals.prototype.setSent = function(ntxid,txid) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
TxProposals.prototype.getTxProposal = function(ntxid) {
|
TxProposals.prototype.getTxProposal = function(ntxid, copayers) {
|
||||||
var txp = this.txps[ntxid];
|
var txp = this.txps[ntxid];
|
||||||
var i = JSON.parse(JSON.stringify(txp));
|
var i = JSON.parse(JSON.stringify(txp));
|
||||||
i.builder = txp.builder;
|
i.builder = txp.builder;
|
||||||
i.ntxid = ntxid;
|
i.ntxid = ntxid;
|
||||||
i.peerActions = {};
|
i.peerActions = {};
|
||||||
|
|
||||||
|
if (copayers) {
|
||||||
|
for(var j=0; j < copayers.length; j++) {
|
||||||
|
var p = copayers[j];
|
||||||
|
i.peerActions[p] = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for(var p in txp.seenBy){
|
for(var p in txp.seenBy){
|
||||||
i.peerActions[p]={seen: txp.seenBy[p]};
|
i.peerActions[p]={seen: txp.seenBy[p]};
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,7 @@ Wallet.prototype._handleTxProposals = function(senderId, data, isInbound) {
|
||||||
if (mergeInfo.hasChanged || addSeen) {
|
if (mergeInfo.hasChanged || addSeen) {
|
||||||
this.log('### BROADCASTING txProposals. ');
|
this.log('### BROADCASTING txProposals. ');
|
||||||
recipients = null;
|
recipients = null;
|
||||||
|
TODO: only diff
|
||||||
this.sendTxProposals(recipients);
|
this.sendTxProposals(recipients);
|
||||||
}
|
}
|
||||||
this.emit('txProposalsUpdated', this.txProposals);
|
this.emit('txProposalsUpdated', this.txProposals);
|
||||||
|
@ -114,7 +115,7 @@ Wallet.prototype._handleData = function(senderId, data, isInbound) {
|
||||||
break;
|
break;
|
||||||
case 'walletReady':
|
case 'walletReady':
|
||||||
this.sendPublicKeyRing(senderId);
|
this.sendPublicKeyRing(senderId);
|
||||||
this.sendTxProposals(senderId);
|
this.sendTxProposals(senderId); // send old
|
||||||
break;
|
break;
|
||||||
case 'publicKeyRing':
|
case 'publicKeyRing':
|
||||||
this._handlePublicKeyRing(senderId, data, isInbound);
|
this._handlePublicKeyRing(senderId, data, isInbound);
|
||||||
|
@ -223,12 +224,23 @@ Wallet.prototype.getOnlinePeerIDs = function() {
|
||||||
return this.network.getOnlinePeerIDs();
|
return this.network.getOnlinePeerIDs();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.getRegisteredCopayerIds = function() {
|
||||||
|
var l = this.publicKeyRing.registeredCopayers();
|
||||||
|
var copayers = [];
|
||||||
|
for (var i = 0; i < l; i++) {
|
||||||
|
var cid = this.getCopayerId(i);
|
||||||
|
copayers.push(cid);
|
||||||
|
}
|
||||||
|
return copayers;
|
||||||
|
};
|
||||||
|
|
||||||
Wallet.prototype.getRegisteredPeerIds = function() {
|
Wallet.prototype.getRegisteredPeerIds = function() {
|
||||||
var l = this.publicKeyRing.registeredCopayers();
|
var l = this.publicKeyRing.registeredCopayers();
|
||||||
if (this.registeredPeerIds.length !== l) {
|
if (this.registeredPeerIds.length !== l) {
|
||||||
this.registeredPeerIds = [];
|
this.registeredPeerIds = [];
|
||||||
|
var copayers = this.getRegisteredCopayerIds();
|
||||||
for (var i = 0; i < l; i++) {
|
for (var i = 0; i < l; i++) {
|
||||||
var cid = this.getCopayerId(i);
|
var cid = copayers[i];
|
||||||
var pid = this.network.peerFromCopayer(cid);
|
var pid = this.network.peerFromCopayer(cid);
|
||||||
this.registeredPeerIds.push({
|
this.registeredPeerIds.push({
|
||||||
peerId: pid,
|
peerId: pid,
|
||||||
|
@ -275,14 +287,18 @@ Wallet.prototype.toEncryptedObj = function() {
|
||||||
return this.storage.export(walletObj);
|
return this.storage.export(walletObj);
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.sendTxProposals = function(recipients) {
|
Wallet.prototype.sendTxProposals = function(recipients, ntxid) {
|
||||||
this.log('### SENDING txProposals TO:', recipients || 'All', this.txProposals);
|
this.log('### SENDING txProposals TO:', recipients || 'All', this.txProposals);
|
||||||
|
|
||||||
|
var toSend = ntxid ? [ntxid] : this.getTxProposals.getNtxids();
|
||||||
|
for(var i in toSend) {
|
||||||
|
var id = toSend[i];
|
||||||
this.network.send(recipients, {
|
this.network.send(recipients, {
|
||||||
type: 'txProposals',
|
type: 'txProposals',
|
||||||
txProposals: this.txProposals.toObj(),
|
txProposals: this.txProposals.toObj(id),
|
||||||
walletId: this.id,
|
walletId: this.id,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.sendWalletReady = function(recipients) {
|
Wallet.prototype.sendWalletReady = function(recipients) {
|
||||||
|
@ -326,8 +342,9 @@ Wallet.prototype.generateAddress = function(isChange) {
|
||||||
|
|
||||||
Wallet.prototype.getTxProposals = function() {
|
Wallet.prototype.getTxProposals = function() {
|
||||||
var ret = [];
|
var ret = [];
|
||||||
|
var copayers = this.getRegisteredCopayerIds();
|
||||||
for (var k in this.txProposals.txps) {
|
for (var k in this.txProposals.txps) {
|
||||||
var i = this.txProposals.getTxProposal(k);
|
var i = this.txProposals.getTxProposal(k, copayers);
|
||||||
i.signedByUs = i.signedBy[this.getMyCopayerId()] ? true : false;
|
i.signedByUs = i.signedBy[this.getMyCopayerId()] ? true : false;
|
||||||
i.rejectedByUs = i.rejectedBy[this.getMyCopayerId()] ? true : false;
|
i.rejectedByUs = i.rejectedBy[this.getMyCopayerId()] ? true : false;
|
||||||
if (this.totalCopayers - i.rejectCount < this.requiredCopayers)
|
if (this.totalCopayers - i.rejectCount < this.requiredCopayers)
|
||||||
|
@ -345,7 +362,7 @@ Wallet.prototype.reject = function(ntxid) {
|
||||||
if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) return;
|
if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) return;
|
||||||
|
|
||||||
txp.rejectedBy[myId] = Date.now();
|
txp.rejectedBy[myId] = Date.now();
|
||||||
this.sendTxProposals();
|
this.sendTxProposals(null, ntxid);
|
||||||
this.store();
|
this.store();
|
||||||
this.emit('txProposalsUpdated');
|
this.emit('txProposalsUpdated');
|
||||||
};
|
};
|
||||||
|
@ -366,7 +383,7 @@ Wallet.prototype.sign = function(ntxid) {
|
||||||
|
|
||||||
if (b.signaturesAdded > before) {
|
if (b.signaturesAdded > before) {
|
||||||
txp.signedBy[myId] = Date.now();
|
txp.signedBy[myId] = Date.now();
|
||||||
this.sendTxProposals();
|
this.sendTxProposals(null, ntxid);
|
||||||
this.store();
|
this.store();
|
||||||
this.emit('txProposalsUpdated');
|
this.emit('txProposalsUpdated');
|
||||||
return true;
|
return true;
|
||||||
|
@ -392,7 +409,7 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
|
||||||
if (txid) {
|
if (txid) {
|
||||||
self.txProposals.setSent(ntxid, txid);
|
self.txProposals.setSent(ntxid, txid);
|
||||||
}
|
}
|
||||||
self.sendTxProposals();
|
self.sendTxProposals(null, ntxid);
|
||||||
self.store();
|
self.store();
|
||||||
return cb(txid);
|
return cb(txid);
|
||||||
});
|
});
|
||||||
|
@ -502,9 +519,10 @@ Wallet.prototype.createTx = function(toAddress, amountSatStr, opts, cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.getSafeUnspent(function(unspentList) {
|
self.getSafeUnspent(function(unspentList) {
|
||||||
if (self.createTxSync(toAddress, amountSatStr, unspentList, opts)) {
|
var ntxid = self.createTxSync(toAddress, amountSatStr, unspentList, opts);
|
||||||
self.sendPublicKeyRing(); // Change Address
|
if (ntxid) {
|
||||||
self.sendTxProposals();
|
self.sendPublicKeyRing(); // For the new change Address
|
||||||
|
self.sendTxProposals(null, ntxid);
|
||||||
self.store();
|
self.store();
|
||||||
self.emit('txProposalsUpdated');
|
self.emit('txProposalsUpdated');
|
||||||
}
|
}
|
||||||
|
@ -558,8 +576,7 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, utxos, opts) {
|
||||||
builder: b,
|
builder: b,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.txProposals.add(data);
|
return this.txProposals.add(data);
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.disconnect = function() {
|
Wallet.prototype.disconnect = function() {
|
||||||
|
|
|
@ -73,7 +73,8 @@ Storage.prototype._write = function(k,v) {
|
||||||
|
|
||||||
// get value by key
|
// get value by key
|
||||||
Storage.prototype.getGlobal = function(k) {
|
Storage.prototype.getGlobal = function(k) {
|
||||||
return localStorage.getItem(k);
|
var item = localStorage.getItem(k);
|
||||||
|
return item == 'undefined' ? undefined : item;
|
||||||
};
|
};
|
||||||
|
|
||||||
// set value for key
|
// set value for key
|
||||||
|
|
|
@ -128,6 +128,8 @@ angular.module('copay.controllerUtils')
|
||||||
var w = $rootScope.wallet;
|
var w = $rootScope.wallet;
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
|
|
||||||
|
var myCopayerId = w.getMyCopayerId();
|
||||||
|
var pending = 0;
|
||||||
var inT = w.getTxProposals();
|
var inT = w.getTxProposals();
|
||||||
var txs = [];
|
var txs = [];
|
||||||
|
|
||||||
|
@ -149,13 +151,16 @@ angular.module('copay.controllerUtils')
|
||||||
i.fee = i.builder.feeSat/bitcore.util.COIN;
|
i.fee = i.builder.feeSat/bitcore.util.COIN;
|
||||||
i.missingSignatures = tx.countInputMissingSignatures(0);
|
i.missingSignatures = tx.countInputMissingSignatures(0);
|
||||||
txs.push(i);
|
txs.push(i);
|
||||||
});
|
|
||||||
$rootScope.txs = txs;
|
if (myCopayerId != i.creator && !i.finallyRejected && !i.sentTs && !i.rejectedByUs && !i.signedByUs) {
|
||||||
var pending = 0;
|
|
||||||
for(var i=0; i<txs.length;i++) {
|
|
||||||
if (!txs[i].finallyRejected && !txs[i].sentTs) {
|
|
||||||
pending++;
|
pending++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$rootScope.txs = txs;
|
||||||
|
if ($rootScope.pendingTxCount < pending) {
|
||||||
|
$rootScope.txAlertCount = pending;
|
||||||
}
|
}
|
||||||
$rootScope.pendingTxCount = pending;
|
$rootScope.pendingTxCount = pending;
|
||||||
w.removeListener('txProposalsUpdated',root.updateTxs)
|
w.removeListener('txProposalsUpdated',root.updateTxs)
|
||||||
|
|
|
@ -0,0 +1,259 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('notifications', []).
|
||||||
|
factory('$notification', ['$timeout',function($timeout){
|
||||||
|
|
||||||
|
console.log('notification service online');
|
||||||
|
var notifications = JSON.parse(localStorage.getItem('$notifications')) || [],
|
||||||
|
queue = [];
|
||||||
|
|
||||||
|
var settings = {
|
||||||
|
info: { duration: 5000, enabled: true },
|
||||||
|
warning: { duration: 5000, enabled: true },
|
||||||
|
error: { duration: 5000, enabled: true },
|
||||||
|
success: { duration: 5000, enabled: true },
|
||||||
|
progress: { duration: 0, enabled: true },
|
||||||
|
custom: { duration: 35000, enabled: true },
|
||||||
|
details: true,
|
||||||
|
localStorage: false,
|
||||||
|
html5Mode: false,
|
||||||
|
html5DefaultIcon: '../img/satoshi.gif'
|
||||||
|
};
|
||||||
|
|
||||||
|
function html5Notify(icon, title, content, ondisplay, onclose){
|
||||||
|
if(window.webkitNotifications.checkPermission() === 0){
|
||||||
|
if(!icon){
|
||||||
|
icon = '../img/favicon.ico';
|
||||||
|
}
|
||||||
|
var noti = window.webkitNotifications.createNotification(icon, title, content);
|
||||||
|
if(typeof ondisplay === 'function'){
|
||||||
|
noti.ondisplay = ondisplay;
|
||||||
|
}
|
||||||
|
if(typeof onclose === 'function'){
|
||||||
|
noti.onclose = onclose;
|
||||||
|
}
|
||||||
|
noti.show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
settings.html5Mode = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
/* ========== SETTINGS RELATED METHODS =============*/
|
||||||
|
|
||||||
|
disableHtml5Mode: function(){
|
||||||
|
settings.html5Mode = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
disableType: function(notificationType){
|
||||||
|
settings[notificationType].enabled = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
enableHtml5Mode: function(){
|
||||||
|
// settings.html5Mode = true;
|
||||||
|
settings.html5Mode = this.requestHtml5ModePermissions();
|
||||||
|
},
|
||||||
|
|
||||||
|
enableType: function(notificationType){
|
||||||
|
settings[notificationType].enabled = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
getSettings: function(){
|
||||||
|
return settings;
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleType: function(notificationType){
|
||||||
|
settings[notificationType].enabled = !settings[notificationType].enabled;
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleHtml5Mode: function(){
|
||||||
|
settings.html5Mode = !settings.html5Mode;
|
||||||
|
},
|
||||||
|
|
||||||
|
requestHtml5ModePermissions: function(){
|
||||||
|
if (window.webkitNotifications){
|
||||||
|
console.log('notifications are available');
|
||||||
|
if (window.webkitNotifications.checkPermission() === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
window.webkitNotifications.requestPermission(function(){
|
||||||
|
if(window.webkitNotifications.checkPermission() === 0){
|
||||||
|
settings.html5Mode = true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
settings.html5Mode = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
console.log('notifications are not supported');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/* ============ QUERYING RELATED METHODS ============*/
|
||||||
|
|
||||||
|
getAll: function(){
|
||||||
|
// Returns all notifications that are currently stored
|
||||||
|
return notifications;
|
||||||
|
},
|
||||||
|
|
||||||
|
getQueue: function(){
|
||||||
|
return queue;
|
||||||
|
},
|
||||||
|
|
||||||
|
/* ============== NOTIFICATION METHODS ==============*/
|
||||||
|
|
||||||
|
info: function(title, content, userData){
|
||||||
|
console.log(title, content);
|
||||||
|
return this.awesomeNotify('info','info', title, content, userData);
|
||||||
|
},
|
||||||
|
|
||||||
|
error: function(title, content, userData){
|
||||||
|
return this.awesomeNotify('error', 'remove', title, content, userData);
|
||||||
|
},
|
||||||
|
|
||||||
|
success: function(title, content, userData){
|
||||||
|
return this.awesomeNotify('success', 'ok', title, content, userData);
|
||||||
|
},
|
||||||
|
|
||||||
|
warning: function(title, content, userData){
|
||||||
|
return this.awesomeNotify('warning', 'exclamation', title, content, userData);
|
||||||
|
},
|
||||||
|
|
||||||
|
awesomeNotify: function(type, icon, title, content, userData){
|
||||||
|
/**
|
||||||
|
* Supposed to wrap the makeNotification method for drawing icons using font-awesome
|
||||||
|
* rather than an image.
|
||||||
|
*
|
||||||
|
* Need to find out how I'm going to make the API take either an image
|
||||||
|
* resource, or a font-awesome icon and then display either of them.
|
||||||
|
* Also should probably provide some bits of color, could do the coloring
|
||||||
|
* through classes.
|
||||||
|
*/
|
||||||
|
// image = '<i class="icon-' + image + '"></i>';
|
||||||
|
return this.makeNotification(type, false, icon, title, content, userData);
|
||||||
|
},
|
||||||
|
|
||||||
|
notify: function(image, title, content, userData){
|
||||||
|
// Wraps the makeNotification method for displaying notifications with images
|
||||||
|
// rather than icons
|
||||||
|
return this.makeNotification('custom', image, true, title, content, userData);
|
||||||
|
},
|
||||||
|
|
||||||
|
makeNotification: function(type, image, icon, title, content, userData){
|
||||||
|
var notification = {
|
||||||
|
'type': type,
|
||||||
|
'image': image,
|
||||||
|
'icon': icon,
|
||||||
|
'title': title,
|
||||||
|
'content': content,
|
||||||
|
'timestamp': +new Date(),
|
||||||
|
'userData': userData
|
||||||
|
};
|
||||||
|
notifications.push(notification);
|
||||||
|
|
||||||
|
if(settings.html5Mode){
|
||||||
|
html5Notify(image, title, content, function(){
|
||||||
|
console.log("inner on display function");
|
||||||
|
}, function(){
|
||||||
|
console.log("inner on close function");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
queue.push(notification);
|
||||||
|
$timeout(function removeFromQueueTimeout(){
|
||||||
|
queue.splice(queue.indexOf(notification), 1);
|
||||||
|
}, settings[type].duration);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.save();
|
||||||
|
return notification;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/* ============ PERSISTENCE METHODS ============ */
|
||||||
|
|
||||||
|
save: function(){
|
||||||
|
// Save all the notifications into localStorage
|
||||||
|
// console.log(JSON);
|
||||||
|
if(settings.localStorage){
|
||||||
|
localStorage.setItem('$notifications', JSON.stringify(notifications));
|
||||||
|
}
|
||||||
|
// console.log(localStorage.getItem('$notifications'));
|
||||||
|
},
|
||||||
|
|
||||||
|
restore: function(){
|
||||||
|
// Load all notifications from localStorage
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: function(){
|
||||||
|
notifications = [];
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}]).
|
||||||
|
directive('notifications', ['$notification', '$compile', function($notification, $compile){
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* It should also parse the arguments passed to it that specify
|
||||||
|
* its position on the screen like "bottom right" and apply those
|
||||||
|
* positions as a class to the container element
|
||||||
|
*
|
||||||
|
* Finally, the directive should have its own controller for
|
||||||
|
* handling all of the notifications from the notification service
|
||||||
|
*/
|
||||||
|
// console.log('this is a new directive');
|
||||||
|
var html =
|
||||||
|
'<div class="dr-notification-wrapper" ng-repeat="noti in queue">' +
|
||||||
|
'<div class="dr-notification-close-btn" ng-click="removeNotification(noti)">' +
|
||||||
|
'<i class="fi-x"></i>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="dr-notification">' +
|
||||||
|
'<div class="dr-notification-image dr-notification-type-{{noti.type}}" ng-switch on="noti.image">' +
|
||||||
|
'<i class="icon-{{noti.icon}}" ng-switch-when="false"></i>' +
|
||||||
|
'<img ng-src="{{noti.image}}" ng-switch-default />' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="dr-notification-content">' +
|
||||||
|
'<h3 class="dr-notification-title">{{noti.title}}</h3>' +
|
||||||
|
'<p class="dr-notification-text">{{noti.content}}</p>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
|
||||||
|
function link(scope, element, attrs){
|
||||||
|
var position = attrs.notifications;
|
||||||
|
position = position.split(' ');
|
||||||
|
element.addClass('dr-notification-container');
|
||||||
|
for(var i = 0; i < position.length ; i++){
|
||||||
|
element.addClass(position[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
scope: {},
|
||||||
|
template: html,
|
||||||
|
link: link,
|
||||||
|
controller: ['$scope', function NotificationsCtrl( $scope ){
|
||||||
|
$scope.queue = $notification.getQueue();
|
||||||
|
|
||||||
|
$scope.removeNotification = function(noti){
|
||||||
|
$scope.queue.splice($scope.queue.indexOf(noti), 1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
};
|
||||||
|
}]);
|
Loading…
Reference in New Issue