From f23f57ccf456028bf85982a2c2f9b966b71dcab5 Mon Sep 17 00:00:00 2001
From: Aaron Bohy <aab@odoo.com>
Date: Mon, 5 Oct 2015 11:50:04 +0200
Subject: [PATCH] [FIX] im_livechat: livechat restored after refactoring of
 mail

Refactoring of the livechat itself and integration in the new architecture of
mail since its large refactoring.

Joint work with ged
---
 .../im_livechat/static/src/js/im_livechat.js  | 392 ++++++++----------
 .../static/src/js/im_livechat_backend.js      |  54 ---
 .../static/src/less/im_livechat.less          |  91 ++--
 .../static/src/xml/im_livechat.xml            |  10 +-
 .../static/src/xml/im_livechat_backend.xml    |  22 +-
 5 files changed, 216 insertions(+), 353 deletions(-)

diff --git a/addons/im_livechat/static/src/js/im_livechat.js b/addons/im_livechat/static/src/js/im_livechat.js
index 40a712e68df4..f9f72b930750 100644
--- a/addons/im_livechat/static/src/js/im_livechat.js
+++ b/addons/im_livechat/static/src/js/im_livechat.js
@@ -1,292 +1,236 @@
 odoo.define('im_livechat.im_livechat', function (require) {
 "use strict";
 
-var bus = require('bus.bus');
+var bus = require('bus.bus').bus;
 var core = require('web.core');
-var user_session = require('web.session');
+var session = require('web.session');
 var time = require('web.time');
 var utils = require('web.utils');
 var Widget = require('web.Widget');
-// var mail_chat_common = require('mail.chat_common');
+
+var ChatWindow = require('mail.ChatWindow');
 
 var _t = core._t;
 var QWeb = core.qweb;
 
-return {
-    LivechatButton: Widget,
-};
-
-/*
- * Conversation Patch
- *
- * The state of anonymous session is hold by the client and not the
- * server. Override the method managing the state of normal conversation.
-*/
-mail_chat_common.Conversation.include({
-    init: function(parent, session, options){
-        this._super.apply(this, arguments);
-        this.shown = true;
-        this.feedback = false;
-        this.options.default_username = session.anonymous_name || this.options.default_username;
-    },
-    show: function(){
-        this._super.apply(this, arguments);
-        this.shown = true;
-    },
-    hide: function(){
-        this._super.apply(this, arguments);
-        this.shown = false;
-    },
-    session_fold: function(state){
-        // set manually the new state
-        if(state === 'closed'){
-            this.destroy();
-            utils.set_cookie('im_livechat_session', "", -1);
-        }else{
-            if(state === 'open'){
-                this.show();
-            }else{
-                if(this.shown){
-                    state = 'fold';
-                    this.hide();
-                }else{
-                    state = 'open';
-                    this.show();
-                }
-            }
-            this._super(state);
-            // session and cookie update
-            utils.set_cookie('im_livechat_session', JSON.stringify(this.get('session')), 60*60);
-        }
-    },
-    on_click_close: function(event) {
-        var self = this;
-        event.stopPropagation();
-        if(!this.feedback && (this.get('messages').length > 1)){
-            this.feedback = new Feedback(this);
-            this.$(".o_mail_chat_im_window_content").empty();
-            this.$(".o_mail_chat_im_window_input").prop('disabled', true);
-            this.feedback.appendTo(this.$(".o_mail_chat_im_window_content"));
-            // bind event to close conversation
-            var args = arguments;
-            var _super = this._super;
-            this.feedback.on("feedback_sent", this, function(){
-                _super.apply(self, args);
-            });
-        }else{
-            this._super.apply(this, arguments);
-        }
-    },
-});
-
-/*
- * Livechat Conversation Manager
- *
- * To avoid exeption when the anonymous has close his conversation and when operator send
- * him a message. Extending ConversationManager is better than monkey-patching it.
- */
-var LivechatConversationManager = mail_chat_common.ConversationManager.extend({
-    message_receive: function(message) {
-        try{
-            this._super(message);
-        }catch(e){}
-    }
-});
-
-/**
- * Livechat Button
- *
- * This widget is the Button to start a conversation with an operator on the livechat channel
- * (defined in options.channel_id). This will auto-popup if the rule say so, create the conversation
- * window if a session is saved in a cookie, ...
- */
 var LivechatButton = Widget.extend({
-    template: 'im_livechat.LivechatButton',
+    className:"openerp o_livechat_button hidden-print",
+
     events: {
-        "click": "on_click"
+        "click": "open_chat"
     },
-    init: function(parent, server_url, options, rule) {
-        this._super.apply(this, arguments);
-        this.server_url = server_url;
+
+    init: function (parent, server_url, options) {
+        this._super(parent);
         this.options = _.defaults(options || {}, {
             placeholder: _t('Ask something ...'),
             default_username: _t("Visitor"),
             button_text: _t("Chat with one of our collaborators"),
             default_message: _t("How may I help you?"),
         });
-        this.rule = rule || false;
-        this.mail_channel = false;
+        this.channel = null;
+        this.chat_window = null;
+        this.messages = [];
     },
-    willStart: function(){
-        return $.when(this.load_qweb_template(), this._super());
+
+    willStart: function () {
+        return this.load_qweb_template();
     },
-    start: function(){
-        var self = this;
-        return this._super.apply(this, arguments).then(function(){
-            // set up the manager
-            self.manager = new LivechatConversationManager(self, self.options);
-            self.manager.set("bottom_offset", self.$el.outerHeight());
-            // check if a session already exists in a cookie
-            var cookie = utils.get_cookie('im_livechat_session');
-            if(cookie){
-                self.set_conversation(JSON.parse(cookie), false);
-            }else{ // if not session, apply the rule
-                var auto_popup_cookie = utils.get_cookie('im_livechat_auto_popup') ? JSON.parse(utils.get_cookie('im_livechat_auto_popup')) : true;
-                self.auto_popup_cookie = auto_popup_cookie;
-                if (self.rule.action === 'auto_popup' && auto_popup_cookie){
-                    setTimeout(function() {
-                        self.on_click();
-                    }, self.rule.auto_popup_timer*1000);
-                }
-            }
+
+    start: function () {
+        this.$el.text(this.options.button_text);
+        bus.on('notification', this, function (notification) {
+            this.add_message(notification[1]);
+            this.render_messages();
         });
+        return this._super();
     },
-    load_qweb_template: function(){
-        var self = this;
-        var defs = [];
-        var templates = ['/mail/static/src/xml/mail_chat_common.xml', '/im_livechat/static/src/xml/im_livechat.xml'];
-        _.each(templates, function(tmpl){
-            defs.push(user_session.rpc('/web/proxy/load', {path: tmpl}).then(function(xml) {
+
+    load_qweb_template: function () {
+        var xml_files = ['/mail/static/src/xml/chat_window.xml',
+                         '/mail/static/src/xml/thread.xml',
+                         '/im_livechat/static/src/xml/im_livechat.xml'];
+        var defs = _.map(xml_files, function (tmpl) {
+            return session.rpc('/web/proxy/load', {path: tmpl}).then(function (xml) {
                 QWeb.add_template(xml);
-            }));
+            });
         });
         return $.when.apply($, defs);
     },
-    load_mail_channel: function(display_warning){
+
+    open_chat: function () {
         var self = this;
-        return user_session.rpc('/im_livechat/get_session', {
-            "channel_id" : this.options.channel_id,
-            "anonymous_name" : this.options.default_username,
-        }, {shadow: true}).then(function(channel){
-            if(channel){
-                self.set_conversation(channel, true);
-            }else{
-                if(display_warning){
-                    self.alert_available_message();
-                }
+        var cookie = utils.get_cookie('im_livechat_session');
+        var def;
+        if (cookie) {
+            def = $.when(JSON.parse(cookie));
+        } else {
+            this.messages = []; // re-initialize messages cache
+            def = session.rpc('/im_livechat/get_session', {
+                channel_id : this.options.channel_id,
+                anonymous_name : this.options.default_username,
+            }, {shadow: true});
+        }
+        def.then(function (channel) {
+            if (!channel || !channel.operator_pid) {
+                alert(_t("None of our collaborators seems to be available, please try again later."));
+            } else {
+                self.channel = channel;
+                self.open_chat_window(channel);
+                self.send_welcome_message();
+                self.render_messages();
+
+                bus.add_channel(channel.uuid);
+                bus.restart_poll();
+
+                utils.set_cookie('im_livechat_session', JSON.stringify(channel), 60*60);
             }
         });
     },
-    on_click: function(event){
-        var self = this;
-        if(!this.mail_channel){
-            this.load_mail_channel(event !== undefined);
-        }
-    },
-    set_conversation: function(mail_channel, welcome_message){
+
+    open_chat_window: function (channel) {
         var self = this;
-        // set the mail_channel and create the Conversation Window
-        this.mail_channel = mail_channel;
-        // force open conversation window
-        if(mail_channel['state'] == 'closed'){
-            mail_channel['state'] = 'open';
-        }
+        var options = {
+            display_stars: false,
+        };
+        this.chat_window = new ChatWindow(this, channel.id, channel.name, false, options);
+        this.chat_window.appendTo($('body')).then(function () {
+            self.chat_window.$el.css({right: 0, bottom: 0});
+            self.$el.hide();
+        });
+        this.chat_window.on("close_chat_session", this, function () {
+            if (this.messages.length > 1) {
+                this.ask_feedback();
+            } else {
+                this.close_chat();
+            }
+        });
+        this.chat_window.on("post_message", this, function (message) {
+            self.send_message(message).fail(function (error, e) {
+                e.preventDefault();
+                return self.send_message(message); // try again just in case
+            });
 
-        this.conv = this.manager.session_apply(mail_channel, {
-            'load_history': !welcome_message,
-            'focus': welcome_message
         });
-        this.conv.on("destroyed", this, function() {
-            bus.bus.stop_polling();
-            delete self.conv;
-            delete self.mail_channel;
+    },
+
+    close_chat: function () {
+        this.chat_window.destroy();
+        this.$el.show();
+        utils.set_cookie('im_livechat_session', "", -1); // remove cookie
+    },
+
+    send_message: function (message) {
+        return session.rpc("/mail/chat_post", {uuid: this.channel.uuid, message_content: message.content});
+    },
+
+    add_message: function (data) {
+        this.messages.push({
+            id: data.id,
+            attachment_ids: data.attachment_ids,
+            author_id: data.author_id,
+            body: data.body,
+            date: data.date,
+            is_needaction: false,
+            is_note: data.is_note,
         });
-        // setup complet when the conversation window is appended
-        this.manager._session_defs[mail_channel.id].then(function(){
-            // start the polling
-            bus.bus.add_channel(mail_channel.uuid);
-            bus.bus.start_polling();
-            // add the automatic welcome message
-            if(welcome_message){
-                self.send_welcome_message();
-            }
-            // create the cookies
-            utils.set_cookie('im_livechat_auto_popup', JSON.stringify(false), 60*60);
-            utils.set_cookie('im_livechat_session', JSON.stringify(mail_channel), 60*60);
-        })
     },
-    send_welcome_message: function(){
-        var self = this;
-        if(this.mail_channel.operator_pid && this.options.default_message) {
-            setTimeout(function(){
-                self.conv.message_receive({
-                    id : 1,
-                    message_type: "comment",
-                    model: 'mail.channel',
-                    body: self.options.default_message,
-                    date: time.datetime_to_str(new Date()),
-                    author_id: self.mail_channel.operator_pid,
-                    channel_ids: [self.mail_channel.id],
-                    tracking_value_ids: [],
-                    attachment_ids: [],
-                });
-            }, 1000);
-        }
+
+    render_messages: function () {
+        this.chat_window.render(this.messages);
+        this.chat_window.scrollBottom();
     },
-    alert_available_message: function(){
-        alert(_t("None of our collaborators seems to be available, please try again later."));
+
+    send_welcome_message: function () {
+        this.add_message({
+            id: 1,
+            attachment_ids: [],
+            author_id: this.channel.operator_pid,
+            body: this.options.default_message,
+            channel_ids: [this.channel.id],
+            date: time.datetime_to_str(new Date()),
+            tracking_value_ids: [],
+        });
     },
-})
+
+    ask_feedback: function () {
+        this.chat_window.$(".o_chat_content").empty();
+        this.chat_window.$(".o_chat_input input").prop('disabled', true);
+
+        var feedback = new Feedback(this, this.channel.uuid);
+        feedback.appendTo(this.chat_window.$(".o_chat_content"));
+
+        feedback.on("send_message", this, this.send_message);
+        feedback.on("feedback_sent", this, this.close_chat);
+    }
+});
 
 /*
  * Rating for Livechat
  *
- * This widget display the 3 rating smileys, and a textarea to add a reason (only for red
- * smileys), and sent the user feedback to the server.
+ * This widget displays the 3 rating smileys, and a textarea to add a reason
+ * (only for red smiley), and sends the user feedback to the server.
  */
 var Feedback = Widget.extend({
-    template : "im_livechat.feedback",
-    init: function(parent){
-        this._super(parent);
-        this.conversation = parent;
-        this.reason = false;
-        this.rating = false;
-        this.server_origin = user_session.origin;
+    template: "im_livechat.FeedBack",
+
+    events: {
+        'click .o_livechat_rating_choices img': 'on_click_smiley',
+        'click .o_rating_submit_button': 'on_click_send',
     },
-    start: function(){
-        this._super.apply(this.arguments);
-        // bind events
-        this.$('.o_livechat_rating_choices img').on('click', _.bind(this.click_smiley, this));
-        this.$('#rating_submit').on('click', _.bind(this.click_send, this));
+
+    init: function (parent, channel_uuid) {
+        this._super(parent);
+        this.channel_uuid = channel_uuid;
+        this.server_origin = session.origin;
+        this.rating = undefined;
     },
-    click_smiley: function(ev){
-        var self = this;
+
+    on_click_smiley: function (ev) {
         this.rating = parseInt($(ev.currentTarget).data('value'));
         this.$('.o_livechat_rating_choices img').removeClass('selected');
         this.$('.o_livechat_rating_choices img[data-value="'+this.rating+'"]').addClass('selected');
+
         // only display textearea if bad smiley selected
-        var close_conv = false;
-        if(this.rating === 0){
+        var close_chat = false;
+        if (this.rating === 0) {
             this.$('.o_livechat_rating_reason').show();
-        }else{
+        } else {
             this.$('.o_livechat_rating_reason').hide();
-            close_conv = true;
+            close_chat = true;
         }
-        this._send_feedback(close_conv).then(function(){
-            self.$('textarea').val(''); // empty the reason each time a click on a smiley is done
-        });
+        this._send_feedback({close: close_chat});
     },
-    click_send: function(ev){
-        this.reason = this.$('textarea').val();
-        if(_.contains([0,5,10], this.rating)){ // need to use contains, since the rating can 0, evaluate to false
-            this._send_feedback(true);
+
+    on_click_send: function () {
+        if (_.isNumber(this.rating)) {
+            this._send_feedback({ reason: this.$('textarea').val(), close: true });
         }
     },
-    _send_feedback: function(close){
+
+    _send_feedback: function (options) {
         var self = this;
-        var uuid = this.conversation.get('session').uuid;
-        return user_session.rpc('/im_livechat/feedback', {uuid: uuid, rate: this.rating, reason : this.reason}).then(function(res) {
-            if(close){
-                self.trigger("feedback_sent"); // will close the conversation
-                    self.conversation.message_send(_.str.sprintf(_t("I rated you with :rating_%d"), self.rating), "message");
+        var args = {
+            uuid: this.channel_uuid,
+            rate: this.rating,
+            reason : options.reason
+        };
+        return session.rpc('/im_livechat/feedback', args).then(function () {
+            if (options.close) {
+                var content = _.str.sprintf(_t("I rated you with :rating_%d"), self.rating);
+                if (options.reason) {
+                    content += _.str.sprintf(_t(" for the following reason: %s"), options.reason);
+                }
+                self.trigger("send_message", {content: content});
+                self.trigger("feedback_sent"); // will close the chat
             }
         });
     }
 });
 
 return {
-    Feedback: Feedback,
     LivechatButton: LivechatButton,
+    Feedback: Feedback,
 };
 
 });
diff --git a/addons/im_livechat/static/src/js/im_livechat_backend.js b/addons/im_livechat/static/src/js/im_livechat_backend.js
index 646d1b8c7df6..e69de29bb2d1 100644
--- a/addons/im_livechat/static/src/js/im_livechat_backend.js
+++ b/addons/im_livechat/static/src/js/im_livechat_backend.js
@@ -1,54 +0,0 @@
-odoo.define('im_livechat.chat_backend', function (require) {
-"use strict";
-
-// var mail_chat_backend = require('mail.chat_backend');
-return;
-
-
-/**
- * Patch for the client action to integrate Livechat Mail Channel in a particular slot
- */
-mail_chat_backend.ChatMailThread.include({
-    init: function(parent, action){
-        this._super.apply(this, arguments);
-        this.set('channel_livechat', []);
-    },
-    start: function(){
-        var self = this;
-        this.on("change:channel_livechat", this, function(){
-            self.channel_render('channel_livechat');
-        });
-            self.$el.on('click', '.o_mail_chat_channel_unpin', _.bind(self.on_click_channel_unpin, self));
-        return this._super.apply(this, arguments).then(function(){
-        });
-    },
-    on_click_channel_unpin: function(event){
-        var self = this;
-        var channel_id = this.$(event.currentTarget).data('channel-id');
-        var channel = this.channels[channel_id];
-        return this.channel_pin(channel.uuid, false).then(function(){
-            self.channel_remove(channel_id);
-            // if unpin current channel, switch to inbox
-            if(self.get('current_channel_id') === channel_id){
-                self.set('current_channel_id', 'channel_inbox');
-            }
-        });
-    },
-    channel_change: function(){
-        // update the default username
-        var current_channel = this.channels[this.get('current_channel_id')];
-        if(current_channel){
-            this.options.default_username = current_channel.anonymous_name || this.options.default_username
-        }
-        return this._super.apply(this, arguments);
-    },
-    // utils function
-    get_channel_slot: function(channel){
-        if(channel.channel_type === 'livechat'){
-            return 'channel_livechat';
-        }
-        return this._super.apply(this, arguments);
-    },
-});
-
-});
diff --git a/addons/im_livechat/static/src/less/im_livechat.less b/addons/im_livechat/static/src/less/im_livechat.less
index 746d3f3d894d..7dc135917ee0 100644
--- a/addons/im_livechat/static/src/less/im_livechat.less
+++ b/addons/im_livechat/static/src/less/im_livechat.less
@@ -1,23 +1,10 @@
-
-.openerp { /* base style of openerp */
-  font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif;
-  color: #4c4c4c;
-  font-size: 13px;
-}
-
-.o_mail_chat_im_window * {
-    box-sizing: border-box;
-}
-
-
 .o_livechat_button {
-    width: 230px;
     position: fixed;
     bottom: 0px;
     right: 6px;
-    display: inline-block;
-    white-space: nowrap;
     min-width: 100px;
+    cursor: pointer;
+    white-space: nowrap;
     background-color: rgba(60, 60, 60, 0.6);
     font-family: 'Lucida Grande', 'Lucida Sans Unicode', Arial, Verdana, sans-serif;
     font-size: 14px;
@@ -29,48 +16,44 @@
     border-bottom: 0px;
     border-top-left-radius: 5px;
     border-top-right-radius: 5px;
-    cursor: pointer;
-    z-index:500;
 }
 
 /* Livechat Rating : feedback smiley */
-.o_mail_chat_im_window_content .o_livechat_rating{
+.o_chat_window .o_chat_content .o_livechat_rating {
     padding: 15px;
-    vertical-align: middle;
-}
-.o_mail_chat_im_window_content .o_livechat_rating .o_livechat_rating_feedback_text {
-    vertical-align: middle;
-    font-size: 14px;
-}
-.o_mail_chat_im_window_content .o_livechat_rating .o_livechat_rating_choices{
-    height:40px;
-    text-align: center;
-    margin: 10px 0 30px 0;
-}
-.o_mail_chat_im_window_content .o_livechat_rating .o_livechat_rating_choices img{
-    width: 50px;
-    opacity: 0.60;
-    cursor: pointer;
-    margin: 5px;
-}
-.o_mail_chat_im_window_content .o_livechat_rating .o_livechat_rating_choices img:hover{
-    opacity:1;
-}
-.o_mail_chat_im_window_content .o_livechat_rating .o_livechat_rating_choices img.selected{
-    opacity: 1;
-}
 
-/* feedback reason */
-.o_mail_chat_im_window_content .o_livechat_rating .o_livechat_rating_reason {
-    margin: 10px 0;
-    display: none; /* hidden by default */
-}
-.o_mail_chat_im_window_content .o_livechat_rating .o_livechat_rating_reason textarea {
-    width: 100%;
-    height: 70px;
-    resize: none;
-}
-.o_mail_chat_im_window_content .o_livechat_rating .o_livechat_rating_reason_button input{
-    float: right;
-    margin-left : 10px;
+    .o_livechat_rating_feedback_text {
+        text-align: justify;
+    }
+
+    .o_livechat_rating_choices {
+        margin: 10px 0;
+        text-align: center;
+
+        > img {
+            width: 50px;
+            opacity: 0.60;
+            cursor: pointer;
+            margin: 5px;
+            &:hover, &.selected {
+                opacity: 1;
+            }
+        }
+    }
+
+    /* feedback reason */
+    .o_livechat_rating_reason {
+        margin: 10px 0;
+        display: none; /* hidden by default */
+
+        > textarea {
+            width: 100%;
+            height: 70px;
+            resize: none;
+        }
+    }
+
+    .o_livechat_rating_reason_button > input {
+        float: right;
+    }
 }
diff --git a/addons/im_livechat/static/src/xml/im_livechat.xml b/addons/im_livechat/static/src/xml/im_livechat.xml
index 614b358c8527..a4db9d1538f6 100644
--- a/addons/im_livechat/static/src/xml/im_livechat.xml
+++ b/addons/im_livechat/static/src/xml/im_livechat.xml
@@ -2,13 +2,7 @@
 
 <templates xml:space="preserve">
 
-    <t t-name="im_livechat.LivechatButton">
-        <div class="openerp o_livechat_button hidden-print" t-att-data-livechat-channel-id="widget.options.channel_id">
-                <t t-esc="widget.options.button_text"/>
-        </div>
-    </t>
-
-    <t t-name="im_livechat.feedback">
+    <t t-name="im_livechat.FeedBack">
         <div class="o_livechat_rating">
             <div class="o_livechat_rating_feedback_text">
                 Did we correctly answer your question ?
@@ -21,7 +15,7 @@
             <div class="o_livechat_rating_reason">
                 <textarea id="reason" placeholder="Explain your note"></textarea>
                 <div class="o_livechat_rating_reason_button">
-                    <input type="button" id="rating_submit" value="Send" />
+                    <input type="button" class="o_rating_submit_button" value="Send" />
                 </div>
             </div>
         </div>
diff --git a/addons/im_livechat/static/src/xml/im_livechat_backend.xml b/addons/im_livechat/static/src/xml/im_livechat_backend.xml
index e371a0b97237..57b438240b20 100644
--- a/addons/im_livechat/static/src/xml/im_livechat_backend.xml
+++ b/addons/im_livechat/static/src/xml/im_livechat_backend.xml
@@ -1,21 +1,17 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <template>
 
-<!--     <t t-extend="mail.chat.ChatMailThread.channels">
-        <t t-jquery="li > a" t-operation="append">
-            <t t-if="channel.channel_type === 'livechat'">
-                <span class="fa fa-times pull-right o_mail_chat_channel_unpin" t-att-title="_t('Close')" t-att-data-channel-id="channel.id"/>
-            </t>
-        </t>
-    </t>
-
-    <t t-extend="mail.chat.ChatMailThread">
+    <t t-extend="mail.chat.Sidebar">
         <t t-jquery=".o_mail_chat_sidebar" t-operation="append">
-            <h4 class="o_mail_chat_sidebar_title"><t t-esc="_t('Livechat')"/></h4>
-            <t t-call="mail.chat.ChatMailThread.channels">
-                <t t-set="channel_slot" t-value="'channel_livechat'"/>
+            <t t-set="channel_type">livechat</t>
+            <t t-call="mail.chat.SidebarTitle">
+                <t t-set="channel_title">Livechat</t>
+                <t t-set="disable_add_channel">true</t>
+            </t>
+            <t t-call="mail.chat.SidebarItems">
+                <t t-set="display_close_button">true</t>
             </t>
         </t>
-    </t> -->
+    </t>
 
 </template>
-- 
GitLab