[IMP] mail: put chat windows in a dropdown in case of overflow
Former algorithm to display chat windows was pretty basic: each window was positionned 5 pixels to the left of the previous one, starting from the bottom right corner of the screen. Thus, when there were too many chat windows to fit in the screen, they overflowed to the left. This rev. handles this case by hidding overflowing chat windows and making them available in a dropdown positionned 5 pixels to the left of the last visible chat window.
This commit is contained in:
@@ -29,6 +29,7 @@ return Widget.extend({
|
||||
display_stars: true,
|
||||
});
|
||||
this.unread_msgs = unread_msgs || 0;
|
||||
this.is_hidden = false;
|
||||
},
|
||||
start: function () {
|
||||
this.$content = this.$('.o_chat_content');
|
||||
@@ -50,23 +51,17 @@ return Widget.extend({
|
||||
return $.when(this._super(), def);
|
||||
},
|
||||
render: function (messages) {
|
||||
this.update_header();
|
||||
this.update_unread(this.unread_msgs);
|
||||
this.thread.render(messages, {display_load_more: false});
|
||||
},
|
||||
update_header: function () {
|
||||
var title = this.unread_msgs > 0 ?
|
||||
this.title + ' (' + this.unread_msgs + ')' : this.title;
|
||||
this.$('.o_chat_title').text(title);
|
||||
},
|
||||
update_unread: function (counter) {
|
||||
this.unread_msgs = counter;
|
||||
this.update_header();
|
||||
this.$('.o_unread_counter').text(counter > 0 ? '(' + counter + ')' : '');
|
||||
},
|
||||
scrollBottom: function () {
|
||||
this.$content.scrollTop(this.$content[0].scrollHeight);
|
||||
},
|
||||
fold: function () {
|
||||
this.update_header();
|
||||
this.$el.animate({
|
||||
height: this.folded ? HEIGHT_FOLDED : HEIGHT_OPEN
|
||||
});
|
||||
@@ -79,6 +74,18 @@ return Widget.extend({
|
||||
}
|
||||
this.fold();
|
||||
},
|
||||
do_show: function () {
|
||||
this.is_hidden = false;
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
do_hide: function () {
|
||||
this.is_hidden = true;
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
do_toggle: function (display) {
|
||||
this.is_hidden = _.isBoolean(display) ? !display : !this.is_hidden;
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
on_keydown: function (event) {
|
||||
// ENTER key (avoid requiring jquery ui for external livechat)
|
||||
if (event.which === 13) {
|
||||
|
||||
@@ -5,16 +5,26 @@ var chat_manager = require('mail.chat_manager');
|
||||
var ChatWindow = require('mail.ChatWindow');
|
||||
|
||||
var core = require('web.core');
|
||||
var utils = require('web.utils');
|
||||
var web_client = require('web.web_client');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
|
||||
// chat window management
|
||||
//----------------------------------------------------------------
|
||||
var CHAT_WINDOW_WIDTH = 300 + 5; // 5 pixels between windows
|
||||
var chat_sessions = [];
|
||||
var CHAT_WINDOW_WIDTH = 300;
|
||||
var display_state = {
|
||||
hidden_sessions: [],
|
||||
hidden_unread_counter: 0, // total number of unread msgs in hidden chat windows
|
||||
nb_slots: 0,
|
||||
space_left: 0,
|
||||
windows_dropdown_is_open: false, // used to keep dropdown open when closing chat windows
|
||||
};
|
||||
|
||||
function open_chat (session) {
|
||||
if (!_.findWhere(chat_sessions, {id: session.id})) {
|
||||
var chat_session = _.findWhere(chat_sessions, {id: session.id});
|
||||
if (!chat_session) {
|
||||
var channel = chat_manager.get_channel(session.id);
|
||||
var chat_session = {
|
||||
id: session.id,
|
||||
@@ -58,7 +68,10 @@ function open_chat (session) {
|
||||
}
|
||||
});
|
||||
|
||||
chat_sessions.push(chat_session);
|
||||
// insert chat_session such that it will be the right-most visible window
|
||||
compute_available_slots(chat_sessions.length+1);
|
||||
chat_sessions.splice(display_state.nb_slots-1, 0, chat_session);
|
||||
|
||||
chat_session.window.appendTo($('body'))
|
||||
.then(reposition_windows)
|
||||
.then(function () {
|
||||
@@ -67,6 +80,8 @@ function open_chat (session) {
|
||||
chat_session.window.render(messages);
|
||||
chat_session.window.scrollBottom();
|
||||
});
|
||||
} else if (chat_session.window.is_hidden) {
|
||||
make_session_visible(chat_session);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,16 +101,92 @@ function toggle_fold_chat (channel) {
|
||||
}
|
||||
}
|
||||
|
||||
function reposition_windows () {
|
||||
function compute_available_slots (nb_windows) {
|
||||
var width = window.innerWidth;
|
||||
var nb_slots = Math.floor(width/CHAT_WINDOW_WIDTH);
|
||||
var space_left = width - (Math.min(nb_slots, nb_windows)*CHAT_WINDOW_WIDTH);
|
||||
if (nb_slots < nb_windows && space_left < 50) {
|
||||
nb_slots--; // leave at least 50px for the hidden windows dropdown button
|
||||
space_left += CHAT_WINDOW_WIDTH;
|
||||
}
|
||||
display_state.nb_slots = nb_slots;
|
||||
display_state.space_left = space_left;
|
||||
}
|
||||
|
||||
var reposition_windows = _.debounce(function () {
|
||||
compute_available_slots(chat_sessions.length);
|
||||
var hidden_sessions = [];
|
||||
var hidden_unread_counter = 0;
|
||||
var nb_slots = display_state.nb_slots;
|
||||
_.each(chat_sessions, function (session, index) {
|
||||
session.window.$el.css({right: (CHAT_WINDOW_WIDTH + 5) * index, bottom: 0});
|
||||
if (index < nb_slots) {
|
||||
session.window.$el.css({right: CHAT_WINDOW_WIDTH*index, bottom: 0});
|
||||
session.window.do_show();
|
||||
} else {
|
||||
hidden_sessions.push(session);
|
||||
hidden_unread_counter += session.window.unread_msgs;
|
||||
session.window.do_hide();
|
||||
}
|
||||
});
|
||||
display_state.hidden_sessions = hidden_sessions;
|
||||
display_state.hidden_unread_counter = hidden_unread_counter;
|
||||
|
||||
if (display_state.$hidden_windows_dropdown) {
|
||||
display_state.$hidden_windows_dropdown.remove();
|
||||
}
|
||||
if (hidden_sessions.length) {
|
||||
display_state.$hidden_windows_dropdown = render_hidden_sessions_dropdown();
|
||||
var $hidden_windows_dropdown = display_state.$hidden_windows_dropdown;
|
||||
$hidden_windows_dropdown.css({right: CHAT_WINDOW_WIDTH * nb_slots, bottom: 0})
|
||||
.appendTo($('body'));
|
||||
reposition_hidden_sessions_dropdown();
|
||||
display_state.windows_dropdown_is_open = false;
|
||||
|
||||
$hidden_windows_dropdown.on('click', '.o_chat_header', function (event) {
|
||||
var session_id = $(event.currentTarget).data('session-id');
|
||||
var session = _.findWhere(hidden_sessions, {id: session_id});
|
||||
if (session) {
|
||||
make_session_visible(session);
|
||||
}
|
||||
});
|
||||
$hidden_windows_dropdown.on('click', '.o_chat_window_close', function (event) {
|
||||
var session_id = $(event.currentTarget).closest('.o_chat_header').data('session-id');
|
||||
var session = _.findWhere(hidden_sessions, {id: session_id});
|
||||
if (session) {
|
||||
session.window.on_click_close(event);
|
||||
display_state.windows_dropdown_is_open = true; // keep the dropdown open
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 100);
|
||||
|
||||
function make_session_visible (session) {
|
||||
utils.swap(chat_sessions, session, chat_sessions[display_state.nb_slots-1]);
|
||||
session.window.toggle_fold(false);
|
||||
reposition_windows();
|
||||
}
|
||||
|
||||
function render_hidden_sessions_dropdown () {
|
||||
var $dropdown = $(QWeb.render("mail.ChatWindowsDropdown", {
|
||||
sessions: display_state.hidden_sessions,
|
||||
open: display_state.windows_dropdown_is_open,
|
||||
unread_counter: display_state.hidden_unread_counter,
|
||||
}));
|
||||
return $dropdown;
|
||||
}
|
||||
|
||||
function reposition_hidden_sessions_dropdown () {
|
||||
// Unfold dropdown to the left if there is enough place
|
||||
var $dropdown_ul = display_state.$hidden_windows_dropdown.children('ul');
|
||||
if (display_state.space_left > $dropdown_ul.width() + 10) {
|
||||
$dropdown_ul.addClass('dropdown-menu-right');
|
||||
}
|
||||
}
|
||||
|
||||
function update_sessions (message, scrollBottom) {
|
||||
_.each(chat_sessions, function (session) {
|
||||
if (_.contains(message.channel_ids, session.id)) {
|
||||
if (!session.window.folded) {
|
||||
if (!session.window.folded && !session.window.is_hidden) {
|
||||
chat_manager.mark_channel_as_seen(chat_manager.get_channel(session.id));
|
||||
}
|
||||
chat_manager.get_messages({channel_id: session.id}).then(function (messages) {
|
||||
@@ -139,11 +230,19 @@ core.bus.on('web_client_ready', null, function () {
|
||||
});
|
||||
|
||||
chat_manager.bus.on('update_channel_unread_counter', null, function (channel) {
|
||||
display_state.hidden_unread_counter = 0;
|
||||
_.each(chat_sessions, function (session) {
|
||||
if (channel.id === session.id) {
|
||||
session.window.update_unread(channel.unread_counter);
|
||||
}
|
||||
if (session.window.is_hidden) {
|
||||
display_state.hidden_unread_counter += session.window.unread_msgs;
|
||||
}
|
||||
});
|
||||
if (display_state.$hidden_windows_dropdown) {
|
||||
display_state.$hidden_windows_dropdown.html(render_hidden_sessions_dropdown().html());
|
||||
reposition_hidden_sessions_dropdown();
|
||||
}
|
||||
});
|
||||
|
||||
chat_manager.is_ready.then(function() {
|
||||
@@ -153,6 +252,8 @@ core.bus.on('web_client_ready', null, function () {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
core.bus.on('resize', null, reposition_windows);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
@o-chat-window-bg: white;
|
||||
@o-chat-window-width: 300px;
|
||||
|
||||
.o_chat_window {
|
||||
.o-flex-display();
|
||||
.o-flex-flow(column, nowrap);
|
||||
position: fixed;
|
||||
width: 300px;
|
||||
width: @o-chat-window-width;
|
||||
max-width: 100%;
|
||||
height: 400px;
|
||||
max-height: 100%;
|
||||
@@ -28,11 +29,13 @@
|
||||
.o_chat_title {
|
||||
cursor: pointer;
|
||||
.o-flex(1, 1, auto);
|
||||
.o-text-overflow();
|
||||
}
|
||||
|
||||
.o_chat_window_close {
|
||||
color: white;
|
||||
padding: 3px;
|
||||
margin-left: 5px;
|
||||
&:hover {
|
||||
background-color: darken(@odoo-brand-primary, 10%);
|
||||
}
|
||||
@@ -67,3 +70,42 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_chat_window_dropdown {
|
||||
width: auto;
|
||||
height: 28px;
|
||||
color: white;
|
||||
background-color: @odoo-brand-primary;
|
||||
cursor: pointer;
|
||||
|
||||
.o_chat_window_dropdown_toggler {
|
||||
padding: 5px;
|
||||
|
||||
.o_total_unread_counter {
|
||||
.o-position-absolute(-10px, 0, auto, auto);
|
||||
background-color: @odoo-brand-optional;
|
||||
padding: 0 2px;
|
||||
font-size: smaller;
|
||||
}
|
||||
}
|
||||
&.open .o_chat_window_dropdown_toggler .o_total_unread_counter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> ul {
|
||||
max-width: @o-chat-window-width;
|
||||
padding: 0;
|
||||
|
||||
> li.o_chat_header {
|
||||
font-size: 12px;
|
||||
padding: 3px 5px;
|
||||
&~li.o_chat_header {
|
||||
border-top: 1px solid white;
|
||||
}
|
||||
&:hover {
|
||||
background-color: darken(@odoo-brand-primary, 10%);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
|
||||
<t t-name="mail.ChatWindow">
|
||||
<div class="o_chat_window">
|
||||
<div class="o_chat_header">
|
||||
<span class="o_chat_title">
|
||||
<t t-esc="widget.title"/>
|
||||
</span>
|
||||
<span>
|
||||
<a href="#" class="o_chat_window_close fa fa-close"></a>
|
||||
</span>
|
||||
<t t-call="mail.ChatWindowHeaderContent">
|
||||
<t t-set="title" t-value="widget.title"/>
|
||||
<t t-set="unread_counter" t-value="widget.unread_msgs"/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="o_chat_content">
|
||||
</div>
|
||||
@@ -20,4 +17,37 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="mail.ChatWindowsDropdown">
|
||||
<div t-attf-class="o_chat_window o_chat_window_dropdown dropup #{open ? 'open' : ''}">
|
||||
<span class="o_chat_window_dropdown_toggler dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-comments-o"/> <t t-esc="sessions.length"/>
|
||||
<span class="o_total_unread_counter">
|
||||
<t t-if="unread_counter"><t t-esc="unread_counter"/></t>
|
||||
</span>
|
||||
</span>
|
||||
<ul class="dropdown-menu">
|
||||
<t t-foreach="sessions" t-as="session">
|
||||
<li class="o_chat_header" t-att-data-session-id="session.id">
|
||||
<t t-call="mail.ChatWindowHeaderContent">
|
||||
<t t-set="title" t-value="session.name"/>
|
||||
<t t-set="unread_counter" t-value="session.window.unread_msgs"/>
|
||||
</t>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="mail.ChatWindowHeaderContent">
|
||||
<span class="o_chat_title">
|
||||
<t t-esc="title"/>
|
||||
<span class="o_unread_counter">
|
||||
<t t-if="unread_counter">(<t t-esc="unread_counter"/>)</t>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
<a href="#" class="o_chat_window_close fa fa-close"/>
|
||||
</span>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
||||
Reference in New Issue
Block a user