mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-06-01 16:39:46 +00:00
(css,js) Improve progress feedback
This ads a "ripple" effect that blocks the context when login in or sending a message. Generic enough to be used elsewhere. Fixes #3765
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('SOGo.Common')
|
||||
.directive('sgRippleClick', sgRippleClick);
|
||||
|
||||
/*
|
||||
* sgRippleClick - A ripple effect to cover the parent element.
|
||||
* @memberof SOGo.Common
|
||||
* @restrict attribute
|
||||
*
|
||||
* @example:
|
||||
|
||||
<md-dialog id="mailEditor">
|
||||
<md-button ng-click="editor.send()"
|
||||
sg-ripple-click="mailEditor">Send</md-button>
|
||||
</md-dialog>
|
||||
|
||||
*/
|
||||
sgRippleClick.$inject = ['$log', '$timeout'];
|
||||
function sgRippleClick($log, $timeout) {
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
compile: compile
|
||||
};
|
||||
|
||||
function compile(tElement, tAttrs) {
|
||||
|
||||
return function postLink(scope, element, attr) {
|
||||
var ripple, content, container, containerId;
|
||||
|
||||
// Lookup container element
|
||||
containerId = element.attr('sg-ripple-click');
|
||||
container = element[0].parentNode;
|
||||
while (container && container.id != containerId) {
|
||||
container = container.parentNode;
|
||||
}
|
||||
if (!container) {
|
||||
$log.error('No parent element found with id ' + containerId);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Lookup sg-ripple-content element
|
||||
content = container.querySelector('sg-ripple-content');
|
||||
if (!content) {
|
||||
$log.error('sg-ripple-content not found inside #' + containerId);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Lookup sg-ripple element
|
||||
ripple = container.querySelector('sg-ripple');
|
||||
if (ripple) {
|
||||
ripple = angular.element(ripple);
|
||||
}
|
||||
else {
|
||||
// If ripple layer doesn't exit, create it with the primary background color
|
||||
ripple = angular.element('<sg-ripple class="md-default-theme md-bg"></sg-ripple>');
|
||||
container.appendChild(ripple[0]);
|
||||
|
||||
// Hide ripple content on initialization
|
||||
if (!content.classList.contains('ng-hide'))
|
||||
content.classList.add('ng-hide');
|
||||
}
|
||||
|
||||
// Register listener
|
||||
element.on('click', listener);
|
||||
|
||||
function listener(event) {
|
||||
if (element[0].hasAttribute('disabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.classList.contains('ng-hide')) {
|
||||
// Show ripple
|
||||
angular.element(container).css({ 'overflow': 'hidden' });
|
||||
content.classList.remove('ng-hide');
|
||||
angular.element(content).css({ top: container.scrollTop + 'px' });
|
||||
ripple.css({
|
||||
'top': (event.pageY - container.offsetTop + container.scrollTop) + 'px',
|
||||
'left': (event.pageX - container.offsetLeft) + 'px',
|
||||
'width': '400vmin',
|
||||
'height': '400vmin'
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Hide ripple layer
|
||||
ripple.css({
|
||||
'top': (event.pageY - container.offsetTop + container.scrollTop) + 'px',
|
||||
'left': (event.pageX - container.offsetLeft) + 'px',
|
||||
'height': '0px',
|
||||
'width': '0px'
|
||||
});
|
||||
// Hide ripple content
|
||||
content.classList.add('ng-hide');
|
||||
// Restore overflow of container once the animation is completed
|
||||
$timeout(function() {
|
||||
angular.element(container).css({ 'overflow': '' });
|
||||
}, 800);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
})();
|
||||
@@ -636,18 +636,18 @@
|
||||
|
||||
Message.$log.debug('send = ' + JSON.stringify(data, undefined, 2));
|
||||
|
||||
return Message.$$resource.post(this.$absolutePath({asDraft: true}), 'send', data).then(function(data) {
|
||||
if (data.status == 'success') {
|
||||
return Message.$$resource.post(this.$absolutePath({asDraft: true}), 'send', data).then(function(response) {
|
||||
if (response.status == 'success') {
|
||||
if (angular.isDefined(_this.origin)) {
|
||||
if (_this.origin.action.startsWith('reply'))
|
||||
_this.origin.message.isanswered = true;
|
||||
else if (_this.origin.action == 'forward')
|
||||
_this.origin.message.isforwarded = true;
|
||||
}
|
||||
return data;
|
||||
return response;
|
||||
}
|
||||
else {
|
||||
return Message.$q.reject(data);
|
||||
return Message.$q.reject(response.data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
MessageEditorController.$inject = ['$window', '$stateParams', '$mdConstant', '$mdDialog', '$mdToast', 'FileUploader', 'stateAccount', 'stateMessage', 'encodeUriFilter', '$timeout', 'Dialog', 'AddressBook', 'Card', 'Preferences'];
|
||||
function MessageEditorController($window, $stateParams, $mdConstant, $mdDialog, $mdToast, FileUploader, stateAccount, stateMessage, encodeUriFilter, $timeout, Dialog, AddressBook, Card, Preferences) {
|
||||
var vm = this, semicolon = 186;
|
||||
MessageEditorController.$inject = ['$scope', '$window', '$stateParams', '$mdConstant', '$mdDialog', '$mdToast', 'FileUploader', 'stateAccount', 'stateMessage', 'encodeUriFilter', '$timeout', 'Dialog', 'AddressBook', 'Card', 'Preferences'];
|
||||
function MessageEditorController($scope, $window, $stateParams, $mdConstant, $mdDialog, $mdToast, FileUploader, stateAccount, stateMessage, encodeUriFilter, $timeout, Dialog, AddressBook, Card, Preferences) {
|
||||
var vm = this;
|
||||
|
||||
vm.addRecipient = addRecipient;
|
||||
vm.autocomplete = {to: {}, cc: {}, bcc: {}};
|
||||
@@ -19,10 +19,16 @@
|
||||
vm.cancel = cancel;
|
||||
vm.save = save;
|
||||
vm.send = send;
|
||||
vm.sendState = false;
|
||||
vm.removeAttachment = removeAttachment;
|
||||
vm.contactFilter = contactFilter;
|
||||
vm.identities = _.map(stateAccount.identities, 'full');
|
||||
vm.recipientSeparatorKeys = [$mdConstant.KEY_CODE.ENTER, $mdConstant.KEY_CODE.TAB, $mdConstant.KEY_CODE.COMMA, semicolon];
|
||||
vm.recipientSeparatorKeys = [
|
||||
$mdConstant.KEY_CODE.ENTER,
|
||||
$mdConstant.KEY_CODE.TAB,
|
||||
$mdConstant.KEY_CODE.COMMA,
|
||||
$mdConstant.KEY_CODE.SEMICOLON
|
||||
];
|
||||
vm.uploader = new FileUploader({
|
||||
url: stateMessage.$absolutePath({asDraft: true}) + '/save',
|
||||
autoUpload: true,
|
||||
@@ -54,6 +60,9 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Destroy file uploader when the controller is being deactivated
|
||||
$scope.$on('$destroy', function() { vm.uploader.destroy(); });
|
||||
|
||||
if ($stateParams.actionName == 'reply') {
|
||||
stateMessage.$reply().then(function(msgObject) {
|
||||
vm.message = msgObject;
|
||||
@@ -168,10 +177,13 @@
|
||||
|
||||
function send() {
|
||||
var ctrls = $parentControllers();
|
||||
|
||||
vm.sendState = 'sending';
|
||||
if (vm.autosave)
|
||||
$timeout.cancel(vm.autosave);
|
||||
|
||||
vm.message.$send().then(function(data) {
|
||||
vm.sendState = 'sent';
|
||||
if (ctrls.draftMailboxCtrl) {
|
||||
// We're sending a draft from a popup window and the draft mailbox is opened.
|
||||
// Reload draft mailbox
|
||||
@@ -192,7 +204,12 @@
|
||||
.content(l('Your email has been sent'))
|
||||
.position('top right')
|
||||
.hideDelay(3000));
|
||||
$mdDialog.hide();
|
||||
|
||||
// Let the user see the succesfull message before closing the dialog
|
||||
$timeout($mdDialog.hide, 1000);
|
||||
}, function(response) {
|
||||
vm.sendState = 'error';
|
||||
vm.errorMessage = response.data? response.data.message : response.statusText;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -15,20 +15,29 @@
|
||||
|
||||
vm.creds = { username: cookieUsername, password: null };
|
||||
vm.login = login;
|
||||
vm.loginState = false;
|
||||
vm.showAbout = showAbout;
|
||||
|
||||
// Show login once everything is initialized
|
||||
vm.showLogin = false;
|
||||
$timeout(function() { vm.showLogin = true; }, 100);
|
||||
|
||||
function login() {
|
||||
vm.loginState = 'authenticating';
|
||||
Authentication.login(vm.creds)
|
||||
.then(function(url) {
|
||||
if (window.location.href === url)
|
||||
window.location.reload(true);
|
||||
else
|
||||
window.location.href = url;
|
||||
vm.loginState = 'logged';
|
||||
|
||||
// Let the user see the succesfull message before reloading the page
|
||||
$timeout(function() {
|
||||
if (window.location.href === url)
|
||||
window.location.reload(true);
|
||||
else
|
||||
window.location.href = url;
|
||||
}, 1000);
|
||||
}, function(msg) {
|
||||
Dialog.alert(l('Authentication Failed'), msg.error);
|
||||
vm.loginState = 'error';
|
||||
vm.errorMessage = msg.error;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user