From 18fccd9d6c612e0a2ff9ddfa37f877cfa51e5c0d Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Wed, 30 Dec 2015 11:15:04 +0100 Subject: [PATCH] [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. --- addons/mail/static/src/js/chat_window.js | 23 ++-- addons/mail/static/src/js/window_manager.js | 113 ++++++++++++++++++- addons/mail/static/src/less/chat_window.less | 44 +++++++- addons/mail/static/src/xml/chat_window.xml | 44 ++++++-- 4 files changed, 202 insertions(+), 22 deletions(-) diff --git a/addons/mail/static/src/js/chat_window.js b/addons/mail/static/src/js/chat_window.js index ca36b430791..e76e2405b43 100644 --- a/addons/mail/static/src/js/chat_window.js +++ b/addons/mail/static/src/js/chat_window.js @@ -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) { diff --git a/addons/mail/static/src/js/window_manager.js b/addons/mail/static/src/js/window_manager.js index f217d929c20..7c26dc88c82 100644 --- a/addons/mail/static/src/js/window_manager.js +++ b/addons/mail/static/src/js/window_manager.js @@ -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); }); }); diff --git a/addons/mail/static/src/less/chat_window.less b/addons/mail/static/src/less/chat_window.less index ca2e8ca74f1..bd9a2452113 100644 --- a/addons/mail/static/src/less/chat_window.less +++ b/addons/mail/static/src/less/chat_window.less @@ -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; + } + } + } +} diff --git a/addons/mail/static/src/xml/chat_window.xml b/addons/mail/static/src/xml/chat_window.xml index 01e475a6eb8..58d04c19838 100644 --- a/addons/mail/static/src/xml/chat_window.xml +++ b/addons/mail/static/src/xml/chat_window.xml @@ -1,16 +1,13 @@ -
- - - - - - + + + +
@@ -20,4 +17,37 @@
+ +
+ + + + + + + +
+
+ + + + + + () + + + + + + +