mail/src/js/controller/mail-list.js

498 lines
17 KiB
JavaScript
Raw Normal View History

define(function(require) {
'use strict';
var angular = require('angular'),
2013-12-04 08:15:12 -05:00
_ = require('underscore'),
2013-09-11 17:31:08 -04:00
appController = require('js/app-controller'),
notification = require('js/util/notification'),
2014-05-23 04:52:34 -04:00
emailDao, outboxBo, keychainDao;
var MailListCtrl = function($scope, $timeout) {
2013-11-08 17:31:20 -05:00
//
// Init
//
2013-09-15 11:05:37 -04:00
emailDao = appController._emailDao;
outboxBo = appController._outboxBo;
2014-05-23 04:52:34 -04:00
keychainDao = appController._keychain;
2013-09-28 10:08:12 -04:00
//
// scope functions
//
2014-02-20 09:42:51 -05:00
$scope.getBody = function(email) {
emailDao.getBody({
folder: currentFolder(),
2014-02-17 08:31:14 -05:00
message: email
}, function(err) {
if (err && err.code !== 42) {
$scope.onError(err);
return;
}
// display fetched body
$scope.$digest();
// automatically decrypt if it's the selected email
if (email === currentMessage()) {
2014-02-24 04:14:07 -05:00
emailDao.decryptBody({
message: email
}, $scope.onError);
}
});
2014-02-17 08:31:14 -05:00
};
2014-01-15 09:27:38 -05:00
/**
* Called when clicking on an email list item
*/
2013-11-08 17:31:20 -05:00
$scope.select = function(email) {
2014-02-18 11:05:51 -05:00
// unselect an item
2013-10-04 12:01:42 -04:00
if (!email) {
$scope.state.mailList.selected = undefined;
2013-10-04 12:01:42 -04:00
return;
}
2013-11-18 11:44:59 -05:00
2014-02-21 04:47:49 -05:00
$scope.state.mailList.selected = email;
$scope.state.read.toggle(true);
2014-02-21 04:47:49 -05:00
2014-05-23 04:52:34 -04:00
keychainDao.refreshKeyForUserId(email.from[0].address, onKeyRefreshed);
2014-02-17 08:31:14 -05:00
2014-05-23 04:52:34 -04:00
function onKeyRefreshed(err) {
if (err) {
$scope.onError(err);
}
2014-05-23 04:52:34 -04:00
emailDao.decryptBody({
message: email
}, $scope.onError);
// if the email is unread, please sync the new state.
// otherweise forget about it.
if (!email.unread) {
return;
}
$scope.toggleUnread(email);
}
};
2014-01-15 09:27:38 -05:00
/**
* Mark an email as unread or read, respectively
2014-01-15 09:27:38 -05:00
*/
$scope.toggleUnread = function(message) {
updateStatus('Updating unread flag...');
message.unread = !message.unread;
emailDao.setFlags({
folder: currentFolder(),
message: message
}, function(err) {
if (err && err.code === 42) {
// offline, restore
message.unread = !message.unread;
updateStatus('Unable to mark unread flag in offline mode!');
return;
}
if (err) {
updateStatus('Error on sync!');
$scope.onError(err);
return;
}
2013-11-11 09:53:34 -05:00
2014-06-10 08:23:51 -04:00
updateStatus('Online');
$scope.$apply();
});
};
2014-01-15 09:27:38 -05:00
/**
* Delete a message
2014-01-15 09:27:38 -05:00
*/
$scope.remove = function(message) {
if (!message) {
2013-11-11 09:53:34 -05:00
return;
}
updateStatus('Deleting message...');
remove();
function remove() {
emailDao.deleteMessage({
folder: currentFolder(),
message: message
}, function(err) {
if (err) {
// show errors where appropriate
if (err.code === 42) {
$scope.select(message);
updateStatus('Unable to delete message in offline mode!');
return;
}
updateStatus('Error during delete!');
$scope.onError(err);
}
updateStatus('Message deleted!');
$scope.$apply();
2013-12-04 08:15:12 -05:00
});
}
2013-11-08 17:31:20 -05:00
};
// share local scope functions with root state
$scope.state.mailList = {
remove: $scope.remove
};
//
// watch tasks
//
/**
* List emails from folder when user changes folder
*/
$scope._stopWatchTask = $scope.$watch('state.nav.currentFolder', function() {
if (!currentFolder()) {
return;
}
// in development, display dummy mail objects
if (!window.chrome || !chrome.identity) {
updateStatus('Last update: ', new Date());
currentFolder().messages = createDummyMails();
return;
}
// display and select first
openCurrentFolder();
});
2014-06-02 11:54:29 -04:00
$scope.$watchCollection('state.nav.currentFolder.messages', selectFirstMessage);
2014-06-02 11:54:29 -04:00
function selectFirstMessage(messages) {
if (!messages) {
return;
}
// Shows the next message based on the uid of the currently selected element
2014-06-02 11:54:29 -04:00
if (messages.indexOf(currentMessage()) === -1) {
// wait until after first $digest() so $scope.filteredMessages is set
$timeout(function() {
$scope.select($scope.filteredMessages ? $scope.filteredMessages[0] : undefined);
});
}
}
/**
* Sync current folder when client comes back online
*/
$scope.$watch('account.online', function(isOnline) {
if (isOnline) {
updateStatus('Online');
openCurrentFolder();
} else {
updateStatus('Offline mode');
}
}, true);
2013-11-08 17:31:20 -05:00
2013-09-28 10:08:12 -04:00
//
// Helper Functions
2013-09-28 10:08:12 -04:00
//
function openCurrentFolder() {
emailDao.openFolder({
folder: currentFolder()
}, function(error) {
// dont wait until scroll to load visible mail bodies
$scope.loadVisibleBodies();
// don't display error for offline case
if (error && error.code === 42) {
return;
}
$scope.onError(error);
});
2013-09-26 07:26:57 -04:00
}
2013-09-30 15:22:46 -04:00
function updateStatus(lbl, time) {
$scope.lastUpdateLbl = lbl;
$scope.lastUpdate = (time) ? time : '';
}
function currentFolder() {
2013-11-08 17:31:20 -05:00
return $scope.state.nav.currentFolder;
2013-09-30 15:22:46 -04:00
}
2014-05-08 10:25:20 -04:00
function currentMessage() {
return $scope.state.mailList.selected;
}
2014-05-08 10:25:20 -04:00
//
// Notification API
//
2014-05-08 10:25:20 -04:00
(emailDao || {}).onIncomingMessage = function(msgs) {
2014-05-08 10:25:20 -04:00
var popupId, popupTitle, popupMessage, unreadMsgs;
unreadMsgs = msgs.filter(function(msg) {
return msg.unread;
});
if (unreadMsgs.length === 0) {
return;
}
popupId = '' + unreadMsgs[0].uid;
if (unreadMsgs.length > 1) {
popupTitle = unreadMsgs.length + ' new messages';
popupMessage = _.pluck(unreadMsgs, 'subject').join('\n');
} else {
popupTitle = unreadMsgs[0].from[0].name || unreadMsgs[0].from[0].address;
popupMessage = unreadMsgs[0].subject;
}
notification.create({
id: popupId,
title: popupTitle,
message: popupMessage
});
};
notification.setOnClickedListener(function(uidString) {
var uid = parseInt(uidString, 10);
if (isNaN(uid)) {
return;
}
$scope.select(_.findWhere(currentFolder().messages, {
uid: uid
}));
});
2013-09-26 07:26:57 -04:00
};
//
// Directives
//
var ngModule = angular.module('mail-list', []);
ngModule.directive('woTouch', function($parse) {
return function(scope, elm, attrs) {
var handler = $parse(attrs.woTouch);
elm.on('touchstart', function() {
elm.addClass('active');
});
elm.on('touchleave touchcancel touchmove', function() {
elm.removeClass('active');
});
elm.on('touchend click', function(event) {
event.preventDefault();
elm.removeClass('active');
scope.$apply(function() {
handler(scope, {
$event: event
});
});
});
};
});
ngModule.directive('listScroll', function() {
return {
link: function(scope, elm, attrs) {
var model = attrs.listScroll,
listEl = elm[0],
scrollTimeout;
/*
* iterates over the mails in the mail list and loads their bodies if they are visible in the viewport
*/
scope.loadVisibleBodies = function() {
var listBorder = listEl.getBoundingClientRect(),
top = listBorder.top,
bottom = listBorder.bottom,
listItems = listEl.children[0].children,
inViewport = false,
listItem, message,
isPartiallyVisibleTop, isPartiallyVisibleBottom, isVisible;
for (var i = 0, len = listItems.length; i < len; i++) {
// the n-th list item (the dom representation of an email) corresponds to
// the n-th message model in the filteredMessages array
listItem = listItems.item(i).getBoundingClientRect();
if (!scope.filteredMessages || scope.filteredMessages.length <= i) {
// stop if i get larger than the size of filtered messages
break;
}
message = scope.filteredMessages[i];
isPartiallyVisibleTop = listItem.top < top && listItem.bottom > top; // a portion of the list item is visible on the top
isPartiallyVisibleBottom = listItem.top < bottom && listItem.bottom > bottom; // a portion of the list item is visible on the bottom
isVisible = listItem.top >= top && listItem.bottom <= bottom; // the list item is visible as a whole
if (isPartiallyVisibleTop || isVisible || isPartiallyVisibleBottom) {
// we are now iterating over visible elements
inViewport = true;
// load mail body of visible
scope.getBody(message);
} else if (inViewport) {
// we are leaving the viewport, so stop iterating over the items
break;
}
}
};
// load body when scrolling
listEl.onscroll = function() {
if (scrollTimeout) {
// remove timeout so that only scroll end
clearTimeout(scrollTimeout);
}
scrollTimeout = setTimeout(function() {
scope.loadVisibleBodies();
}, 300);
};
// load the visible message bodies, when the list is re-initialized and when scrolling stopped
scope.$watchCollection(model, function() {
scope.loadVisibleBodies();
});
}
};
});
// Helper for development mode
2013-10-09 04:22:29 -04:00
function createDummyMails() {
var uid = 0;
var Email = function(unread, attachments, answered) {
this.uid = uid++;
this.from = [{
name: 'Whiteout Support',
address: 'support@whiteout.io'
}]; // sender address
this.to = [{
address: 'max.musterman@gmail.com'
}, {
address: 'max.musterman@gmail.com'
}]; // list of receivers
this.cc = [{
address: 'john.doe@gmail.com'
}]; // list of receivers
if (attachments) {
// body structure with three attachments
2014-02-17 08:31:14 -05:00
this.bodystructure = {
"1": {
"part": "1",
"type": "text/plain",
"parameters": {
"charset": "us-ascii"
},
"encoding": "7bit",
"size": 9,
"lines": 2
},
"2": {
"part": "2",
"type": "application/octet-stream",
"parameters": {
"name": "a.md"
},
"encoding": "7bit",
"size": 123,
"disposition": [{
"type": "attachment",
"filename": "a.md"
}]
},
"3": {
"part": "3",
"type": "application/octet-stream",
"parameters": {
"name": "b.md"
},
"encoding": "7bit",
"size": 456,
"disposition": [{
"type": "attachment",
"filename": "b.md"
}]
},
"4": {
"part": "4",
"type": "application/octet-stream",
"parameters": {
"name": "c.md"
},
"encoding": "7bit",
"size": 789,
"disposition": [{
"type": "attachment",
"filename": "c.md"
}]
},
"type": "multipart/mixed"
};
this.attachments = [{
"filename": "a.md",
"filesize": 123,
"mimeType": "text/x-markdown",
"part": "2",
"content": null
}, {
"filename": "b.md",
"filesize": 456,
"mimeType": "text/x-markdown",
"part": "3",
"content": null
}, {
"filename": "c.md",
"filesize": 789,
"mimeType": "text/x-markdown",
"part": "4",
"content": null
}];
} else {
2014-02-17 08:31:14 -05:00
this.bodystructure = {
"part": "1",
"type": "text/plain",
"parameters": {
"charset": "us-ascii"
},
"encoding": "7bit",
"size": 9,
"lines": 2
};
this.attachments = [];
}
this.unread = unread;
this.answered = answered;
2013-09-26 07:26:57 -04:00
this.sentDate = new Date('Thu Sep 19 2013 20:41:23 GMT+0200 (CEST)');
2013-11-18 11:44:59 -05:00
this.subject = 'Getting started'; // Subject line
this.body = 'And a good day to you too sir. \n' +
'\n' +
'Thursday, Apr 24, 2014 3:33 PM safewithme.testuser@gmail.com wrote:\n' +
'> adsfadfasdfasdfasfdasdfasdfas\n' +
'\n' +
'http://example.com\n' +
'\n' +
'> Tuesday, Mar 25, 2014 4:19 PM gianniarcore@gmail.com wrote:\n' +
'>> from 0.7.0.1\n' +
'>>\n' +
'>> God speed!'; // plaintext body
//this.html = '<!DOCTYPE html><html><head></head><body><h1>Hello there</h1></body></html>';
this.encrypted = true;
this.decrypted = true;
};
var dummys = [new Email(true, true), new Email(true, false, true), new Email(false, true, true), new Email(false, true), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false), new Email(false)];
2013-10-09 04:22:29 -04:00
return dummys;
}
return MailListCtrl;
});