Added toast config and messages when saving from vault. Persist scroll position when returning to vault from other pages.

This commit is contained in:
Kyle Spearrin 2016-09-12 19:48:36 -04:00
parent ba5286c438
commit 5f39939d65
17 changed files with 154 additions and 30 deletions

View File

@ -81,16 +81,20 @@ gulp.task('lib', ['clean:lib'], function () {
src: paths.npmDir + 'angular-ui-router/release/*.js', src: paths.npmDir + 'angular-ui-router/release/*.js',
dest: paths.libDir + 'angular-ui-router' dest: paths.libDir + 'angular-ui-router'
}, },
{
src: [paths.npmDir + 'angular-toastr/dist/angular-toastr.tpls.js', paths.npmDir + 'angular-toastr/dist/angular-toastr.css'],
dest: paths.libDir + 'angular-toastr'
},
{ {
src: [paths.npmDir + 'sjcl/core/cbc.js', paths.npmDir + 'sjcl/core/bitArray.js', paths.npmDir + 'sjcl/sjcl.js'], src: [paths.npmDir + 'sjcl/core/cbc.js', paths.npmDir + 'sjcl/core/bitArray.js', paths.npmDir + 'sjcl/sjcl.js'],
dest: paths.libDir + 'sjcl' dest: paths.libDir + 'sjcl'
}, },
{ {
src: paths.npmDir + 'ngclipboard/dist/ngclipboard*.js', src: paths.npmDir + 'ngclipboard/dist/ngclipboard.js',
dest: paths.libDir + 'ngclipboard' dest: paths.libDir + 'ngclipboard'
}, },
{ {
src: paths.npmDir + 'clipboard/dist/clipboard*.js', src: paths.npmDir + 'clipboard/dist/clipboard.js',
dest: paths.libDir + 'clipboard' dest: paths.libDir + 'clipboard'
} }
]; ];

View File

@ -22,6 +22,7 @@
"run-sequence": "1.2.2", "run-sequence": "1.2.2",
"ngclipboard": "1.1.1", "ngclipboard": "1.1.1",
"clipboard": "1.5.12", "clipboard": "1.5.12",
"merge-stream": "1.0.0" "merge-stream": "1.0.0",
"angular-toastr": "2.1.1"
} }
} }

View File

@ -3,6 +3,7 @@
'ui.router', 'ui.router',
'angular-jwt', 'angular-jwt',
'ngAnimate', 'ngAnimate',
'toastr',
'bit.directives', 'bit.directives',
'bit.services', 'bit.services',

View File

@ -1,7 +1,7 @@
angular angular
.module('bit') .module('bit')
.config(function ($stateProvider, $urlRouterProvider, $httpProvider, jwtInterceptorProvider) { .config(function ($stateProvider, $urlRouterProvider, $httpProvider, jwtInterceptorProvider, toastrConfig) {
jwtInterceptorProvider.urlParam = 'access_token'; jwtInterceptorProvider.urlParam = 'access_token';
jwtInterceptorProvider.tokenGetter = /*@ngInject*/ function (config, appSettings, tokenService) { jwtInterceptorProvider.tokenGetter = /*@ngInject*/ function (config, appSettings, tokenService) {
if (config.url.indexOf(appSettings.apiUri) === 0) { if (config.url.indexOf(appSettings.apiUri) === 0) {
@ -11,6 +11,13 @@
} }
}; };
angular.extend(toastrConfig, {
closeButton: true,
progressBar: true,
showMethod: 'slideDown',
positionClass: 'toast-bottom-center'
});
if ($httpProvider.defaults.headers.post) { if ($httpProvider.defaults.headers.post) {
$httpProvider.defaults.headers.post = {}; $httpProvider.defaults.headers.post = {};
} }
@ -56,7 +63,8 @@
.state('tabs.vault', { .state('tabs.vault', {
url: "/vault", url: "/vault",
templateUrl: "app/vault/views/vault.html", templateUrl: "app/vault/views/vault.html",
controller: 'vaultController' controller: 'vaultController',
params: { scrollY: 0 }
}) })
.state('tabs.settings', { .state('tabs.settings', {
url: "/settings", url: "/settings",
@ -74,21 +82,21 @@
templateUrl: "app/vault/views/vaultViewSite.html", templateUrl: "app/vault/views/vaultViewSite.html",
controller: 'vaultViewSiteController', controller: 'vaultViewSiteController',
data: { authorize: true }, data: { authorize: true },
params: { animation: null } params: { animation: null, returnScrollY: 0 }
}) })
.state('addSite', { .state('addSite', {
url: "/add-site", url: "/add-site",
templateUrl: "app/vault/views/vaultAddSite.html", templateUrl: "app/vault/views/vaultAddSite.html",
controller: 'vaultAddSiteController', controller: 'vaultAddSiteController',
data: { authorize: true }, data: { authorize: true },
params: { animation: null } params: { animation: null, returnScrollY: 0 }
}) })
.state('editSite', { .state('editSite', {
url: "/edit-site?siteId", url: "/edit-site?siteId",
templateUrl: "app/vault/views/vaultEditSite.html", templateUrl: "app/vault/views/vaultEditSite.html",
controller: 'vaultEditSiteController', controller: 'vaultEditSiteController',
data: { authorize: true }, data: { authorize: true },
params: { animation: null, fromView: true } params: { animation: null, fromView: true, returnScrollY: 0 }
}); });
}) })
.run(function ($rootScope, userService, loginService, tokenService, $state) { .run(function ($rootScope, userService, loginService, tokenService, $state) {

View File

@ -1,7 +1,9 @@
angular angular
.module('bit.vault') .module('bit.vault')
.controller('vaultAddSiteController', function ($scope, $state, siteService, folderService, cipherService, $q) { .controller('vaultAddSiteController', function ($scope, $state, $stateParams, siteService, folderService, cipherService, $q) {
var returnScrollY = $stateParams.returnScrollY;
$scope.site = { $scope.site = {
folderId: null folderId: null
}; };
@ -39,14 +41,14 @@
var site = new Site(siteModel, true); var site = new Site(siteModel, true);
return site; return site;
}).then(function (site) { }).then(function (site) {
return saveSite(site, function (site) { return saveSite(site).then(function (site) {
alert('Saved ' + site.id + '!'); toastr.success('Added site');
}); });
}); });
}; };
$scope.close = function () { $scope.close = function () {
$state.go('tabs.vault', { animation: 'out-slide-down' }); $state.go('tabs.vault', { animation: 'out-slide-down', scrollY: returnScrollY || 0 });
}; };
function saveSite(site) { function saveSite(site) {

View File

@ -1,10 +1,10 @@
angular angular
.module('bit.vault') .module('bit.vault')
.controller('vaultController', function ($scope, $rootScope, siteService, folderService, $q, cipherService) { .controller('vaultController', function ($scope, $rootScope, siteService, folderService, $q, cipherService, $state, $stateParams) {
var delayLoad = true; var delayLoad = true;
if (!$rootScope.vaultSites) { if (!$rootScope.vaultSites) {
$rootScope.vaultSites =[]; $rootScope.vaultSites = [];
delayLoad = false; delayLoad = false;
} }
if (!$rootScope.vaultFolders) { if (!$rootScope.vaultFolders) {
@ -13,6 +13,7 @@
} }
if (delayLoad) { if (delayLoad) {
setTimeout(setScrollY, 100);
setTimeout(loadVault, 1000); setTimeout(loadVault, 1000);
} }
else { else {
@ -64,6 +65,9 @@
$q.all(promises).then(function () { $q.all(promises).then(function () {
$rootScope.vaultSites = decSites; $rootScope.vaultSites = decSites;
$rootScope.vaultFolders = decFolders; $rootScope.vaultFolders = decFolders;
if (!delayLoad) {
setScrollY();
}
}); });
}); });
}); });
@ -76,4 +80,31 @@
return item.name.toLowerCase(); return item.name.toLowerCase();
}; };
$scope.addSite = function () {
$state.go('addSite', {
animation: 'in-slide-up',
returnScrollY: getScrollY()
});
};
$scope.viewSite = function (site) {
$state.go('viewSite', {
siteId: site.id,
animation: 'in-slide-up',
returnScrollY: getScrollY()
});
};
function getScrollY() {
var content = document.getElementsByClassName('content')[0];
return content.scrollTop;
};
function setScrollY() {
if ($stateParams.scrollY) {
var content = document.getElementsByClassName('content')[0];
content.scrollTop = $stateParams.scrollY;
}
};
}); });

View File

@ -1,7 +1,9 @@
angular angular
.module('bit.vault') .module('bit.vault')
.controller('vaultEditSiteController', function ($scope, $state, $stateParams, siteService, folderService, cipherService, $q) { .controller('vaultEditSiteController', function ($scope, $state, $stateParams, siteService, folderService, cipherService, $q, toastr) {
var returnScrollY = $stateParams.returnScrollY;
$scope.site = { $scope.site = {
folderId: null folderId: null
}; };
@ -44,8 +46,8 @@
var site = new Site(siteModel, true); var site = new Site(siteModel, true);
return site; return site;
}).then(function (site) { }).then(function (site) {
return saveSite(site, function (site) { return saveSite(site).then(function (site) {
alert('Saved ' + site.id + '!'); toastr.success('Edited site');
}); });
}); });
}; };
@ -55,7 +57,7 @@
$state.go('viewSite', { siteId: $stateParams.siteId, animation: 'out-slide-down' }); $state.go('viewSite', { siteId: $stateParams.siteId, animation: 'out-slide-down' });
} }
else { else {
$state.go('tabs.vault', { animation: 'out-slide-down' }); $state.go('tabs.vault', { animation: 'out-slide-down', scrollY: returnScrollY || 0 });
} }
}; };

View File

@ -1,2 +1,2 @@
angular angular
.module('bit.vault', ['ngAnimate']); .module('bit.vault', ['ngAnimate', 'toastr']);

View File

@ -1,7 +1,9 @@
angular angular
.module('bit.vault') .module('bit.vault')
.controller('vaultViewSiteController', function ($scope, $stateParams, siteService, cipherService) { .controller('vaultViewSiteController', function ($scope, $state, $stateParams, siteService, cipherService) {
var returnScrollY = $stateParams.returnScrollY;
$scope.site = null; $scope.site = null;
siteService.get($stateParams.siteId, function (site) { siteService.get($stateParams.siteId, function (site) {
cipherService.decryptSite(site).then(function (model) { cipherService.decryptSite(site).then(function (model) {
@ -17,4 +19,8 @@
} }
}); });
}); });
$scope.close = function () {
$state.go('tabs.vault', { animation: 'out-slide-down', scrollY: returnScrollY || 0 });
};
}); });

View File

@ -1,6 +1,6 @@
<div class="header"> <div class="header">
<div class="right"> <div class="right">
<a ui-sref="addSite({animation: 'in-slide-up'})"><i class="fa fa-plus fa-lg"></i></a> <a href="" ng-click="addSite()"><i class="fa fa-plus fa-lg"></i></a>
</div> </div>
<div class="title">My Vault</div> <div class="title">My Vault</div>
</div> </div>
@ -10,7 +10,7 @@
<div class="list-grouped-header"> <div class="list-grouped-header">
<i class="fa fa-folder-open"></i> {{folder.name}} <i class="fa fa-folder-open"></i> {{folder.name}}
</div> </div>
<a ui-sref="viewSite({siteId: site.id, animation: 'in-slide-up'})" class="list-grouped-item condensed" ng-repeat="site in folderSites = (vaultSites | filter: { folderId: folder.id } | orderBy: ['name', 'username'])"> <a href="" ng-click="viewSite(site)" class="list-grouped-item condensed" ng-repeat="site in folderSites = (vaultSites | filter: { folderId: folder.id } | orderBy: ['name', 'username'])">
<span class="btn-list"><i class="fa fa-lg fa-ellipsis-h text-muted"></i></span> <span class="btn-list"><i class="fa fa-lg fa-ellipsis-h text-muted"></i></span>
<span class="text">{{site.name}}</span> <span class="text">{{site.name}}</span>
<span class="detail">{{site.username}}</span> <span class="detail">{{site.username}}</span>

View File

@ -1,6 +1,6 @@
<div class="header"> <div class="header">
<div class="left"> <div class="left">
<a href="" ui-sref="tabs.vault({animation: 'out-slide-down'})">Close</a> <a href="" ng-click="close()">Close</a>
</div> </div>
<div class="right"> <div class="right">
<a href="" ui-sref="editSite({animation: 'in-slide-up', siteId: site.id, fromView: true})">Edit</a> <a href="" ui-sref="editSite({animation: 'in-slide-up', siteId: site.id, fromView: true})">Edit</a>

View File

@ -7,6 +7,7 @@
<title>bitwarden</title> <title>bitwarden</title>
<link rel="stylesheet" href="../lib/font-awesome/css/font-awesome.css"> <link rel="stylesheet" href="../lib/font-awesome/css/font-awesome.css">
<link rel="stylesheet" href="../lib/angular-toastr/angular-toastr.css">
<link rel="stylesheet" href="css/popup.css"> <link rel="stylesheet" href="css/popup.css">
<script src="../lib/jquery/jquery.js"></script> <script src="../lib/jquery/jquery.js"></script>
@ -17,6 +18,7 @@
<script src="../lib/angular-animate/angular-animate.js"></script> <script src="../lib/angular-animate/angular-animate.js"></script>
<script src="../lib/angular-ui-router/angular-ui-router.js"></script> <script src="../lib/angular-ui-router/angular-ui-router.js"></script>
<script src="../lib/angular-jwt/angular-jwt.js"></script> <script src="../lib/angular-jwt/angular-jwt.js"></script>
<script src="../lib/angular-toastr/angular-toastr.tpls.js"></script>
<script src="app/app.js"></script> <script src="app/app.js"></script>
<script src="app/settings.js"></script> <script src="app/settings.js"></script>

View File

@ -1,4 +1,6 @@
.in-slide-up { @import (reference) "variables.less";
.in-slide-up {
.main-view.ng-enter, .main-view.ng-enter,
.main-view.ng-leave { .main-view.ng-leave {
-webkit-transition: all 0.4s ease; -webkit-transition: all 0.4s ease;

View File

@ -1,6 +1,4 @@
@import (reference) "../../../node_modules/bootstrap/less/mixins.less"; @import (reference) "variables.less";
@import (reference) "../../../node_modules/bootstrap/less/variables.less";
@import (reference) "variables.less";
.header { .header {
min-height: 44px; min-height: 44px;

View File

@ -0,0 +1,60 @@
@import (reference) "variables.less";
/* Toastr */
#toast-container {
&.toast-bottom-center .toast {
width: 100%;
margin-bottom: 0;
margin-top: 6px;
}
.toast {
background-image: none !important;
border-radius: 0;
.box-shadow(0 0 8px rgba(0, 0, 0, 0.5));
&.toast-danger, &.toast-error {
background-image: none !important;
background-color: @brand-danger;
&:before {
content: "\f0e7";
}
}
&.toast-warning {
background-image: none !important;
background-color: @brand-warning;
&:before {
content: "\f071";
}
}
&.toast-info {
background-image: none !important;
background-color: @brand-info;
&:before {
content: "\f005";
}
}
&.toast-success {
background-image: none !important;
background-color: @brand-success;
&:before {
content: "\f00C";
}
}
&:before {
position: fixed;
font-family: FontAwesome;
font-size: 24px;
line-height: 24px;
float: left;
color: #ffffff;
padding-right: 0.5em;
margin: auto 0.5em auto -1.5em;
}
}
}

View File

@ -2,6 +2,7 @@
@import "variables.less"; @import "variables.less";
@import "components.less"; @import "components.less";
@import "animations.less"; @import "animations.less";
@import "plugins.less";
body { body {
width: 320px; width: 320px;

View File

@ -1,10 +1,16 @@
@import (reference) "../../../node_modules/bootstrap/less/mixins.less"; @import (reference) "../../../node_modules/bootstrap/less/bootstrap.less";
@import (reference) "../../../node_modules/bootstrap/less/mixins.less";
@import (reference) "../../../node_modules/bootstrap/less/variables.less"; @import (reference) "../../../node_modules/bootstrap/less/variables.less";
@font-family-sans-serif: "Open Sans", sans-serif; @font-family-sans-serif: "Open Sans", sans-serif;
@text-color: #000000; @text-color: #000000;
@brand-primary: #3c8dbc;
@background-color: #efeff4; @background-color: #efeff4;
@border-color: #f0f0f0; @border-color: #f0f0f0;
@border-color-dark: #ddd; @border-color-dark: #ddd;
@list-item-hover: #fbfbfb; @list-item-hover: #fbfbfb;
@brand-primary: #3c8dbc;
@brand-danger: #dd4b39;
@brand-success: #00a65a;
@brand-info: #00c0ef;
@brand-warning: #f39c12;