mirror of
https://github.com/moparisthebest/mail
synced 2024-11-26 19:02:20 -05:00
[WO-281] implement reply all and forward
This commit is contained in:
parent
f3c2180dfb
commit
302fc378fb
@ -87,7 +87,7 @@ define(function(require) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.onConnect = function(callback) {
|
self.onConnect = function(callback) {
|
||||||
if (!self.isOnline() || !self._emailDao._account) {
|
if (!self.isOnline() || !self._emailDao || !self._emailDao._account) {
|
||||||
// prevent connection infinite loop
|
// prevent connection infinite loop
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
|
@ -285,6 +285,9 @@ define(function(require) {
|
|||||||
this.to = [{
|
this.to = [{
|
||||||
address: 'max.musterman@gmail.com'
|
address: 'max.musterman@gmail.com'
|
||||||
}]; // list of receivers
|
}]; // list of receivers
|
||||||
|
this.cc = [{
|
||||||
|
address: 'john.doe@gmail.com'
|
||||||
|
}]; // list of receivers
|
||||||
if (attachments) {
|
if (attachments) {
|
||||||
// body structure with three attachments
|
// body structure with three attachments
|
||||||
this.bodystructure = {
|
this.bodystructure = {
|
||||||
|
@ -164,6 +164,45 @@ define(function(require) {
|
|||||||
|
|
||||||
var ngModule = angular.module('read', []);
|
var ngModule = angular.module('read', []);
|
||||||
|
|
||||||
|
ngModule.directive('replySelection', function() {
|
||||||
|
return function(scope, elm) {
|
||||||
|
var popover = angular.element(document.querySelector('.reply-selection')),
|
||||||
|
visible = false;
|
||||||
|
|
||||||
|
elm.on('touchstart click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
visible = true;
|
||||||
|
|
||||||
|
// 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.transition = 'opacity 0.1s linear';
|
||||||
|
popover[0].style.top = (top + height) + 'px';
|
||||||
|
popover[0].style.left = (left + width / 2 - popover[0].offsetWidth / 2) + 'px';
|
||||||
|
popover[0].style.opacity = '1';
|
||||||
|
});
|
||||||
|
|
||||||
|
elm.parent().parent().on('touchstart click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (!visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
popover[0].style.transition = 'opacity 0.25s linear, top 0.25s step-end, left 0.25s step-end';
|
||||||
|
popover[0].style.opacity = '0';
|
||||||
|
popover[0].style.top = '-9999px';
|
||||||
|
popover[0].style.left = '-9999px';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
ngModule.directive('frameLoad', function() {
|
ngModule.directive('frameLoad', function() {
|
||||||
return function(scope, elm) {
|
return function(scope, elm) {
|
||||||
elm.bind('load', function() {
|
elm.bind('load', function() {
|
||||||
|
@ -26,14 +26,14 @@ define(function(require) {
|
|||||||
|
|
||||||
$scope.state.writer = {
|
$scope.state.writer = {
|
||||||
open: false,
|
open: false,
|
||||||
write: function(replyTo) {
|
write: function(replyTo, replyAll, forward) {
|
||||||
this.open = true;
|
this.open = true;
|
||||||
$scope.replyTo = replyTo;
|
$scope.replyTo = replyTo;
|
||||||
|
|
||||||
resetFields();
|
resetFields();
|
||||||
|
|
||||||
// fill fields depending on replyTo
|
// fill fields depending on replyTo
|
||||||
fillFields(replyTo);
|
fillFields(replyTo, replyAll, forward);
|
||||||
$scope.updatePreview();
|
$scope.updatePreview();
|
||||||
|
|
||||||
$scope.verify($scope.to[0]);
|
$scope.verify($scope.to[0]);
|
||||||
@ -60,24 +60,67 @@ define(function(require) {
|
|||||||
$scope.attachments = [];
|
$scope.attachments = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillFields(re) {
|
function fillFields(re, replyAll, forward) {
|
||||||
var from, body;
|
var from, sentDate, body;
|
||||||
|
|
||||||
if (!re) {
|
if (!re) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.writerTitle = 'Reply';
|
$scope.writerTitle = (forward) ? 'Forward' : 'Reply';
|
||||||
|
|
||||||
// fill recipient field
|
// fill recipient field
|
||||||
|
if (!forward) {
|
||||||
$scope.to.unshift({
|
$scope.to.unshift({
|
||||||
address: re.from[0].address
|
address: re.from[0].address
|
||||||
});
|
});
|
||||||
|
$scope.to.forEach($scope.verify);
|
||||||
|
}
|
||||||
|
if (replyAll) {
|
||||||
|
re.to.concat(re.cc).forEach(function(recipient) {
|
||||||
|
if (recipient.address === emailDao._account.emailAddress) {
|
||||||
|
// don't reply to yourself
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$scope.cc.unshift({
|
||||||
|
address: recipient.address
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$scope.cc.forEach($scope.verify);
|
||||||
|
}
|
||||||
|
|
||||||
// fill subject
|
// fill subject
|
||||||
|
if (forward) {
|
||||||
|
$scope.subject = 'Fwd: ' + re.subject;
|
||||||
|
} else {
|
||||||
$scope.subject = 'Re: ' + ((re.subject) ? re.subject.replace('Re: ', '') : '');
|
$scope.subject = 'Re: ' + ((re.subject) ? re.subject.replace('Re: ', '') : '');
|
||||||
|
}
|
||||||
|
|
||||||
// fill text body
|
// fill text body
|
||||||
from = re.from[0].name || re.from[0].address;
|
from = re.from[0].name || re.from[0].address;
|
||||||
body = '\n\n' + $filter('date')(re.sentDate, 'EEEE, MMM d, yyyy h:mm a') + ' ' + from + ' wrote:\n> ';
|
sentDate = $filter('date')(re.sentDate, 'EEEE, MMM d, yyyy h:mm a');
|
||||||
|
|
||||||
|
function createString(array) {
|
||||||
|
var str = '';
|
||||||
|
array.forEach(function(to) {
|
||||||
|
str += (str) ? ', ' : '';
|
||||||
|
str += ((to.name) ? to.name : to.address) + ' <' + to.address + '>';
|
||||||
|
});
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forward) {
|
||||||
|
body = '\n\n' +
|
||||||
|
'---------- Forwarded message ----------\n' +
|
||||||
|
'From: ' + re.from[0].name + ' <' + re.from[0].address + '>\n' +
|
||||||
|
'Date: ' + sentDate + '\n' +
|
||||||
|
'Subject: ' + re.subject + '\n' +
|
||||||
|
'To: ' + createString(re.to) + '\n' +
|
||||||
|
'Cc: ' + createString(re.cc) + '\n\n\n';
|
||||||
|
|
||||||
|
} else {
|
||||||
|
body = '\n\n' + sentDate + ' ' + from + ' wrote:\n> ';
|
||||||
|
}
|
||||||
|
|
||||||
// only display non html mails in reply part
|
// only display non html mails in reply part
|
||||||
if (!re.html) {
|
if (!re.html) {
|
||||||
|
@ -44,7 +44,7 @@ textarea {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
// allow text selection
|
// allow text selection
|
||||||
-webkit-user-select: text;
|
user-select: none;
|
||||||
|
|
||||||
// make scrollbars invisible
|
// make scrollbars invisible
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
|
@ -13,10 +13,7 @@
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
border: 1px solid #cccccc;
|
border: 1px solid $color-grey-lighter;
|
||||||
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;
|
white-space: normal;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
background-color: darken($color-white, 3%);
|
background-color: darken($color-white, 2%);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,6 +84,7 @@
|
|||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
user-select: text;
|
||||||
|
|
||||||
.working {
|
.working {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@ -138,6 +139,24 @@
|
|||||||
iframe {
|
iframe {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reply-selection {
|
||||||
|
.popover-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option {
|
||||||
|
padding: 7px 30px;
|
||||||
|
color: $color-blue;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
background-color: darken($color-white, 2%);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.controls {
|
.controls {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div class="controls">
|
<div class="controls">
|
||||||
<button ng-click="state.mailList.remove(state.mailList.selected)" class="btn-icon" title="Delete mail"></button>
|
<button ng-click="state.mailList.remove(state.mailList.selected)" class="btn-icon" title="Delete mail"></button>
|
||||||
<button ng-click="state.writer.write(state.mailList.selected)" class="btn-icon" title="Reply to"></button>
|
<button class="btn-icon" title="Reply to" reply-selection></button>
|
||||||
<button ng-click="state.writer.write()" class="btn-icon" title="New mail"></button>
|
<button ng-click="state.writer.write()" class="btn-icon" title="New mail"></button>
|
||||||
</div><!--/.controls-->
|
</div><!--/.controls-->
|
||||||
|
|
||||||
@ -59,4 +59,15 @@
|
|||||||
<div class="arrow"></div>
|
<div class="arrow"></div>
|
||||||
<div class="popover-content">{{keyId}}</div>
|
<div class="popover-content">{{keyId}}</div>
|
||||||
</div><!--/.popover-->
|
</div><!--/.popover-->
|
||||||
|
|
||||||
|
<div class="reply-selection popover bottom">
|
||||||
|
<div class="arrow"></div>
|
||||||
|
<div class="popover-content">
|
||||||
|
<div class="option" ng-click="state.writer.write(state.mailList.selected)">Reply</div>
|
||||||
|
<div class="seperator-line"></div>
|
||||||
|
<div class="option" ng-click="state.writer.write(state.mailList.selected, true)">Reply All</div>
|
||||||
|
<div class="seperator-line"></div>
|
||||||
|
<div class="option" ng-click="state.writer.write(state.mailList.selected, null, true)">Forward</div>
|
||||||
|
</div>
|
||||||
|
</div><!--/.reply-selection-->
|
||||||
</div><!--/.view-read-->
|
</div><!--/.view-read-->
|
||||||
|
@ -10,16 +10,15 @@ define(function(require) {
|
|||||||
appController = require('js/app-controller');
|
appController = require('js/app-controller');
|
||||||
|
|
||||||
describe('Navigation Controller unit test', function() {
|
describe('Navigation Controller unit test', function() {
|
||||||
var scope, ctrl, origEmailDao, emailDaoMock, outboxBoMock, hasIdentity, outboxFolder;
|
var scope, ctrl, origEmailDao, emailDaoMock, outboxBoMock, hasIdentity, outboxFolder, onConnectStub;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function(done) {
|
||||||
hasIdentity = !! window.chrome.identity;
|
hasIdentity = !! window.chrome.identity;
|
||||||
if (!hasIdentity) {
|
if (!hasIdentity) {
|
||||||
window.chrome.identity = {};
|
window.chrome.identity = {};
|
||||||
}
|
}
|
||||||
// remember original module to restore later
|
// remember original module to restore later
|
||||||
origEmailDao = appController._emailDao;
|
origEmailDao = appController._emailDao;
|
||||||
|
|
||||||
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
emailDaoMock = sinon.createStubInstance(EmailDAO);
|
||||||
emailDaoMock._account = {
|
emailDaoMock._account = {
|
||||||
folders: [{
|
folders: [{
|
||||||
@ -37,6 +36,8 @@ define(function(require) {
|
|||||||
outboxBoMock = sinon.createStubInstance(OutboxBO);
|
outboxBoMock = sinon.createStubInstance(OutboxBO);
|
||||||
appController._outboxBo = outboxBoMock;
|
appController._outboxBo = outboxBoMock;
|
||||||
outboxBoMock.startChecking.returns();
|
outboxBoMock.startChecking.returns();
|
||||||
|
onConnectStub = sinon.stub(appController, 'onConnect');
|
||||||
|
onConnectStub.yields();
|
||||||
|
|
||||||
angular.module('navigationtest', []);
|
angular.module('navigationtest', []);
|
||||||
mocks.module('navigationtest');
|
mocks.module('navigationtest');
|
||||||
@ -46,6 +47,7 @@ define(function(require) {
|
|||||||
ctrl = $controller(NavigationCtrl, {
|
ctrl = $controller(NavigationCtrl, {
|
||||||
$scope: scope
|
$scope: scope
|
||||||
});
|
});
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -55,6 +57,7 @@ define(function(require) {
|
|||||||
if (hasIdentity) {
|
if (hasIdentity) {
|
||||||
delete window.chrome.identity;
|
delete window.chrome.identity;
|
||||||
}
|
}
|
||||||
|
onConnectStub.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('initial state', function() {
|
describe('initial state', function() {
|
||||||
|
@ -119,7 +119,7 @@ define(function(require) {
|
|||||||
expect(scope.subject).to.equal('Re: ' + subject);
|
expect(scope.subject).to.equal('Re: ' + subject);
|
||||||
expect(scope.body).to.contain(body);
|
expect(scope.body).to.contain(body);
|
||||||
expect(scope.ciphertextPreview).to.not.be.empty;
|
expect(scope.ciphertextPreview).to.not.be.empty;
|
||||||
expect(verifyMock.calledOnce).to.be.true;
|
expect(verifyMock.called).to.be.true;
|
||||||
|
|
||||||
scope.verify.restore();
|
scope.verify.restore();
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user