[FIX] mail, web: chat window and emojis

- Fix several contexts where the emoji popover was hidden by a webclient
  element (control panel / ...). To solve this bug, the design has been
  improved to not use popover but instead use custom design (better
  integrated with new chatter design)

- Chat window is now still usable when a modal is opened

- Simplify chatter LESS
This commit is contained in:
qsm-odoo
2017-09-20 15:03:56 +02:00
parent 26e079f246
commit 7b965a9673
6 changed files with 111 additions and 67 deletions
+68 -26
View File
@@ -358,6 +358,10 @@ var BasicComposer = Widget.extend(chat_mixin, {
"click .o_attachment_delete": "on_attachment_delete",
"click .o_attachment_download": "_onAttachmentDownload",
"click .o_attachment_view": "_onAttachmentView",
'click .o_composer_button_emoji': '_onEmojiButtonClick',
'focusout .o_composer_button_emoji': '_onEmojiButtonFocusout',
'focus .o_mail_emoji_container .o_mail_emoji': '_onEmojiImageFocus',
'click .o_mail_emoji_container .o_mail_emoji': '_onEmojiImageClick',
},
// RPCs done to fetch the mention suggestions are throttled with the following value
MENTION_THROTTLE: 200,
@@ -420,9 +424,6 @@ var BasicComposer = Widget.extend(chat_mixin, {
});
}
// Emojis
this.emoji_container_classname = 'o_composer_emoji';
this.isMini = options.isMini;
this.avatarURL = session.uid > 0 ? session.url('/web/image', {
@@ -448,23 +449,6 @@ var BasicComposer = Widget.extend(chat_mixin, {
$(window).on(this.fileupload_id, this.on_attachment_loaded);
this.on("change:attachment_ids", this, this.render_attachments);
// Emoji
this.$('.o_composer_button_emoji').popover({
placement: 'top',
content: function() {
if (!self.$emojis) { // lazy rendering
self.$emojis = $(QWeb.render('mail.ChatComposer.emojis', {
emojis: self._getEmojis(),
}));
self.$emojis.filter('.o_mail_emoji').on('click', self, self.on_click_emoji_img);
}
return self.$emojis;
},
html: true,
container: '.' + self.emoji_container_classname,
trigger: 'focus',
});
// Mention
this.mention_manager.prependTo(this.$('.o_composer'));
@@ -529,11 +513,6 @@ var BasicComposer = Widget.extend(chat_mixin, {
this.$input.focus();
},
on_click_emoji_img: function(event) {
this.$input.val(this.$input.val() + " " + $(event.currentTarget).data('emoji') + " ");
this.$input.focus();
},
setState: function (state) {
this.set('attachment_ids', state.attachments);
this.$input.val(state.text);
@@ -775,6 +754,20 @@ var BasicComposer = Widget.extend(chat_mixin, {
focus: function () {
this.$input.focus();
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Hides the emojis container.
*
* @private
*/
_hideEmojis: function () {
this.$emojisContainer.remove();
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
@@ -798,6 +791,56 @@ var BasicComposer = Widget.extend(chat_mixin, {
attachmentViewer.appendTo($('body'));
}
},
/**
* Called when the emoji button is clicked -> opens/hides the emoji panel.
* Also, this method is in charge of the rendering of this panel the first
* time it is opened.
*
* @private
*/
_onEmojiButtonClick: function () {
if (!this.$emojisContainer) { // lazy rendering
this.$emojisContainer = $(QWeb.render('mail.ChatComposer.emojis', {
emojis: this._getEmojis(),
}));
}
if (this.$emojisContainer.parent().length) {
this._hideEmojis();
} else {
this.$emojisContainer.appendTo(this.$('.o_composer'));
}
},
/**
* Called when the emoji button is blurred -> closes the emoji panel. The
* closing is scheduled to be done at the end of the current execution
* stack to allow stoping this closing if the button was focusout to select
* an emoji (for example).
*
* @private
*/
_onEmojiButtonFocusout: function () {
this._hideEmojisTimeout = setTimeout(this._hideEmojis.bind(this), 0);
},
/**
* Called when an emoji is focused -> @see _onEmojiButtonFocusout
*
* @private
*/
_onEmojiImageFocus: function () {
clearTimeout(this._hideEmojisTimeout);
},
/**
* Called when an emoji is clicked -> adds it in the <input/>, focuses the
* <input/> and closes the emoji panel.
*
* @private
* @param {Event} ev
*/
_onEmojiImageClick: function (ev) {
this.$input.val(this.$input.val() + " " + $(ev.currentTarget).data('emoji') + " ");
this.$input.focus();
this._hideEmojis();
},
});
var ExtendedComposer = BasicComposer.extend({
@@ -807,7 +850,6 @@ var ExtendedComposer = BasicComposer.extend({
});
this._super(parent, options);
this.extended = true;
this.emoji_container_classname = 'o_extended_composer_emoji';
},
start: function () {
+2 -4
View File
@@ -3,6 +3,7 @@
@o-mail-chatter-gap: 10px; // has to be defined here as this file is both in
// backend and other assets (livechat)
@o-chat-header-height: 46px;
@o-chat-window-zindex: 1101; // blockUI's z-index is 1100 > @zindex-modal
.o_chat_window {
.o-flex-display();
@@ -15,7 +16,7 @@
font-size: 12px;
background-color: @o-chat-window-bg;
border-radius: 6px 6px 0 0;
z-index: 3;
z-index: @o-chat-window-zindex;
box-shadow: -5px -5px 10px fade(black, 18%);
@media (max-width: @screen-xs-max) {
@@ -143,9 +144,6 @@
}
}
.o_ui_blocked .o_chat_window {
z-index: 1101; // blockUI's z-index is 1100
}
.o_no_chat_window .o_chat_window {
display: none;
}
+31 -20
View File
@@ -2,7 +2,6 @@
position: relative;
.o-flex(0, 0, auto);
.o-flex-display();
.o-align-items(flex-start);
margin: 1px 0;
border-top: none;
background: @gray-lighter;
@@ -59,6 +58,18 @@
color: @odoo-brand-optional;
}
}
.o_mail_emoji_container {
padding: 0 @o-mail-chatter-gap @o-mail-chatter-gap 0;
border: 1px solid @gray-lighter-dark;
border-width: 1px 0 0 0;
background-color: @gray-lighter;
.o_mail_emoji {
margin: @o-mail-chatter-gap 0 0 @o-mail-chatter-gap;
cursor: pointer; // only in the popover
}
}
}
// Both inline and not-inline design. Mini-composer disabled.
@@ -72,10 +83,6 @@
.o-position-absolute(0, @left: -@mail-thread-avatar-size - @o-mail-chatter-gap);
}
.o_composer_container, .o_composer_send {
.o-flex-display();
}
.o_chatter_composer_info, .o_composer_suggested_partners {
.o-flex(0, 0, 100%);
margin-bottom: @o-mail-chatter-gap*0.5;
@@ -83,7 +90,6 @@
.o_composer_container {
position: relative;
.o-flex-flow(column, nowrap);
}
.o_composer {
@@ -91,6 +97,10 @@
textarea {
padding-right: @grid-gutter-width; // avoid overlaps betwenn textarea and o_composer_button_full_composer
}
.o_mail_emoji_container {
margin: 0 -@o-mail-chatter-gap;
}
}
.o_composer, .o_composer_subject {
@@ -129,9 +139,14 @@
.o-flex(1, 0, auto);
}
.o_composer {
padding: @o-mail-chatter-gap @o-mail-chatter-gap*0.5 0 @o-mail-chatter-gap;
padding: @o-mail-chatter-gap @o-mail-chatter-gap 0 @o-mail-chatter-gap;
border-radius: 2px 0 0 2px;
border-right: 0;
border-right-width: 0;
.o_mail_emoji_container {
text-align: right;
border-right-width: 1px;
}
}
.o_composer_input {
.o-flex-display();
@@ -157,7 +172,6 @@
}
.o_composer_button_send {
.o-flex(0, 0, auto);
border-radius: 0 3px 3px 0;
// Make the button appear as the same size as the subject input
padding: 10px @padding-base-horizontal;
@@ -173,6 +187,12 @@
border-top: 1px solid @gray-lighter-dark;
width: 100%;
}
.o_composer {
.o_mail_emoji_container {
.o-position-absolute(auto, 0, 100%, 0);
border-bottom-width: 1px;
}
}
.o_composer_input {
.o-flex-display();
.o-flex-flow(row, nowrap);
@@ -206,22 +226,13 @@
display: inline-block;
height: 20px;
width: 20px;
padding: 0;
> img {
width: 100%;
}
}
.o_composer_emoji {
.popover-content {
padding: @o-mail-chatter-gap*0.3 0;
}
.o_mail_emoji {
margin: @o-mail-chatter-gap*0.6;
cursor: pointer; // only in the popover
}
}
// Attachment Icons (common for chat thread and chat composer)
// ------------------------------------------------------------------
@-webkit-keyframes oe_mail_attach_loading_anim {
@@ -391,7 +402,7 @@
.arrow-left {
left: 0;
}
.arrow-right {
right: 0;
}
+4 -5
View File
@@ -32,16 +32,15 @@
<input type="hidden" name="session_id" t-att-value="widget.getSession().session_id"/>
</t>
</span>
<span t-att-class="widget.emoji_container_classname"></span>
</div>
<t t-name="mail.ChatComposer.emojis">
<div t-name="mail.ChatComposer.emojis" class="o_mail_emoji_container">
<t t-foreach="emojis" t-as="emoji">
<span t-att-data-emoji="emoji.source" class="o_mail_emoji" t-att-title="emoji.description">
<button t-att-data-emoji="emoji.source" class="btn btn-link o_mail_emoji" t-att-title="emoji.description">
<t t-raw="emoji.substitution"/>
</span>
</button>
</t>
</t>
</div>
<t t-name="mail.AbstractMentionSuggestions">
<ul class="dropdown-menu" aria-labelledby="dropdownMailMentionMenu">
+3 -7
View File
@@ -106,16 +106,12 @@ QUnit.module('mail', {}, function () {
var chatWindow = new ChatWindow(parent, 1, "user", false, [], {});
chatWindow.appendTo($dom);
// Simulate clicking: 1) the focus event is fired, 2) we remove the
// 'fade' class of the generated popover so that animations do not break
// the test, 3) fire the click event which was the cause of the problem
var $emojiButton = chatWindow.$('.o_composer_button_emoji');
$emojiButton.trigger('focusin').focus();
var $popover = chatWindow.$('.o_composer_emoji .popover');
$popover.removeClass('fade');
$emojiButton.click();
$emojiButton.trigger('focusin').focus().click();
var $popover = chatWindow.$('.o_mail_emoji_container');
var done = assert.async();
// Async is needed as the popover focusout hiding is deferred
setTimeout(function () {
assert.ok($popover.is(':visible'), "emoji popover should have stayed opened");
parent.destroy();
+3 -5
View File
@@ -54,19 +54,17 @@ if ($.blockUI) {
var throbbers = [];
function blockUI () {
function blockUI() {
var tmp = $.blockUI.apply($, arguments);
var throbber = new Throbber();
throbbers.push(throbber);
throbber.appendTo($(".oe_blockui_spin_container"));
$('body').addClass('o_ui_blocked');
return tmp;
}
function unblockUI () {
function unblockUI() {
_.invoke(throbbers, 'destroy');
throbbers = [];
$('body').removeClass('o_ui_blocked');
return $.unblockUI.apply($, arguments);
}
@@ -104,7 +102,7 @@ function redirect (url, wait) {
// * Client action to reload the whole interface.
// * If params.menu_id, it opens the given menu entry.
// * If params.wait, reload will wait the openerp server to be reachable before reloading
function Reload(parent, action) {
var params = action.params || {};
var menu_id = params.menu_id || false;