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

369 lines
9.7 KiB
JavaScript

'use strict';
var searchTimeout;
//
// Constants
//
var INIT_DISPLAY_LEN = 50,
SCROLL_DISPLAY_LEN = 10,
FOLDER_TYPE_INBOX = 'Inbox',
NOTIFICATION_INBOX_TIMEOUT = 5000;
var MailListCtrl = function($scope, $timeout, $location, $filter, $q, status, notification, email, keychain, dialog, search, dummy) {
//
// scope state
//
$scope.state.mailList = {};
/**
* Gathers unread notifications to be cancelled later
*/
$scope.pendingNotifications = [];
//
// url/history handling
//
/**
* Set the route to a message which will go to read mode
*/
$scope.navigate = function(message) {
if (!message || !message.from) {
// early return if message has not finished loading yet
return;
}
$location.search('uid', message.uid);
};
$scope.loc = $location;
$scope.$watch('(loc.search()).uid', function(uid) {
uid = parseInt(uid, 10);
if (isNaN(uid)) {
// no (or nonsensical) uid specified in url... select no message
$scope.select();
return;
}
// select the message specified by the uid in the url
$scope.select(_.findWhere(currentFolder().messages, {
uid: uid
}));
});
//
// scope functions
//
$scope.getBody = function(messages) {
return $q(function(resolve) {
resolve();
}).then(function() {
return email.getBody({
folder: currentFolder(),
messages: messages
});
}).catch(function(err) {
if (err.code !== 42) {
dialog.error(err);
}
});
};
/**
* Called when clicking on an message list item
*/
$scope.select = function(message) {
// unselect an item
if (!message) {
$scope.state.mailList.selected = undefined;
return;
}
$scope.state.mailList.selected = message;
if ($location.search().dev) {
// stop here in dev mode
return;
}
return $q(function(resolve) {
resolve();
}).then(function() {
return keychain.refreshKeyForUserId({
userId: message.from[0].address
});
}).then(function() {
return email.decryptBody({
message: message
});
}).then(function() {
// if the message is unread, please sync the new state.
// otherweise forget about it.
if (!message.unread) {
return;
}
// let's close pending notifications for unread messages in the inbox
if (currentFolder().type === FOLDER_TYPE_INBOX) {
while ($scope.pendingNotifications.length) {
notification.close($scope.pendingNotifications.shift());
}
}
return $scope.state.actionBar.markMessage(message, false, true);
}).catch(dialog.error);
};
$scope.flag = function(message, flagged) {
return $scope.state.actionBar.flagMessage(message, flagged);
};
/**
* Date formatting
*/
$scope.formatDate = function(date) {
if (!date) {
return;
}
if (typeof date === 'string') {
date = new Date(date);
}
var now = new Date();
// return time if mail is from today
if (now.getDay() === date.getDay() && now.getMonth() === date.getMonth() && now.getFullYear() === date.getFullYear()) {
return $filter('date')(date, 'shortTime');
}
return $filter('date')(date, 'mediumDate');
};
//
// watch tasks
//
/**
* List messages from folder when user changes folder
*/
$scope._stopWatchTask = $scope.$watch('state.nav.currentFolder', function() {
if (!currentFolder()) {
return;
}
// reset searchFilter
$scope.searchText = undefined;
// in development, display dummy mail objects
if ($location.search().dev) {
status.update('Last update: ', new Date());
currentFolder().messages = dummy.listMails();
return;
}
// display and select first
return openCurrentFolder();
});
$scope.watchMessages = $scope.$watchCollection('state.nav.currentFolder.messages', function(messages) {
if (!messages) {
return;
}
// sort message by uid
messages.sort(byUidDescending);
// Unselect message if it has been deleted from the messages array
if (messages.indexOf(currentMessage()) === -1) {
$scope.select();
}
// set display buffer to first messages
$scope.displayMessages = messages.slice(0, INIT_DISPLAY_LEN);
});
/**
* display more items (for infinite scrolling)
*/
$scope.displayMore = function() {
if (!currentFolder() || !$scope.displayMessages) {
// folders not yet initialized
return;
}
var len = currentFolder().messages.length,
dLen = $scope.displayMessages.length;
if (dLen === len || $scope.searchText) {
// all messages are already displayed or we're in search mode
return;
}
// copy next interval of messages to the end of the display messages array
var next = currentFolder().messages.slice(dLen, dLen + SCROLL_DISPLAY_LEN);
Array.prototype.push.apply($scope.displayMessages, next);
};
/**
* Handle search event in other parts of the app by filtering messages in the mail-list
*/
$scope.$on('search', function(e, query) {
$scope.displaySearchResults(query);
});
/**
* This method is called when the user changes the searchText
*/
$scope.displaySearchResults = function(searchText) {
if (searchTimeout) {
// remove timeout to wait for user typing query
clearTimeout(searchTimeout);
}
if (!searchText) {
// set display buffer to first messages
$scope.displayMessages = currentFolder().messages.slice(0, INIT_DISPLAY_LEN);
status.setSearching(false);
status.update('Online');
return;
}
// display searching spinner
status.setSearching(true);
status.update('Searching ...');
searchTimeout = setTimeout(function() {
$scope.$apply(function() {
// filter relevant messages
$scope.displayMessages = search.filter(currentFolder().messages, searchText);
status.setSearching(false);
status.update('Matches in this folder');
});
}, 500);
};
/**
* Sync current folder when client comes back online
*/
$scope.watchOnline = $scope.$watch('account.online', function(isOnline) {
// wait one cycle for the status display controllers to init
return $timeout(function() {
if (isOnline) {
status.update('Online');
return openCurrentFolder();
} else {
status.update('Offline mode');
}
});
}, true);
//
// Helper Functions
//
function openCurrentFolder() {
if (!currentFolder()) {
return;
}
return $q(function(resolve) {
resolve();
}).then(function() {
return email.openFolder({
folder: currentFolder()
}).catch(function(err) {
// don't display err for offline case
if (err.code !== 42) {
throw err;
}
});
}).then(function() {
// dont wait until scroll to load visible mail bodies
$scope.loadVisibleBodies();
}).catch(dialog.error);
}
$scope.$on('read', function(e, state) {
if (!state) {
// load bodies after closing read mode
$scope.loadVisibleBodies();
}
});
function currentFolder() {
return $scope.state.nav && $scope.state.nav.currentFolder;
}
function currentMessage() {
return $scope.state.mailList.selected;
}
//
// Notification API
//
email.onIncomingMessage = function(msgs) {
var note, title, message, unreadMsgs;
unreadMsgs = msgs.filter(function(msg) {
return msg.unread;
});
if (unreadMsgs.length === 0) {
return;
}
if (unreadMsgs.length === 1) {
title = unreadMsgs[0].from[0].name || unreadMsgs[0].from[0].address;
message = unreadMsgs[0].subject;
} else {
title = unreadMsgs.length + ' new messages';
message = _.pluck(unreadMsgs, 'subject').join('\n');
}
note = notification.create({
title: title,
message: message,
onClick: function() {
// remove from pending notificatiosn
var index = $scope.pendingNotifications.indexOf(note);
if (index !== -1) {
$scope.pendingNotifications.splice(index, 1);
}
// open the message
$scope.navigate(_.findWhere(currentFolder().messages, {
uid: unreadMsgs[0].uid
}));
},
timeout: NOTIFICATION_INBOX_TIMEOUT
});
$scope.pendingNotifications.push(note);
};
};
//
// helper functions
//
function byUidDescending(a, b) {
if (a.uid < b.uid) {
return 1;
} else if (b.uid < a.uid) {
return -1;
} else {
return 0;
}
}
module.exports = MailListCtrl;