mirror of
https://github.com/moparisthebest/mail
synced 2024-11-26 02:42:17 -05:00
implement popover
remove lightbox on wrong passphrase input show key fingerprint in popup
This commit is contained in:
parent
91b9de2998
commit
6f1c59f1fe
@ -207,9 +207,11 @@ define(function(require) {
|
||||
pubkeyDao = new PublicKeyDAO(restDao);
|
||||
lawnchairDao = new LawnchairDAO();
|
||||
keychain = new KeychainDAO(lawnchairDao, pubkeyDao);
|
||||
self._keychain = keychain;
|
||||
imapClient = new ImapClient(imapOptions);
|
||||
smtpClient = new SmtpClient(smtpOptions);
|
||||
pgp = new PGP();
|
||||
self._crypto = pgp;
|
||||
userStorage = new DeviceStorageDAO(lawnchairDao);
|
||||
self._emailDao = new EmailDAO(keychain, imapClient, smtpClient, pgp, userStorage);
|
||||
|
||||
|
@ -4,6 +4,7 @@ window.name = 'NG_DEFER_BOOTSTRAP!';
|
||||
require([
|
||||
'angular',
|
||||
'js/controller/dialog',
|
||||
'js/controller/popover',
|
||||
'js/controller/account',
|
||||
'js/controller/login',
|
||||
'js/controller/login-initial',
|
||||
@ -16,14 +17,14 @@ require([
|
||||
'cryptoLib/util',
|
||||
'angularRoute',
|
||||
'angularTouch'
|
||||
], function(angular, DialogCtrl, AccountCtrl, LoginCtrl, LoginInitialCtrl, LoginNewDeviceCtrl, LoginExistingCtrl, MailListCtrl, ReadCtrl, WriteCtrl, NavigationCtrl, util) {
|
||||
], function(angular, DialogCtrl, PopoverCtrl, AccountCtrl, LoginCtrl, LoginInitialCtrl, LoginNewDeviceCtrl, LoginExistingCtrl, MailListCtrl, ReadCtrl, WriteCtrl, NavigationCtrl, util) {
|
||||
'use strict';
|
||||
|
||||
// reset window.name
|
||||
window.name = util.UUID();
|
||||
|
||||
// init main angular module including dependencies
|
||||
var app = angular.module('mail', ['ngRoute', 'ngTouch', 'navigation', 'mail-list', 'write', 'read', 'login-new-device']);
|
||||
var app = angular.module('mail', ['ngRoute', 'ngTouch', 'navigation', 'mail-list', 'write', 'read', 'login-new-device', 'popover']);
|
||||
|
||||
// set router paths
|
||||
app.config(function($routeProvider) {
|
||||
@ -58,6 +59,7 @@ require([
|
||||
app.controller('MailListCtrl', MailListCtrl);
|
||||
app.controller('AccountCtrl', AccountCtrl);
|
||||
app.controller('DialogCtrl', DialogCtrl);
|
||||
app.controller('PopoverCtrl', PopoverCtrl);
|
||||
|
||||
// manually bootstrap angular due to require.js
|
||||
angular.element().ready(function() {
|
||||
|
@ -43,7 +43,9 @@ define(function(require) {
|
||||
|
||||
function onUnlock(err) {
|
||||
if (err) {
|
||||
handleError(err);
|
||||
$scope.incorrect = true;
|
||||
$scope.buttonEnabled = true;
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
||||
|
49
src/js/controller/popover.js
Normal file
49
src/js/controller/popover.js
Normal file
@ -0,0 +1,49 @@
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var angular = require('angular');
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var PopoverCtrl = function($scope) {
|
||||
$scope.state.popover = {};
|
||||
};
|
||||
|
||||
//
|
||||
// Directives
|
||||
//
|
||||
|
||||
var ngModule = angular.module('popover', []);
|
||||
ngModule.directive('popover', function($parse) {
|
||||
return function(scope, elm, attrs) {
|
||||
var popover = angular.element(document.querySelector('.popover'));
|
||||
|
||||
elm.on('mouseover', function() {
|
||||
var model = $parse(attrs.popover);
|
||||
scope.$watch(model, function(value) {
|
||||
// set popover title and content
|
||||
scope.state.popover.title = attrs.popoverTitle;
|
||||
scope.state.popover.content = value;
|
||||
|
||||
// set popover position
|
||||
var top = elm[0].offsetTop;
|
||||
var left = elm[0].offsetLeft;
|
||||
var width = elm[0].offsetWidth;
|
||||
var height = elm[0].offsetHeight;
|
||||
|
||||
popover[0].style.top = (top + height / 2 - popover[0].offsetHeight / 2) + 'px';
|
||||
popover[0].style.left = (left + width) + 'px';
|
||||
popover[0].style.opacity = '1';
|
||||
});
|
||||
});
|
||||
|
||||
elm.on('mouseout', function() {
|
||||
popover[0].style.opacity = '0';
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
return PopoverCtrl;
|
||||
});
|
@ -1,13 +1,18 @@
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var angular = require('angular');
|
||||
var appController = require('js/app-controller'),
|
||||
angular = require('angular'),
|
||||
crypto, keychain;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var ReadCtrl = function($scope) {
|
||||
crypto = appController._crypto;
|
||||
keychain = appController._keychain;
|
||||
|
||||
$scope.state.read = {
|
||||
open: false,
|
||||
toggle: function(to) {
|
||||
@ -18,6 +23,16 @@ define(function(require) {
|
||||
$scope.lineEmpty = function(line) {
|
||||
return line.replace(/>/g, '').trim().length === 0;
|
||||
};
|
||||
|
||||
$scope.getFingerprint = function(address) {
|
||||
keychain.getReceiverPublicKey(address, function(err, pubkey) {
|
||||
var fpr = crypto.getFingerprint(pubkey.publicKey);
|
||||
var formatted = fpr.slice(0, 4) + ' ' + fpr.slice(4, 8) + ' ' + fpr.slice(8, 12) + ' ' + fpr.slice(12, 16) + ' ' + fpr.slice(16, 20) + ' ' + fpr.slice(20, 24) + ' ' + fpr.slice(24, 28) + ' ' + fpr.slice(28, 32) + ' ' + fpr.slice(32, 36) + ' ' + fpr.slice(36);
|
||||
|
||||
$scope.fingerprint = formatted;
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
|
@ -46,9 +46,15 @@ define(function(require) {
|
||||
/**
|
||||
* Show a user's fingerprint
|
||||
*/
|
||||
PGP.prototype.getFingerprint = function() {
|
||||
PGP.prototype.getFingerprint = function(publicKeyArmored) {
|
||||
var publicKey, privateKey;
|
||||
|
||||
if (publicKeyArmored) {
|
||||
// parse the optional public key parameter
|
||||
publicKey = openpgp.read_publicKey(publicKeyArmored)[0];
|
||||
return util.hexstrdump(publicKey.getFingerprint()).toUpperCase();
|
||||
}
|
||||
|
||||
privateKey = openpgp.keyring.exportPrivateKey(0);
|
||||
if (privateKey && privateKey.keyId) {
|
||||
publicKey = openpgp.keyring.getPublicKeysForKeyId(privateKey.keyId)[0];
|
||||
|
@ -17,6 +17,7 @@
|
||||
@import "components/nav";
|
||||
@import "components/mail-list";
|
||||
@import "components/layout";
|
||||
@import "components/popover";
|
||||
|
||||
// Views
|
||||
@import "views/shared";
|
||||
|
125
src/sass/components/_popover.scss
Normal file
125
src/sass/components/_popover.scss
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2013 Twitter, Inc under the Apache 2.0 license.
|
||||
* From Bootstrap popover https://github.com/twbs/bootstrap
|
||||
*/
|
||||
.popover {
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
left: -9999px;
|
||||
display: block;
|
||||
z-index: 1010;
|
||||
max-width: 276px;
|
||||
padding: 1px;
|
||||
text-align: left;
|
||||
background-color: #ffffff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #cccccc;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
white-space: normal;
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s;
|
||||
|
||||
&.top {
|
||||
margin-top: -10px;
|
||||
}
|
||||
&.right {
|
||||
margin-left: 10px;
|
||||
}
|
||||
&.bottom {
|
||||
margin-top: 10px;
|
||||
}
|
||||
&.left {
|
||||
margin-left: -10px;
|
||||
}
|
||||
.popover-title {
|
||||
margin: 0;
|
||||
padding: 8px 14px;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
line-height: 18px;
|
||||
background-color: #f7f7f7;
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
.popover-content {
|
||||
padding: 9px 14px;
|
||||
}
|
||||
.arrow,
|
||||
.arrow:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
.arrow {
|
||||
border-width: 11px;
|
||||
}
|
||||
.arrow:after {
|
||||
border-width: 10px;
|
||||
content: "";
|
||||
}
|
||||
&.top .arrow {
|
||||
left: 50%;
|
||||
margin-left: -11px;
|
||||
border-bottom-width: 0;
|
||||
border-top-color: #999999;
|
||||
border-top-color: rgba(0, 0, 0, 0.25);
|
||||
bottom: -11px;
|
||||
}
|
||||
&.top .arrow:after {
|
||||
content: " ";
|
||||
bottom: 1px;
|
||||
margin-left: -10px;
|
||||
border-bottom-width: 0;
|
||||
border-top-color: #ffffff;
|
||||
}
|
||||
&.right .arrow {
|
||||
top: 50%;
|
||||
left: -11px;
|
||||
margin-top: -11px;
|
||||
border-left-width: 0;
|
||||
border-right-color: #999999;
|
||||
border-right-color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
&.right .arrow:after {
|
||||
content: " ";
|
||||
left: 1px;
|
||||
bottom: -10px;
|
||||
border-left-width: 0;
|
||||
border-right-color: #ffffff;
|
||||
}
|
||||
&.bottom .arrow {
|
||||
left: 50%;
|
||||
margin-left: -11px;
|
||||
border-top-width: 0;
|
||||
border-bottom-color: #999999;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.25);
|
||||
top: -11px;
|
||||
}
|
||||
&.bottom .arrow:after {
|
||||
content: " ";
|
||||
top: 1px;
|
||||
margin-left: -10px;
|
||||
border-top-width: 0;
|
||||
border-bottom-color: #ffffff;
|
||||
}
|
||||
&.left .arrow {
|
||||
top: 50%;
|
||||
right: -11px;
|
||||
margin-top: -11px;
|
||||
border-right-width: 0;
|
||||
border-left-color: #999999;
|
||||
border-left-color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
&.left .arrow:after {
|
||||
content: " ";
|
||||
right: 1px;
|
||||
border-right-width: 0;
|
||||
border-left-color: #ffffff;
|
||||
bottom: -10px;
|
||||
}
|
||||
}
|
@ -27,4 +27,13 @@
|
||||
</div>
|
||||
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
|
||||
<div class="lightbox lightbox-effect view-dialog" ng-include="'tpl/dialog.html'"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- popover -->
|
||||
<div class="popover right" ng-controller="PopoverCtrl">
|
||||
<div class="arrow"></div>
|
||||
<div ng-switch="state.popover.title !== undefined">
|
||||
<div ng-switch-when="true" class="popover-title"><b>{{state.popover.title}}</b></div>
|
||||
</div>
|
||||
<div class="popover-content">{{state.popover.content}}</div>
|
||||
</div><!--/.popover-->
|
@ -9,14 +9,14 @@
|
||||
<p class="subject" ng-click="state.read.toggle(false)">{{state.mailList.selected.subject || 'No subject'}}</p>
|
||||
<p class="date">{{state.mailList.selected.sentDate | date:'EEEE, MMM d, yyyy h:mm a'}}</p>
|
||||
<p class="address">
|
||||
From: <span class="label" data-icon-append="" ng-repeat="f in state.mailList.selected.from">{{f.name || f.address}}</span>
|
||||
From: <span class="label" data-icon-append="" ng-repeat="f in state.mailList.selected.from" ng-mouseover="getFingerprint(f.address)" popover="fingerprint" popover-title="Fingerprint">{{f.name || f.address}}</span>
|
||||
</p>
|
||||
<p class="address">
|
||||
To: <span class="label" data-icon-append="" ng-repeat="t in state.mailList.selected.to">{{t.address}}</span>
|
||||
To: <span class="label" data-icon-append="" ng-repeat="t in state.mailList.selected.to" ng-mouseover="getFingerprint(t.address)" popover="fingerprint" popover-title="Fingerprint">{{t.address}}</span>
|
||||
</p>
|
||||
<div ng-switch="state.mailList.selected.cc !== undefined">
|
||||
<p class="address" ng-switch-when="true">
|
||||
CC: <span class="label" ng-repeat="t in state.mailList.selected.cc">{{t.address}}</span>
|
||||
CC: <span class="label" ng-repeat="t in state.mailList.selected.cc" ng-mouseover="getFingerprint(t.address)" popover="fingerprint" popover-title="Fingerprint">{{t.address}}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div><!--/.headers-->
|
||||
|
@ -140,10 +140,15 @@ define(function(require) {
|
||||
});
|
||||
|
||||
describe('Get Fingerprint', function() {
|
||||
it('should work', function() {
|
||||
it('should work without param', function() {
|
||||
var fingerprint = pgp.getFingerprint();
|
||||
expect(fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
|
||||
});
|
||||
|
||||
it('should work with param', function() {
|
||||
var fingerprint = pgp.getFingerprint(pubkey);
|
||||
expect(fingerprint).to.equal('5856CEF789C3A307E8A1B976F6F60E9B42CDFF4C');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Encrypt', function() {
|
||||
|
Loading…
Reference in New Issue
Block a user