1
0
mirror of https://github.com/moparisthebest/mail synced 2024-12-22 15:28:49 -05:00

implement popover

remove lightbox on wrong passphrase input

show key fingerprint in popup
This commit is contained in:
Tankred Hase 2013-12-03 13:15:10 +01:00
parent 91b9de2998
commit 6f1c59f1fe
11 changed files with 226 additions and 10 deletions

View File

@ -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);

View File

@ -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() {

View File

@ -43,7 +43,9 @@ define(function(require) {
function onUnlock(err) {
if (err) {
handleError(err);
$scope.incorrect = true;
$scope.buttonEnabled = true;
$scope.$apply();
return;
}

View 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;
});

View File

@ -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();
});
};
};
//

View File

@ -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];

View File

@ -17,6 +17,7 @@
@import "components/nav";
@import "components/mail-list";
@import "components/layout";
@import "components/popover";
// Views
@import "views/shared";

View 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;
}
}

View File

@ -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-->

View File

@ -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="&#xe009;" ng-repeat="f in state.mailList.selected.from">{{f.name || f.address}}</span>
From: <span class="label" data-icon-append="&#xe009;" 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="&#xe009;" ng-repeat="t in state.mailList.selected.to">{{t.address}}</span>
To: <span class="label" data-icon-append="&#xe009;" 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-->

View File

@ -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() {