diff --git a/NEWS b/NEWS
index 09deea7ed..5288e6534 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,7 @@ Enhancements
- [web] expose all email addresses in autocompletion of message editor (#3443)
- [web] Gravatar service can now be disabled (#3600)
- [web] collapsable mail accounts (#3493)
+ - [web] show progress indicator when loading messages and cards
Bug fixes
- [web] fixed creation of chip on blur (sgTransformOnBlur directive)
diff --git a/UI/Templates/ContactsUI/UIxContactFoldersView.wox b/UI/Templates/ContactsUI/UIxContactFoldersView.wox
index ab4337098..80b5f510c 100644
--- a/UI/Templates/ContactsUI/UIxContactFoldersView.wox
+++ b/UI/Templates/ContactsUI/UIxContactFoldersView.wox
@@ -411,17 +411,22 @@
+ ng-show="currentCard.$isList()">
+
+
+
diff --git a/UI/Templates/MailerUI/UIxMailFolderTemplate.wox b/UI/Templates/MailerUI/UIxMailFolderTemplate.wox
index 59ad127d1..8657aa4f2 100644
--- a/UI/Templates/MailerUI/UIxMailFolderTemplate.wox
+++ b/UI/Templates/MailerUI/UIxMailFolderTemplate.wox
@@ -302,6 +302,11 @@
forward
attach_file
+
+
+
@@ -309,7 +314,7 @@
ng-show="mailbox.service.selectedFolder.$isLoading">
+ md-diameter="32">
index
_this.idsMap[data.id] = i;
diff --git a/UI/WebServerResources/js/Contacts/AddressBookController.js b/UI/WebServerResources/js/Contacts/AddressBookController.js
index f67110421..cca2da3e9 100644
--- a/UI/WebServerResources/js/Contacts/AddressBookController.js
+++ b/UI/WebServerResources/js/Contacts/AddressBookController.js
@@ -17,7 +17,6 @@
vm.selectCard = selectCard;
vm.toggleCardSelection = toggleCardSelection;
vm.newComponent = newComponent;
- vm.notSelectedComponent = notSelectedComponent;
vm.unselectCards = unselectCards;
vm.confirmDeleteSelectedCards = confirmDeleteSelectedCards;
vm.copySelectedCards = copySelectedCards;
@@ -79,10 +78,6 @@
}
}
- function notSelectedComponent(currentCard, type) {
- return (currentCard && currentCard.c_component == type && !currentCard.selected);
- }
-
function unselectCards() {
_.forEach(vm.selectedFolder.$cards, function(card) {
card.selected = false;
@@ -180,12 +175,10 @@
});
}
else {
- promises.push(vm.selectedFolder.$getCard(card.id).then(function(card) {
- return card.$futureCardData.then(function(data) {
- _.forEach(data.refs, function(ref) {
- if (ref.email.length)
- recipients.push(ref.$shortFormat());
- });
+ promises.push(card.$reload().then(function(card) {
+ _.forEach(card.refs, function(ref) {
+ if (ref.email.length)
+ recipients.push(ref.$shortFormat());
});
}));
}
diff --git a/UI/WebServerResources/js/Contacts/Card.service.js b/UI/WebServerResources/js/Contacts/Card.service.js
index 98264fa5b..4c2925a68 100644
--- a/UI/WebServerResources/js/Contacts/Card.service.js
+++ b/UI/WebServerResources/js/Contacts/Card.service.js
@@ -70,9 +70,11 @@
}
angular.module('SOGo.ContactsUI')
.constant('sgCard_STATUS', {
- NOT_LOADED: 0,
- LOADING: 1,
- LOADED: 2
+ NOT_LOADED: 0,
+ DELAYED_LOADING: 1,
+ LOADING: 2,
+ LOADED: 3,
+ DELAYED_MS: 300
})
.factory('Card', Card.$factory);
@@ -164,6 +166,33 @@
});
};
+ /**
+ * @function $isLoading
+ * @memberof Card.prototype
+ * @returns true if the Card definition is still being retrieved from server after a specific delay
+ * @see sgCard_STATUS
+ */
+ Card.prototype.$isLoading = function() {
+ return this.$loaded == Card.STATUS.LOADING;
+ };
+
+ /**
+ * @function $reload
+ * @memberof Message.prototype
+ * @desc Fetch the viewable message body along with other metadata such as the list of attachments.
+ * @returns a promise of the HTTP operation
+ */
+ Card.prototype.$reload = function() {
+ var futureCardData;
+
+ if (this.$futureCardData)
+ return this;
+
+ futureCardData = Card.$$resource.fetch([this.pid, this.id].join('/'), 'view');
+
+ return this.$unwrap(futureCardData);
+ };
+
/**
* @function $save
* @memberof Card.prototype
@@ -498,7 +527,11 @@
var _this = this;
// Card is not loaded yet
- this.$loaded = Card.STATUS.LOADING;
+ this.$loaded = Card.STATUS.DELAYED_LOADING;
+ Card.$timeout(function() {
+ if (_this.$loaded != Card.STATUS.LOADED)
+ _this.$loaded = Card.STATUS.LOADING;
+ }, Card.STATUS.DELAYED_MS);
// Expose the promise
this.$futureCardData = futureCardData.then(function(data) {
@@ -523,6 +556,8 @@
return _this;
});
+
+ return this.$futureCardData;
};
/**
diff --git a/UI/WebServerResources/js/Contacts/Contacts.app.js b/UI/WebServerResources/js/Contacts/Contacts.app.js
index f6bd3e15d..d10d1abe1 100644
--- a/UI/WebServerResources/js/Contacts/Contacts.app.js
+++ b/UI/WebServerResources/js/Contacts/Contacts.app.js
@@ -64,7 +64,9 @@
},
resolve: {
stateCard: stateCard
- }
+ },
+ onEnter: onEnterCard,
+ onExit: onExitCard
})
.state('app.addressbook.card.view', {
url: '/view',
@@ -129,10 +131,37 @@
/**
* @ngInject
*/
- stateCard.$inject = ['$stateParams', 'stateAddressbook'];
- function stateCard($stateParams, stateAddressbook) {
+ stateCard.$inject = ['$state', '$stateParams', 'stateAddressbook'];
+ function stateCard($state, $stateParams, stateAddressbook) {
+ var card;
+
+ card = _.find(stateAddressbook.$cards, function(cardObject) {
+ return (cardObject.id == $stateParams.cardId);
+ });
+
+ if (card) {
+ return card.$reload();
+ }
+ else {
+ // Card not found
+ $state.go('app.addressbook');
+ }
+ }
+
+ /**
+ * @ngInject
+ */
+ onEnterCard.$inject = ['$stateParams', 'stateAddressbook'];
+ function onEnterCard($stateParams, stateAddressbook) {
stateAddressbook.selectedCard = $stateParams.cardId;
- return stateAddressbook.$getCard($stateParams.cardId);
+ }
+
+ /**
+ * @ngInject
+ */
+ onExitCard.$inject = ['stateAddressbook'];
+ function onExitCard(stateMailbox) {
+ delete stateAddressbook.selectedCard;
}
/**
diff --git a/UI/WebServerResources/js/Mailer/Mailbox.service.js b/UI/WebServerResources/js/Mailer/Mailbox.service.js
index 64eef92a3..b5224eec2 100644
--- a/UI/WebServerResources/js/Mailer/Mailbox.service.js
+++ b/UI/WebServerResources/js/Mailer/Mailbox.service.js
@@ -600,7 +600,7 @@
* @return the index of the first deleted message
*/
Mailbox.prototype.$_deleteMessages = function(uids, messages) {
- var _this = this, selectedMessages, selectedUIDs, unseen, firstIndex = this.$messages.length;
+ var _this = this, selectedUIDs, _$messages, unseen, firstIndex = this.$messages.length;
// Decrement the unseen count
unseen = _.filter(messages, function(message, i) { return !message.isread; });
diff --git a/UI/WebServerResources/js/Mailer/Mailer.app.js b/UI/WebServerResources/js/Mailer/Mailer.app.js
index cdb279d38..200caff7d 100644
--- a/UI/WebServerResources/js/Mailer/Mailer.app.js
+++ b/UI/WebServerResources/js/Mailer/Mailer.app.js
@@ -61,13 +61,13 @@
controllerAs: 'viewer'
}
},
- onEnter: onEnterMessage,
- onExit: onExitMessage,
resolve: {
stateMailbox: stateVirtualMailboxOfMessage,
stateMessages: stateMessages,
stateMessage: stateMessage
- }
+ },
+ onEnter: onEnterMessage,
+ onExit: onExitMessage
})
.state('mail.account.inbox', {
url: '/inbox',
@@ -279,7 +279,7 @@
});
if (message) {
- return message.$reload();
+ return message.$reload({useCache: true});
}
else {
// Message not found
diff --git a/UI/WebServerResources/js/Mailer/Message.service.js b/UI/WebServerResources/js/Mailer/Message.service.js
index 1eb0cf9f5..2b578e48f 100644
--- a/UI/WebServerResources/js/Mailer/Message.service.js
+++ b/UI/WebServerResources/js/Mailer/Message.service.js
@@ -39,8 +39,9 @@
* @desc The factory we'll use to register with Angular
* @returns the Message constructor
*/
- Message.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'Gravatar', 'Resource', 'Preferences', function($q, $timeout, $log, Settings, Gravatar, Resource, Preferences) {
+ Message.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'sgMessage_STATUS', 'Gravatar', 'Resource', 'Preferences', function($q, $timeout, $log, Settings, Message_STATUS, Gravatar, Resource, Preferences) {
angular.extend(Message, {
+ STATUS: Message_STATUS,
$q: $q,
$timeout: $timeout,
$log: $log,
@@ -72,6 +73,13 @@
angular.module('SOGo.MailerUI', ['SOGo.Common']);
}
angular.module('SOGo.MailerUI')
+ .constant('sgMessage_STATUS', {
+ NOT_LOADED: 0,
+ DELAYED_LOADING: 1,
+ LOADING: 2,
+ LOADED: 3,
+ DELAYED_MS: 300
+ })
.factory('Message', Message.$factory);
/**
@@ -503,15 +511,29 @@
});
};
+ /**
+ * @function $isLoading
+ * @memberof Message.prototype
+ * @returns true if the Message content is still being retrieved from server after a specific delay
+ * @see sgMessage_STATUS
+ */
+ Message.prototype.$isLoading = function() {
+ return this.$loaded == Message.STATUS.LOADING;
+ };
+
/**
* @function $reload
* @memberof Message.prototype
* @desc Fetch the viewable message body along with other metadata such as the list of attachments.
+ * @param {object} [options] - set {useCache: true} to use already fetched data
* @returns a promise of the HTTP operation
*/
Message.prototype.$reload = function(options) {
var futureMessageData;
+ if (options && options.useCache && this.$futureMessageData)
+ return this;
+
futureMessageData = Message.$$resource.fetch(this.$absolutePath(options), 'view');
return this.$unwrap(futureMessageData);
@@ -638,6 +660,13 @@
Message.prototype.$unwrap = function(futureMessageData) {
var _this = this;
+ // Message is not loaded yet
+ this.$loaded = Message.STATUS.DELAYED_LOADING;
+ Message.$timeout(function() {
+ if (_this.$loaded != Message.STATUS.LOADED)
+ _this.$loaded = Message.STATUS.LOADING;
+ }, Message.STATUS.DELAYED_MS);
+
// Resolve and expose the promise
this.$futureMessageData = futureMessageData.then(function(data) {
// Calling $timeout will force Angular to refresh the view
@@ -653,6 +682,7 @@
angular.extend(_this, data);
_this.$formatFullAddresses();
_this.$loadUnsafeContent = false;
+ _this.$loaded = Message.STATUS.LOADED;
return _this;
});
});
diff --git a/UI/WebServerResources/scss/components/progressLinear/progress-linear.scss b/UI/WebServerResources/scss/components/progressLinear/progress-linear.scss
index ae84c9a0e..b17295053 100644
--- a/UI/WebServerResources/scss/components/progressLinear/progress-linear.scss
+++ b/UI/WebServerResources/scss/components/progressLinear/progress-linear.scss
@@ -1,2 +1,9 @@
/// progressLinear.scss -*- Mode: scss; indent-tabs-mode: nil; basic-offset: 2 -*-
@import 'extends';
+
+.sg-progress-linear-bottom {
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ right: 0;
+}
\ No newline at end of file