Merge pull request #2059 from cmgustavo/bug/mobile-15

Bug/mobile 15
This commit is contained in:
Matias Alejo Garcia 2014-12-09 19:31:10 -03:00
commit d3625226f0
12 changed files with 193 additions and 125 deletions

18
2fa
View file

@ -1,18 +0,0 @@
- 2-2
1 desktop
1 mobile
- each device creates its own key
- both devices are expected to be logged using the same profile (email / password)
- if using local storage -> all is OK.
- keys will be safe an will never leave the device
- if using insight
both 'copayers' need to have a new and different password for accessing the wallet.
-

View file

@ -81,7 +81,7 @@ header .alt-currency {
color: #fff;
}
.panel h3, .box-setup h3 {
.panel h3 {
font-weight: 700;
font-size: 16px;
color: #2C3E50;
@ -362,13 +362,6 @@ a:hover {
color: #8597A7;
}
.box-setup {
margin-bottom: 1rem;
padding: 1.3rem;
border-radius: 3px;
background: #fff;
}
.box-setup-footer {
overflow: hidden;
margin-top: 1rem;
@ -403,6 +396,13 @@ a:hover {
background-color: #C0392A;
}
.box-notification a.close-notification {
position: absolute;
top: 8px;
right: 10px;
font-size: 24px;
}
.panel.last-transactions {
padding: 0;
margin-bottom: 2.25rem;
@ -971,8 +971,9 @@ table tbody tr:last-child td {
border-bottom: none;
}
table tr.deleting {
background: #FCD5D5;
table.manage-wallets tr:hover {
background-color: #eee;
cursor: pointer;
}
input[type=date], input[type=datetime-local], input[type=datetime], input[type=email], input[type=month], input[type=number], input[type=password], input[type=search], input[type=tel], input[type=text], input[type=time], input[type=url], input[type=week], textarea {
@ -1425,18 +1426,12 @@ a.text-warning:hover {color: #FD7262;}
background-color: #1ABC9C;
}
.box-setup .panel {
.panel {
background-color: #2C3E50;
padding: 1rem;
border: 0;
}
.box-setup h1 {
font-size: 16px;
text-transform: uppercase;
text-align: center;
}
.panel qrcode {
background-color: white;
}

View file

@ -148,6 +148,7 @@ angular.module('copayApp.controllers').controller('HomeController', function($sc
$scope.error = 'Unknown error';
}
$rootScope.starting = false;
$scope.loading = null;
$timeout(function(){
$rootScope.$digest();
},1)

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('ProfileController', function($scope, $rootScope, $location, $modal, $filter, $timeout, backupService, identityService) {
angular.module('copayApp.controllers').controller('ProfileController', function($scope, $rootScope, $location, $modal, $filter, $timeout, backupService, identityService, isMobile) {
$scope.username = $rootScope.iden.getName();
$scope.isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
$scope.isSafari = isMobile.Safari();
$rootScope.title = 'Profile';
$scope.hideAdv = true;
@ -13,18 +13,7 @@ angular.module('copayApp.controllers').controller('ProfileController', function(
$scope.viewProfileBackup = function() {
$scope.backupProfilePlainText = backupService.profileEncrypted($rootScope.iden);
$scope.hideViewProfileBackup = true;
};
$scope.deleteWallet = function(w) {
if (!w) return;
identityService.deleteWallet(w, function(err) {
$scope.loading = false;
if (err) {
copay.logger.warn(err);
}
$scope.setWallets();
});
};
};
$scope.init = function() {
@ -52,30 +41,7 @@ angular.module('copayApp.controllers').controller('ProfileController', function(
$timeout(function(){
$scope.$digest();
})
};
$scope.downloadWalletBackup = function(w) {
if (!w) return;
backupService.walletDownload(w);
}
$scope.viewWalletBackup = function(w) {
var ModalInstanceCtrl = function($scope, $modalInstance) {
if (!w) return;
$scope.backupWalletPlainText = backupService.walletEncrypted(w);
$scope.hideViewWalletBackup = true;
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
};
$modal.open({
templateUrl: 'views/modals/backup-text.html',
windowClass: 'tiny',
controller: ModalInstanceCtrl
});
};
};
$scope.deleteProfile = function() {
identityService.deleteProfile(function(err, res) {
@ -90,4 +56,53 @@ angular.module('copayApp.controllers').controller('ProfileController', function(
}, 1);
});
};
$scope.showWalletInfo = function(w) {
var ModalInstanceCtrl = function($scope, $modalInstance) {
if (!w) return;
$scope.isSafari = isMobile.Safari();
$scope.item = w;
$scope.error = null;
$scope.success = null;
$scope.deleteWallet = function() {
$scope.loading = true;
identityService.deleteWallet($scope.item, function(err) {
if (err) {
$scope.loading = null;
$scope.error = err.message;
copay.logger.warn(err);
}
else {
$modalInstance.close($scope.item.name || $scope.item.id);
}
});
};
$scope.downloadWalletBackup = function() {
backupService.walletDownload($scope.item);
};
$scope.viewWalletBackup = function() {
$scope.backupWalletPlainText = backupService.walletEncrypted($scope.item);
};
$scope.close = function() {
$modalInstance.dismiss('cancel');
};
};
var modalInstance = $modal.open({
templateUrl: 'views/modals/wallet-info.html',
windowClass: 'tiny',
controller: ModalInstanceCtrl
});
modalInstance.result.then(function(walletName) {
$scope.loading = false;
$scope.success = 'The wallet "' + walletName + '" was deleted';
$scope.setWallets();
});
};
});

View file

@ -120,7 +120,7 @@ angular.module('copayApp.services')
};
root.deleteWallet = function(w, cb) {
$rootScope.iden.deleteWallet(w.id, cb);
$rootScope.iden.deleteWallet(w.getId(), cb);
};
root.isFocused = function(wid) {

View file

@ -17,6 +17,9 @@ var isMobile = {
Windows: function() {
return !!navigator.userAgent.match(/IEMobile/i);
},
Safari: function() {
return Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
},
any: function() {
return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
}

View file

@ -16,7 +16,7 @@ describe("Unit: Controllers", function() {
config.plugins.LocalStorage = true;
config.plugins.GoogleDrive = null;
config.plugins.InsightStorage = null;
config.plugins.EncryptedInsightStorage= null;
config.plugins.EncryptedInsightStorage = null;
var anAddr = 'mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy';
var anAmount = 1000;
@ -50,6 +50,10 @@ describe("Unit: Controllers", function() {
$rootScope.safeUnspentCount = 1;
$rootScope.pendingTxCount = 0;
//
// TODO Use the REAL wallet, and stub only networking and DB components!
//
var w = {};
w.id = 1234;
w.isComplete = sinon.stub().returns(true);
@ -92,7 +96,9 @@ describe("Unit: Controllers", function() {
'e': 'errmsg',
'loading': false
});
w.sizes = sinon.stub().returns({tota:1234});
w.sizes = sinon.stub().returns({
tota: 1234
});
w.getBalance = sinon.stub().returns(10000);
w.publicKeyRing = sinon.stub().yields(null);
w.publicKeyRing.nicknameForCopayer = sinon.stub().returns('nickcopayer');
@ -103,11 +109,11 @@ describe("Unit: Controllers", function() {
}]);
var iden = {};
iden.deleteWallet = sinon.stub().yields(null);
iden.getLastFocusedWallet = sinon.stub().returns(null);
iden.listWallets = sinon.stub().returns([w]);
iden.getWalletById = sinon.stub().returns(w);
iden.getName = sinon.stub().returns('name');
iden.deleteWallet = sinon.stub();
$rootScope.wallet = w;
$rootScope.iden = iden;
@ -561,20 +567,35 @@ describe("Unit: Controllers", function() {
});
describe('Profile Controller', function() {
var ctrl, scope;
var ctrl, inScope, modalCtrl;
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
ctrl = $controller('ProfileController', {
$scope: scope,
$modal: {},
$modal: {
open: function(opts) {
inScope = $rootScope.$new();
modalCtrl = opts.controller(inScope, {
close: sinon.stub(),
});
return {
result: {
then: sinon.stub(),
}
};
},
},
});
saveAsLastCall = null;
}));
it('Backup Wallet controller #download', function() {
var w = scope.wallet;
expect(saveAsLastCall).equal(null);
scope.downloadWalletBackup(w);
scope.showWalletInfo(w);
inScope.downloadWalletBackup();
expect(saveAsLastCall.blob.size).equal(7);
expect(saveAsLastCall.blob.type).equal('text/plain;charset=utf-8');
});
@ -582,7 +603,8 @@ describe("Unit: Controllers", function() {
it('Backup Wallet controller should name backup correctly for multiple copayers', function() {
var w = scope.wallet;
expect(saveAsLastCall).equal(null);
scope.downloadWalletBackup(w);
scope.showWalletInfo(w);
inScope.downloadWalletBackup();
expect(saveAsLastCall.filename).equal('nickname-fakeWallet-keybackup.json.aes');
});
@ -590,16 +612,19 @@ describe("Unit: Controllers", function() {
var w = scope.wallet;
expect(saveAsLastCall).equal(null);
scope.wallet.totalCopayers = 1;
scope.downloadWalletBackup(w);
scope.showWalletInfo(w);
inScope.downloadWalletBackup();
expect(saveAsLastCall.filename).equal('fakeWallet-keybackup.json.aes');
});
it('Delete a wallet', function() {
var w = scope.wallet;
scope.deleteWallet(w, function() {
scope.$digest();
expect(scope.wallet).equal(null);
});
scope.showWalletInfo(w);
inScope.deleteWallet();
scope.$digest();
scope.iden.deleteWallet.calledOnce.should.equal(true);
scope.iden.deleteWallet.getCall(0).args[0].should.equal(w.getId());
});
});

View file

@ -26,7 +26,7 @@
<div ng-include="'views/includes/version.html'"></div>
</div>
<div class="p10 box-setup bg-success m10b text-white" ng-show="confirmedEmail">
<div class="p10 bg-success m20b text-white" ng-show="confirmedEmail">
<div class="left">
<i class="size-36 fi-check m10r"></i>
</div>
@ -119,7 +119,7 @@
<div class="clipo">
<img src="img/clipo-signin.png" alt="clipo" width="310">
</div>
<div class="p10 m20b size-14 box-setup bg-success text-white" ng-show="anyWallet && !confirmedEmail && !error">
<div class="p10 m20b size-14 bg-success text-white" ng-show="anyWallet && !confirmedEmail && !error">
<div class="left">
<i class="size-36 fi-alert m10r"></i>
</div>
@ -127,7 +127,7 @@
You can import your current wallets after
<a class="text-white" ng-click="$root.go('/createProfile')">creating your profile</a>
</div>
<div class="p10 box-setup bg-success m20b text-white" ng-show="pendingPayment">
<div class="p10 bg-success m20b text-white" ng-show="pendingPayment">
<div class="left">
<i class="size-36 m10r"></i>
</div>

View file

@ -1,10 +0,0 @@
<div class="text-center">
<h1>Copy backup in a safe place</h1>
<textarea class="show-for-large-up" readonly rows="7">{{backupWalletPlainText}}</textarea>
<textarea class="hide-for-large-up" rows="12">{{backupWalletPlainText}}</textarea>
<div translate class="m10t size-12 text-gray text-right">
Copy this text as it is in a safe place (notepad or email)
</div>
</div>
<a class="close-reveal-modal" ng-click="cancel()">&#215;</a>

View file

@ -0,0 +1,58 @@
<div class="text-center" ng-init="isComplete = item.isComplete(); networkName = item.getNetworkName()">
<h1>{{item.name || item.id }}</h1>
<h3>
{{item.requiredCopayers}} of {{item.totalCopayers}} - {{networkName}}
, {{isComplete ? 'Complete' : 'Waiting for copayers...'}}
</h3>
<div class="m10b" ng-if="isComplete">
<b>{{item.balanceInfo.totalBalance || 0}} {{item.settings.unitName}}</b>
</div>
<div class="m20b">
Approximate size: {{item.kb}} kB
<span ng-if="item.usage">({{item.usage}}%) </span>
</div>
<div class="box-notification" ng-show="error">
<div class="box-icon error">
<i class="fi-x size-24"></i>
</div>
<span class="text-warning size-14">
{{error|translate}}
</span>
</div>
<div class="row" ng-show="!backupWalletPlainText && !error">
<div class="large-6 medium-6 small-6 columns">
<button class="primary tiny" ng-click="downloadWalletBackup()" ng-disabled="loading"
ng-show="!isSafari"><i class="fi-download"></i> Download backup</button>
<button class="primary tiny" ng-click="viewWalletBackup()" ng-disabled="loading"
ng-show="isSafari"><i class="fi-eye"></i> View Backup</button>
</div>
<div class="large-6 medium-6 small-6 columns">
<button class="warning tiny" ng-disabled="loading"
ng-really-message="{{'Are you sure you want to delete the wallet'}} {{(item.name || item.id)}}"
ng-really-click="deleteWallet()">
<span ng-show="!loading">
<i class="fi-trash"></i> Delete
</span>
<span ng-show="loading">
<i class="fi-bitcoin-circle icon-rotate spinner"></i> Deleting...
</span>
</button>
</div>
</div>
<div ng-show="backupWalletPlainText">
<h3>Copy backup in a safe place</h3>
<div class="input">
<textarea class="show-for-large-up" readonly rows="7">{{backupWalletPlainText}}</textarea>
<textarea class="hide-for-large-up" rows="12">{{backupWalletPlainText}}</textarea>
<i class="icon-compose"></i>
</div>
<div translate class="m10t size-12 text-gray text-right">
Copy this text as it is in a safe place (notepad or email)
</div>
</div>
<a class="close-reveal-modal" ng-click="close()">&#215;</a>
</div>

View file

@ -42,55 +42,55 @@
<div class="row" ng-init="setWallets()">
<div class="large-12 columns">
<h2>Manage wallets</h2>
<table>
<h2>Manage wallets</h2>
<div class="box-notification" ng-show="success">
<div class="box-icon success">
<i class="fi-check size-24"></i>
</div>
<span class="text-success size-14">
{{success|translate}}
</span>
<a href ng-click="success=false" class="close-notification success">&#215;</a>
</div>
<table class="manage-wallets">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th class="hide-for-small-only">Status</th>
<th>Balance</th>
<th>Approx Size</th>
<th class="text-right">Actions</th>
<th class="hide-for-small-only">Balance</th>
<th class="hide-for-small-only">Approx Size</th>
</tr>
</thead>
<tbody>
<tr
data-ng-repeat="item in wallets | orderBy:'name'"
ng-init="isComplete = item.isComplete();
networkName = item.getNetworkName()"
ng-class="{'deleting':loading==item.id}">
<td>{{item.name || item.id }}</td>
ng-init="isComplete = item.isComplete(); networkName = item.getNetworkName()"
ng-click="showWalletInfo(item)">
<td>
<span ng-show="loading == item.id"><i class="fi-bitcoin-circle icon-rotate spinner"></i></span>
<span ng-show="loading != item.id">
{{item.name || item.id }}
</span>
</td>
<td>{{item.requiredCopayers}} of {{item.totalCopayers}} - {{networkName}}</td>
<td class="hide-for-small-only">
{{isComplete ? 'Complete' : 'Waiting for copayers...'}}
</td>
<td>
<td class="hide-for-small-only">
<span ng-if="!isComplete">-</span>
<span ng-if="isComplete">
{{item.balanceInfo.totalBalance || 0}} {{item.settings.unitName}}
</span>
</td>
<td>
<td class="hide-for-small-only">
<span>
{{item.kb}} kB
<span ng-if="item.usage">({{item.usage}}%) </span>
</span>
</td>
<td class="text-right">
<div ng-show="loading != item.id">
<a title="Download Backup" class="text-gray" ng-click="downloadWalletBackup(item)"
ng-show="!isSafari"><i class="fi-download size-18 m10r"></i></a>
<a title="View Backup" ng-click="viewWalletBackup(item)"
ng-show="isSafari"><i class="fi-eye"></i></a>
&nbsp;
<a title="Delete Wallet" ng-really-message="{{'Are you sure you want to delete the wallet'}} {{(item.name || item.id)}}"
ng-really-click="deleteWallet(item)"
ng-show="loading != item.id"><i class="fi-trash text-gray size-18"></i></a>
</div>
<span ng-show="loading == item.id"><i class="fi-bitcoin-circle icon-rotate spinner"></i></span>
</td>
</tr>
</tbody>
</table>

1
w
View file

@ -1 +0,0 @@
xprv9s21ZrQH143K3DYCbPDJdFhNDCzChAdsFwz2HFwgtcEufYffAvhZXcJ3XwxjepPJp3FBSHYFnPsxtAzDbSruoRW3rVThyqc2iFKSFw7zhE6