[FIX] im_chat, im_livechat : split backend/frontend part of im_chat, refactor action binding for dropdown menu, redefine livechat bundle using new js module files, and correct the 'session' in im_livechat namespace.
This commit is contained in:
@@ -64,67 +64,63 @@
|
||||
.oe_im_chatview.oe_im_chatview_disconnected_status .oe_im_chatview_disconnected {
|
||||
display: block;
|
||||
}
|
||||
.oe_im_chatview .oe_im_chatview_header {
|
||||
|
||||
|
||||
|
||||
/* ####### Chat View : Header ####### */
|
||||
.oe_im_chatview_header {
|
||||
padding: 3px 6px 2px;
|
||||
background: #DEDEDE;
|
||||
background: -moz-linear-gradient(#FCFCFC, #DEDEDE);
|
||||
background: -webkit-gradient(linear, left top, left bottom, from(#FCFCFC), to(#DEDEDE));
|
||||
border-radius: 3px 3px 0 0;
|
||||
-moz-border-radius: 3px 3px 0 0;
|
||||
-webkit-border-radius: 3px 3px 0 0;
|
||||
border-radius: 3px 3px 0 0;
|
||||
border-bottom: 1px solid #AEB9BD;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.oe_im_chatview .oe_im_chatview_header_name{
|
||||
.oe_im_chatview_header .header_name{
|
||||
max-width: 75%;
|
||||
word-wrap: break-word;
|
||||
display: inline-block;
|
||||
height: 15px;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.oe_im_chatview .oe_im_chatview_right {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
|
||||
.oe_im_chatview_header .right_buttons {
|
||||
float: right;
|
||||
}
|
||||
.oe_im_chatview_header .right_buttons > * {
|
||||
display: inline-block;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
color: gray ;
|
||||
background: transparent;
|
||||
font-size: 16px;
|
||||
line-height: 16px;
|
||||
margin-left: 5px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/* options */
|
||||
.oe_im_chatview .oe_im_chatview_option_group{
|
||||
z-index: 10;
|
||||
}
|
||||
.oe_im_chatview .oe_im_chatview_option_list{
|
||||
.oe_im_chatview_header .right_buttons .dropdown-menu{
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
.oe_im_chatview .oe_im_chatview_option_list li{
|
||||
.oe_im_chatview_header .right_buttons .dropdown-menu > li{
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: 1.428571429;
|
||||
color: #333;
|
||||
color: #4c4c4c;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.oe_im_chatview .oe_im_chatview_right div, .oe_im_chatview .oe_im_chatview_right button{
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
margin-left: 5px;
|
||||
-webkit-appearance: none;
|
||||
font-size: 18px;
|
||||
line-height: 16px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 1px 0 white;
|
||||
opacity: 0.9;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
||||
.oe_im_chatview .oe_im_chatview_content {
|
||||
overflow: auto;
|
||||
height: 287px;
|
||||
|
||||
@@ -9,423 +9,47 @@ var data = require('web.data');
|
||||
var Model = require('web.Model');
|
||||
var session = require('web.session');
|
||||
var SystrayMenu = require('web.SystrayMenu');
|
||||
var time = require('web.time');
|
||||
var utils = require('web.utils');
|
||||
var web_client = require('web.web_client');
|
||||
var Widget = require('web.Widget');
|
||||
var im_chat_common = require('im_chat.im_chat_common');
|
||||
|
||||
var _t = core._t;
|
||||
var QWeb = core.qweb;
|
||||
|
||||
var NBR_LIMIT_HISTORY = 20;
|
||||
var USERS_LIMIT = 20;
|
||||
|
||||
var ConversationManager = Widget.extend({
|
||||
init: function(parent, options) {
|
||||
var self = this;
|
||||
this._super(parent);
|
||||
this.options = _.clone(options) || {};
|
||||
_.defaults(this.options, {
|
||||
inputPlaceholder: _t("Say something..."),
|
||||
defaultMessage: null,
|
||||
defaultUsername: _t("Visitor"),
|
||||
});
|
||||
// business
|
||||
this.sessions = {};
|
||||
this.bus = bus.bus;
|
||||
this.bus.on("notification", this, this.on_notification);
|
||||
this.bus.options.im_presence = true;
|
||||
|
||||
// ui
|
||||
this.set("right_offset", 0);
|
||||
this.set("bottom_offset", 0);
|
||||
this.on("change:right_offset", this, this.calc_positions);
|
||||
this.on("change:bottom_offset", this, this.calc_positions);
|
||||
// ########## CONVERSATION extentions ###############
|
||||
|
||||
this.set("window_focus", true);
|
||||
this.on("change:window_focus", self, function(e) {
|
||||
self.bus.options.im_presence = self.get("window_focus");
|
||||
});
|
||||
this.set("waiting_messages", 0);
|
||||
this.on("change:waiting_messages", this, this.window_title_change);
|
||||
$(window).on("focus", _.bind(this.window_focus, this));
|
||||
$(window).on("blur", _.bind(this.window_blur, this));
|
||||
this.window_title_change();
|
||||
},
|
||||
on_notification: function(notification) {
|
||||
var self = this;
|
||||
var channel = notification[0];
|
||||
var message = notification[1];
|
||||
var regex_uuid = new RegExp(/(\w{8}(-\w{4}){3}-\w{12}?)/g);
|
||||
|
||||
// Concern im_chat : if the channel is the im_chat.session or im_chat.status, or a 'private' channel (aka the UUID of a session)
|
||||
if((Array.isArray(channel) && (channel[1] === 'im_chat.session' || channel[1] === 'im_chat.presence')) || (regex_uuid.test(channel))){
|
||||
// message to display in the chatview
|
||||
if (message.type === "message" || message.type === "meta") {
|
||||
self.received_message(message);
|
||||
}
|
||||
// activate the received session
|
||||
if(message.uuid){
|
||||
this.apply_session(message);
|
||||
}
|
||||
// user status notification
|
||||
if(message.im_status){
|
||||
self.trigger("im_new_user_status", [message]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// window focus unfocus beep and title
|
||||
window_focus: function() {
|
||||
this.set("window_focus", true);
|
||||
this.set("waiting_messages", 0);
|
||||
},
|
||||
window_blur: function() {
|
||||
this.set("window_focus", false);
|
||||
},
|
||||
window_beep: function() {
|
||||
if (typeof(Audio) === "undefined") {
|
||||
return;
|
||||
}
|
||||
var audio = new Audio();
|
||||
var ext = audio.canPlayType("audio/ogg; codecs=vorbis") ? ".ogg" : ".mp3";
|
||||
audio.src = session.url("/im_chat/static/src/audio/ting") + ext;
|
||||
audio.play();
|
||||
},
|
||||
window_title_change: function() {
|
||||
var title = undefined;
|
||||
if (this.get("waiting_messages") !== 0) {
|
||||
title = _.str.sprintf(_t("%d Messages"), this.get("waiting_messages"));
|
||||
this.window_beep();
|
||||
}
|
||||
if (! web_client || !web_client.set_title_part)
|
||||
return;
|
||||
web_client.set_title_part("im_messages", title);
|
||||
},
|
||||
|
||||
apply_session: function(session, focus){
|
||||
var self = this;
|
||||
var conv = this.sessions[session.uuid];
|
||||
if (! conv) {
|
||||
if(session.state !== 'closed'){
|
||||
conv = new Conversation(this, this, session, this.options);
|
||||
conv.appendTo($("body"));
|
||||
conv.on("destroyed", this, _.bind(this.delete_session, this));
|
||||
this.sessions[session.uuid] = conv;
|
||||
this.calc_positions();
|
||||
}
|
||||
}else{
|
||||
conv.set("session", session);
|
||||
}
|
||||
conv && this.trigger("im_session_activated", conv);
|
||||
if (focus)
|
||||
conv.focus();
|
||||
return conv;
|
||||
},
|
||||
activate_session: function(session, focus) {
|
||||
var self = this;
|
||||
var active_session = _.clone(session);
|
||||
active_session.state = 'open';
|
||||
var conv = this.apply_session(active_session, focus);
|
||||
if(session.state !== 'open'){
|
||||
conv.update_fold_state('open');
|
||||
}
|
||||
return conv;
|
||||
},
|
||||
delete_session: function(uuid){
|
||||
delete this.sessions[uuid];
|
||||
this.calc_positions();
|
||||
},
|
||||
received_message: function(message) {
|
||||
im_chat_common.ConversationManager.include({
|
||||
_message_receive: function(message){
|
||||
var self = this;
|
||||
var session_id = message.to_id[0];
|
||||
var uuid = message.to_id[1];
|
||||
var from_id = message.from_id ? message.from_id[0] : false;
|
||||
var current_user = session ? session.uid : false;
|
||||
if (! this.get("window_focus") && from_id != current_user) {
|
||||
this.set("waiting_messages", this.get("waiting_messages") + 1);
|
||||
}
|
||||
var conv = this.sessions[uuid];
|
||||
var conv = this.sessions[message.to_id[1]];
|
||||
if(!conv){
|
||||
// fetch the session, and init it with the message
|
||||
var def_session = new Model("im_chat.session").call("session_info", [], {"ids" : [session_id]}).then(function(session){
|
||||
conv = self.activate_session(session, false);
|
||||
conv.received_message(message);
|
||||
conv = self.session_apply(session, {'force_open': true});
|
||||
conv.message_receive(message);
|
||||
});
|
||||
}else{
|
||||
conv.received_message(message);
|
||||
this._super(message);
|
||||
}
|
||||
},
|
||||
calc_positions: function() {
|
||||
var self = this;
|
||||
var current = this.get("right_offset");
|
||||
_.each(this.sessions, function(s) {
|
||||
s.set("bottom_position", self.get("bottom_offset"));
|
||||
s.set("right_position", current);
|
||||
current += s.$().outerWidth(true);
|
||||
});
|
||||
},
|
||||
destroy: function() {
|
||||
$(window).off("unload", this.unload);
|
||||
$(window).off("focus", this.window_focus);
|
||||
$(window).off("blur", this.window_blur);
|
||||
return this._super();
|
||||
}
|
||||
});
|
||||
|
||||
var Conversation = Widget.extend({
|
||||
className: "openerp_style oe_im_chatview",
|
||||
events: {
|
||||
"keydown input": "keydown",
|
||||
"click .oe_im_chatview_options" : "click_options",
|
||||
"click .oe_im_chatview_close": "click_close",
|
||||
"click .oe_im_chatview_header": "click_header"
|
||||
im_chat_common.Conversation.include({
|
||||
// user actions
|
||||
prepare_action_menu: function(){
|
||||
this._super();
|
||||
this._add_action(_t('Shortcuts'), 'im_chat_option_shortcut', 'fa fa-info-circle', this.action_shorcode);
|
||||
this._add_action(_t('Quit discussion'), 'im_chat_option_quit', 'fa fa-minus-square', this.action_quit_session);
|
||||
},
|
||||
init: function(parent, c_manager, session, options) {
|
||||
this._super(parent);
|
||||
this.c_manager = c_manager;
|
||||
this.options = options || {};
|
||||
this.loading_history = true;
|
||||
this.set("messages", []);
|
||||
this.set("session", session);
|
||||
this.set("right_position", 0);
|
||||
this.set("bottom_position", 0);
|
||||
this.set("pending", 0);
|
||||
this.inputPlaceholder = this.options.defaultInputPlaceholder;
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
self.$().append(QWeb.render("im_chat.Conversation", {widget: self}));
|
||||
self.$().hide();
|
||||
self.on("change:session", self, self.update_session);
|
||||
self.on("change:right_position", self, self.calc_pos);
|
||||
self.on("change:bottom_position", self, self.calc_pos);
|
||||
self.full_height = self.$().height();
|
||||
self.calc_pos();
|
||||
self.on("change:pending", self, _.bind(function() {
|
||||
if (self.get("pending") === 0) {
|
||||
self.$(".oe_im_chatview_nbr_messages").text("");
|
||||
} else {
|
||||
self.$(".oe_im_chatview_nbr_messages").text("(" + self.get("pending") + ")");
|
||||
}
|
||||
}, self));
|
||||
// messages business
|
||||
self.on("change:messages", this, this.render_messages);
|
||||
self.$('.oe_im_chatview_content').on('scroll',function(){
|
||||
if($(this).scrollTop() === 0){
|
||||
self.load_history();
|
||||
}
|
||||
});
|
||||
self.load_history();
|
||||
self.$().show();
|
||||
// prepare the header and the correct state
|
||||
self.update_session();
|
||||
|
||||
self.init_options();
|
||||
},
|
||||
init_options: function(){
|
||||
this.define_options();
|
||||
if(this.$('.oe_im_chatview_option_list li').length === 0){
|
||||
this.$('.oe_im_chatview_option_group').hide();
|
||||
}
|
||||
},
|
||||
define_options: function(){
|
||||
// add here the option to put in the dropdown menu
|
||||
this._add_option("Shortcuts", "option_shortcut", "fa fa-info-circle");
|
||||
this.$('.oe_im_chatview_option_list .option_shortcut').on('click', _.bind(this.click_option_shortcut, this));
|
||||
// quit the conversation
|
||||
this._add_option('Quit discussion', 'im_chat_option_quit', 'fa fa-minus-square');
|
||||
this.$('.oe_im_chatview_option_list .im_chat_option_quit').on('click', this, _.bind(this.action_quit_conversation, this));
|
||||
},
|
||||
_add_option: function(label, style_class, icon_fa_class){
|
||||
if(icon_fa_class){
|
||||
label = '<i class="'+icon_fa_class+'"></i> ' + label;
|
||||
}
|
||||
this.$('.oe_im_chatview_option_list').append('<li class="'+style_class+'">'+label+'</li>');
|
||||
},
|
||||
action_quit_conversation: function(){
|
||||
var self = this;
|
||||
var Session = new Model("im_chat.session");
|
||||
return Session.call("quit_user", [this.get("session").uuid]).then(function(res) {
|
||||
if(! res){
|
||||
core.bus.trigger('display_notification_warning', _t("Warning"), _t("You are only 2 identified users. Just close the conversation to leave."));
|
||||
}
|
||||
});
|
||||
},
|
||||
show: function(){
|
||||
this.$().animate({
|
||||
height: this.full_height
|
||||
});
|
||||
this.set("pending", 0);
|
||||
},
|
||||
hide: function(){
|
||||
this.$().animate({
|
||||
height: this.$(".oe_im_chatview_header").outerHeight()
|
||||
});
|
||||
},
|
||||
calc_pos: function() {
|
||||
this.$().css("right", this.get("right_position"));
|
||||
this.$().css("bottom", this.get("bottom_position"));
|
||||
},
|
||||
update_fold_state: function(state){
|
||||
return new Model("im_chat.session").call("update_state", [], {"uuid" : this.get("session").uuid, "state" : state});
|
||||
},
|
||||
update_session: function(){
|
||||
// built the name
|
||||
var names = [];
|
||||
_.each(this.get("session").users, function(user){
|
||||
if( (session.uid !== user.id) && !(_.isUndefined(session.uid) && !user.id) ){
|
||||
names.push(user.name);
|
||||
}
|
||||
});
|
||||
this.$(".oe_im_chatview_header_name").text(names.join(", "));
|
||||
this.$(".oe_im_chatview_header_name").attr('title', names.join(", "));
|
||||
// update the fold state
|
||||
if(this.get("session").state){
|
||||
if(this.get("session").state === 'closed'){
|
||||
this.destroy();
|
||||
}else{
|
||||
if(this.get("session").state === 'open'){
|
||||
this.show();
|
||||
}else{
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
load_history: function(){
|
||||
var self = this;
|
||||
if(this.loading_history){
|
||||
var data = {uuid: self.get("session").uuid, limit: NBR_LIMIT_HISTORY};
|
||||
var lastid = _.first(this.get("messages")) ? _.first(this.get("messages")).id : false;
|
||||
if(lastid){
|
||||
data.last_id = lastid;
|
||||
}
|
||||
session.rpc("/im_chat/history", data).then(function(messages){
|
||||
if(messages){
|
||||
self.insert_messages(messages);
|
||||
if(messages.length != NBR_LIMIT_HISTORY){
|
||||
self.loading_history = false;
|
||||
}
|
||||
}else{
|
||||
self.loading_history = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
received_message: function(message) {
|
||||
if (this.get('session').state === 'open') {
|
||||
this.set("pending", 0);
|
||||
} else {
|
||||
this.set("pending", this.get("pending") + 1);
|
||||
}
|
||||
this.insert_messages([message]);
|
||||
this._go_bottom();
|
||||
},
|
||||
send_message: function(message, type) {
|
||||
var self = this;
|
||||
var send_it = function() {
|
||||
return session.rpc("/im_chat/post", {uuid: self.get("session").uuid, message_type: type, message_content: message});
|
||||
};
|
||||
var tries = 0;
|
||||
send_it().fail(function(error, e) {
|
||||
e.preventDefault();
|
||||
tries += 1;
|
||||
if (tries < 3)
|
||||
return send_it();
|
||||
});
|
||||
},
|
||||
insert_messages: function(messages){
|
||||
var self = this;
|
||||
var get_anonymous_name = function(){
|
||||
var name = self.options.defaultUsername;
|
||||
_.each(self.get('session').users, function(u){
|
||||
if(!u.id){
|
||||
name = u.name;
|
||||
}
|
||||
});
|
||||
return name;
|
||||
};
|
||||
// avoid duplicated messages
|
||||
messages = _.filter(messages, function(m){ return !_.contains(_.pluck(self.get("messages"), 'id'), m.id) ; });
|
||||
// escape the message content and set the timezone
|
||||
_.map(messages, function(m){
|
||||
if(!m.from_id){
|
||||
m.from_id = [false, get_anonymous_name()];
|
||||
}
|
||||
m.create_date = moment(time.str_to_datetime(m.create_date)).format('YYYY-MM-DD HH:mm:ss');
|
||||
return m;
|
||||
});
|
||||
this.set("messages", _.sortBy(this.get("messages").concat(messages), function(m){ return m.id; }));
|
||||
},
|
||||
render_messages: function(){
|
||||
var self = this;
|
||||
var res = {};
|
||||
var last_date_day, last_user_id = -1;
|
||||
_.each(this.get("messages"), function(current){
|
||||
// add the url of the avatar for all users in the conversation
|
||||
current.from_id[2] = session.url(_.str.sprintf("/im_chat/image/%s/%s", self.get('session').uuid, current.from_id[0]));
|
||||
var date_day = current.create_date.split(" ")[0];
|
||||
if(date_day !== last_date_day){
|
||||
res[date_day] = [];
|
||||
last_user_id = -1;
|
||||
}
|
||||
last_date_day = date_day;
|
||||
if(current.type == "message"){ // traditionnal message
|
||||
if(last_user_id === current.from_id[0]){
|
||||
_.last(res[date_day]).push(current);
|
||||
}else{
|
||||
res[date_day].push([current]);
|
||||
}
|
||||
last_user_id = current.from_id[0];
|
||||
}else{ // meta message
|
||||
res[date_day].push([current]);
|
||||
last_user_id = -1;
|
||||
}
|
||||
});
|
||||
// render and set the content of the chatview
|
||||
// TODO jem : when refactoring this, don't forget to pre-process date in this function before render quweb template
|
||||
// since, moment will not be define in qweb on the website pages, because the helper (see csn) is in core.js and cannot be
|
||||
// imported in the frontend.
|
||||
this.$('.oe_im_chatview_content_bubbles').html($(QWeb.render("im_chat.Conversation_content", {"list": res})));
|
||||
this._go_bottom();
|
||||
},
|
||||
keydown: function(e) {
|
||||
if(e && e.which == 27) {
|
||||
if(this.$el.prev().find('.oe_im_chatview_input').length > 0){
|
||||
this.$el.prev().find('.oe_im_chatview_input').focus();
|
||||
}else{
|
||||
this.$el.next().find('.oe_im_chatview_input').focus();
|
||||
}
|
||||
e.stopPropagation();
|
||||
this.update_fold_state('closed');
|
||||
}
|
||||
if(e && e.which !== 13) {
|
||||
return;
|
||||
}
|
||||
var mes = this.$("input").val();
|
||||
if (! mes.trim()) {
|
||||
return;
|
||||
}
|
||||
this.$("input").val("");
|
||||
this.send_message(mes, "message");
|
||||
},
|
||||
_go_bottom: function() {
|
||||
this.$(".oe_im_chatview_content").scrollTop(this.$(".oe_im_chatview_content").get(0).scrollHeight);
|
||||
},
|
||||
add_user: function(user){
|
||||
return new Model("im_chat.session").call("add_user", [this.get("session").uuid , user.id]);
|
||||
},
|
||||
focus: function() {
|
||||
this.$(".oe_im_chatview_input").focus();
|
||||
},
|
||||
click_options: function(e){
|
||||
this.$('.oe_im_chatview_options').dropdown();
|
||||
},
|
||||
click_option_shortcut: function(){
|
||||
web_client.action_manager.do_action({
|
||||
action_shorcode: function(e){
|
||||
return web_client.action_manager.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
name : 'Shortcode',
|
||||
name : _t('Shortcode'),
|
||||
res_model: 'im_chat.shortcode',
|
||||
view_mode: 'tree,form',
|
||||
view_type: 'tree',
|
||||
@@ -438,23 +62,46 @@ var Conversation = Widget.extend({
|
||||
}
|
||||
});
|
||||
},
|
||||
click_header: function(event){
|
||||
var classes = event.target.className.split(' ');
|
||||
if(_.contains(classes, 'oe_im_chatview_header_name') || _.contains(classes, 'oe_im_chatview_header')){
|
||||
this.update_fold_state();
|
||||
action_quit_session: function(e){
|
||||
var self = this;
|
||||
var Session = new Model("im_chat.session");
|
||||
return Session.call("quit_user", [this.get("session").uuid]).then(function(res) {
|
||||
if(! res){
|
||||
self.do_warn(_t("Warning"), _t("You are only 2 identified users. Just close the conversation to leave."));
|
||||
}
|
||||
});
|
||||
},
|
||||
// session
|
||||
session_update_state: function(state){
|
||||
var self = this;
|
||||
var args = arguments;
|
||||
var super_call = this._super;
|
||||
// broadcast the state changing
|
||||
return new Model("im_chat.session").call("update_state", [], {"uuid" : this.get("session").uuid, "state" : state}).then(function(){
|
||||
super_call.apply(self, args);
|
||||
});
|
||||
},
|
||||
// window title
|
||||
window_title_change: function() {
|
||||
this.super();
|
||||
var title = undefined;
|
||||
if (this.get("waiting_messages") !== 0) {
|
||||
title = _.str.sprintf(_t("%d Messages"), this.get("waiting_messages"))
|
||||
}
|
||||
web_client.set_title_part("im_messages", title);
|
||||
},
|
||||
click_close: function(event) {
|
||||
this.update_fold_state('closed');
|
||||
// TODO : change this way
|
||||
add_user: function(user){
|
||||
return new Model("im_chat.session").call("add_user", [this.get("session").uuid , user.id]);
|
||||
},
|
||||
destroy: function() {
|
||||
this.trigger("destroyed", this.get('session').uuid);
|
||||
return this._super();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
// ###### BACKEND : contact panel, top menu button #########
|
||||
|
||||
var UserWidget = Widget.extend({
|
||||
"template": "im_chat.UserWidget",
|
||||
template: "im_chat.UserWidget",
|
||||
events: {
|
||||
"click": "activate_user",
|
||||
},
|
||||
@@ -477,7 +124,7 @@ var UserWidget = Widget.extend({
|
||||
this.$(".oe_im_user_online").attr('src', img_src);
|
||||
},
|
||||
activate_user: function() {
|
||||
this.trigger("activate_user", this.get("id"));
|
||||
this.trigger("user_clicked", this.get("id"));
|
||||
},
|
||||
});
|
||||
|
||||
@@ -496,43 +143,55 @@ var InstantMessaging = Widget.extend({
|
||||
this.users = [];
|
||||
this.widgets = {};
|
||||
|
||||
this.c_manager = new ConversationManager(this);
|
||||
this.on("change:right_offset", this.c_manager, _.bind(function() {
|
||||
this.c_manager.set("right_offset", this.get("right_offset"));
|
||||
}, this));
|
||||
this.user_search_dm = new utils.DropMisordered();
|
||||
// listen bus
|
||||
this.bus = bus.bus;
|
||||
this.bus.on("notification", this, this.on_notification);
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
// ui
|
||||
this.conv_manager = new im_chat_common.ConversationManager(this);
|
||||
this.on("change:right_offset", this.conv_manager, _.bind(function() {
|
||||
this.conv_manager.set("right_offset", this.get("right_offset"));
|
||||
}, this));
|
||||
$(window).scroll(_.bind(this.position_compute, this));
|
||||
$(window).resize(_.bind(this.position_compute, this));
|
||||
this.$el.css("right", -this.$el.outerWidth());
|
||||
$(window).scroll(_.bind(this.calc_box, this));
|
||||
$(window).resize(_.bind(this.calc_box, this));
|
||||
this.calc_box();
|
||||
this.position_compute();
|
||||
|
||||
this.on("change:current_search", this, this.search_users_status);
|
||||
// business
|
||||
this.on("change:current_search", this, this.user_search);
|
||||
|
||||
// add a drag & drop listener
|
||||
self.c_manager.on("im_session_activated", self, function(conv) {
|
||||
self.conv_manager.on("im_session_activated", self, function(conv) {
|
||||
conv.$el.droppable({
|
||||
drop: function(event, ui) {
|
||||
conv.add_user(ui.draggable.data("user"));
|
||||
}
|
||||
});
|
||||
});
|
||||
// add a listener for the update of users status
|
||||
this.c_manager.on("im_new_user_status", this, this.update_users_status);
|
||||
|
||||
// fetch the unread message and the recent activity (e.i. to re-init in case of refreshing page)
|
||||
session.rpc("/im_chat/init",{}).then(function(notifications) {
|
||||
return session.rpc("/im_chat/init", {}).then(function(notifications) {
|
||||
_.each(notifications, function(notif){
|
||||
self.c_manager.on_notification(notif);
|
||||
self.conv_manager.on_notification(notif, {'load_history': true});
|
||||
});
|
||||
// start polling
|
||||
bus.bus.start_polling();
|
||||
self.bus.start_polling();
|
||||
});
|
||||
return;
|
||||
},
|
||||
calc_box: function() {
|
||||
on_notification: function(notification){
|
||||
var channel = notification[0];
|
||||
var message = notification[1];
|
||||
// user status notification
|
||||
if(channel[1] === 'im_chat.presence'){
|
||||
if(message.im_status){
|
||||
this.user_update_status([message]);
|
||||
}
|
||||
}
|
||||
},
|
||||
// ui
|
||||
position_compute: function() {
|
||||
var $topbar = window.$('#oe_main_menu_navbar'); // .oe_topbar is replaced with .navbar of bootstrap3
|
||||
var top = $topbar.offset().top + $topbar.height();
|
||||
top = Math.max(top - $(window).scrollTop(), 0);
|
||||
@@ -542,30 +201,8 @@ var InstantMessaging = Widget.extend({
|
||||
input_change: function() {
|
||||
this.set("current_search", this.$(".oe_im_searchbox").val());
|
||||
},
|
||||
search_users_status: function(e) {
|
||||
var user_model = new Model("res.users");
|
||||
var self = this;
|
||||
return this.user_search_dm.add(user_model.call("im_search", [this.get("current_search"),
|
||||
USERS_LIMIT], {context:new data.CompoundContext()})).then(function(result) {
|
||||
self.$(".oe_im_input").val("");
|
||||
var old_widgets = self.widgets;
|
||||
self.widgets = {};
|
||||
self.users = [];
|
||||
_.each(result, function(user) {
|
||||
user.image_url = session.url('/web/binary/image', {model:'res.users', field: 'image_small', id: user.id});
|
||||
var widget = new UserWidget(self, user);
|
||||
widget.appendTo(self.$(".oe_im_users"));
|
||||
widget.on("activate_user", self, self.activate_user);
|
||||
self.widgets[user.id] = widget;
|
||||
self.users.push(user);
|
||||
});
|
||||
_.each(old_widgets, function(w) {
|
||||
w.destroy();
|
||||
});
|
||||
});
|
||||
},
|
||||
switch_display: function() {
|
||||
this.calc_box();
|
||||
this.position_compute();
|
||||
var fct = _.bind(function(place) {
|
||||
this.set("right_offset", place + this.$el.outerWidth());
|
||||
this.$(".oe_im_searchbox").focus();
|
||||
@@ -583,21 +220,43 @@ var InstantMessaging = Widget.extend({
|
||||
return;
|
||||
}
|
||||
// update the list of user status when show the IM
|
||||
this.search_users_status();
|
||||
this.user_search();
|
||||
this.$el.animate({
|
||||
right: 0,
|
||||
}, opt);
|
||||
}
|
||||
this.shown = ! this.shown;
|
||||
},
|
||||
activate_user: function(user_id) {
|
||||
// user methods
|
||||
user_search: function(e) {
|
||||
var self = this;
|
||||
var user_model = new Model("res.users");
|
||||
return user_model.call('im_search', [this.get("current_search"), USERS_LIMIT]).then(function(result){
|
||||
self.$(".oe_im_input").val("");
|
||||
var old_widgets = self.widgets;
|
||||
self.widgets = {};
|
||||
self.users = [];
|
||||
_.each(result, function(user) {
|
||||
user.image_url = session.url('/web/binary/image', {model:'res.users', field: 'image_small', id: user.id});
|
||||
var widget = new UserWidget(self, user);
|
||||
widget.appendTo(self.$(".oe_im_users"));
|
||||
widget.on("user_clicked", self, self.user_clicked);
|
||||
self.widgets[user.id] = widget;
|
||||
self.users.push(user);
|
||||
});
|
||||
_.each(old_widgets, function(w) {
|
||||
w.destroy();
|
||||
});
|
||||
});
|
||||
},
|
||||
user_clicked: function(user_id) {
|
||||
var self = this;
|
||||
var sessions = new Model("im_chat.session");
|
||||
return sessions.call("session_get", [user_id]).then(function(session) {
|
||||
self.c_manager.activate_session(session, true);
|
||||
self.conv_manager.session_apply(session, {'focus': true, 'force_open': true});
|
||||
});
|
||||
},
|
||||
update_users_status: function(users_list){
|
||||
user_update_status: function(users_list){
|
||||
var self = this;
|
||||
_.each(users_list, function(el) {
|
||||
if (self.widgets[el.id]) {
|
||||
@@ -637,8 +296,6 @@ Users.call('has_group', ['base.group_user']).done(function(is_employee) {
|
||||
});
|
||||
|
||||
return {
|
||||
Conversation: Conversation,
|
||||
ConversationManager: ConversationManager,
|
||||
InstantMessaging: InstantMessaging,
|
||||
UserWidget: UserWidget,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
odoo.define('im_chat.im_chat_common', function (require) {
|
||||
"use strict";
|
||||
|
||||
// to do: make this work in website
|
||||
|
||||
var bus = require('bus.bus');
|
||||
var core = require('web.core');
|
||||
var session = require('web.session');
|
||||
var time = require('web.time');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
var _t = core._t;
|
||||
var QWeb = core.qweb;
|
||||
|
||||
var NBR_LIMIT_HISTORY = 20;
|
||||
|
||||
|
||||
var ConversationManager = Widget.extend({
|
||||
init: function(parent, options) {
|
||||
var self = this;
|
||||
this._super(parent);
|
||||
this.options = _.clone(options) || {};
|
||||
_.defaults(this.options, {
|
||||
inputPlaceholder: _t("Say something..."),
|
||||
defaultMessage: null,
|
||||
defaultUsername: _t("Visitor"),
|
||||
force_open: false,
|
||||
load_history: true,
|
||||
focus: false,
|
||||
});
|
||||
// business
|
||||
this.sessions = {};
|
||||
this.bus = bus.bus;
|
||||
this.bus.on("notification", this, this.on_notification);
|
||||
this.bus.options.im_presence = true;
|
||||
|
||||
// ui
|
||||
this.set("right_offset", 0);
|
||||
this.set("bottom_offset", 0);
|
||||
this.on("change:right_offset", this, this.calc_positions);
|
||||
this.on("change:bottom_offset", this, this.calc_positions);
|
||||
|
||||
this.set("window_focus", true);
|
||||
this.on("change:window_focus", self, function(e) {
|
||||
self.bus.options.im_presence = self.get("window_focus");
|
||||
});
|
||||
this.set("waiting_messages", 0);
|
||||
this.on("change:waiting_messages", this, this.window_title_change);
|
||||
$(window).on("focus", _.bind(this.window_focus, this));
|
||||
$(window).on("blur", _.bind(this.window_blur, this));
|
||||
this.window_title_change();
|
||||
},
|
||||
on_notification: function(notification, options) {
|
||||
var self = this;
|
||||
var channel = notification[0];
|
||||
var message = notification[1];
|
||||
var regex_uuid = new RegExp(/(\w{8}(-\w{4}){3}-\w{12}?)/g);
|
||||
|
||||
// Concern im_chat : if the channel is the im_chat.session or im_chat.status, or a 'private' channel (aka the UUID of a session)
|
||||
if((Array.isArray(channel) && (channel[1] === 'im_chat.session')) || (regex_uuid.test(channel))){
|
||||
// message to display in the chatview
|
||||
if (message.type === "message" || message.type === "meta") {
|
||||
self.message_receive(message);
|
||||
}
|
||||
// activate the received session
|
||||
if(message.uuid){
|
||||
this.session_apply(message, options);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// window focus unfocus beep and title
|
||||
window_focus: function() {
|
||||
this.set("window_focus", true);
|
||||
this.set("waiting_messages", 0);
|
||||
},
|
||||
window_blur: function() {
|
||||
this.set("window_focus", false);
|
||||
},
|
||||
window_beep: function() {
|
||||
if (typeof(Audio) === "undefined") {
|
||||
return;
|
||||
}
|
||||
var audio = new Audio();
|
||||
var ext = audio.canPlayType("audio/ogg; codecs=vorbis") ? ".ogg" : ".mp3";
|
||||
audio.src = session.url("/im_chat/static/src/audio/ting") + ext;
|
||||
audio.play();
|
||||
},
|
||||
window_title_change: function() {
|
||||
var title = undefined;
|
||||
if (this.get("waiting_messages") !== 0) {
|
||||
title = _.str.sprintf(_t("%d Messages"), this.get("waiting_messages"));
|
||||
this.window_beep();
|
||||
}
|
||||
},
|
||||
// sessions and messages
|
||||
session_apply: function(active_session, options){
|
||||
// options used by this function : force_open and focus (load_history can be usefull)
|
||||
var self = this;
|
||||
options = _.extend(_.clone(this.options), options || {});
|
||||
// force open
|
||||
var session = _.clone(active_session);
|
||||
if(options['force_open']){
|
||||
session.state = 'open';
|
||||
}
|
||||
// create/get the conversation widget
|
||||
var conv = this.sessions[session.uuid];
|
||||
if (! conv) {
|
||||
if(session.state !== 'closed'){
|
||||
conv = new Conversation(this, this, session, options);
|
||||
conv.appendTo($("body"));
|
||||
conv.on("destroyed", this, _.bind(this.session_delete, this));
|
||||
this.sessions[session.uuid] = conv;
|
||||
this.calc_positions();
|
||||
}
|
||||
}else{
|
||||
conv.set("session", session);
|
||||
}
|
||||
// if force open, broadcast it
|
||||
if(options['force_open'] && active_session.state !== 'open'){
|
||||
conv.session_update_state('open');
|
||||
}
|
||||
// apply the focus
|
||||
if (options['focus']){
|
||||
conv.focus();
|
||||
}
|
||||
return conv;
|
||||
},
|
||||
session_delete: function(uuid){
|
||||
delete this.sessions[uuid];
|
||||
this.calc_positions();
|
||||
},
|
||||
message_receive: function(message) {
|
||||
var self = this;
|
||||
var session_id = message.to_id[0];
|
||||
var uuid = message.to_id[1];
|
||||
var from_id = message['from_id'] ? message['from_id'][0] : false;
|
||||
if (! this.get("window_focus") && from_id != this.get_current_uid()) {
|
||||
this.set("waiting_messages", this.get("waiting_messages") + 1);
|
||||
}
|
||||
this._message_receive(message);
|
||||
},
|
||||
_message_receive: function(message){
|
||||
var uuid = message.to_id[1];
|
||||
var conv = this.sessions[uuid];
|
||||
conv.message_receive(message);
|
||||
},
|
||||
// others
|
||||
get_current_uid: function(){
|
||||
return session && !_.isUndefined(session.uid) ? session.uid : false;
|
||||
},
|
||||
calc_positions: function() {
|
||||
var self = this;
|
||||
var current = this.get("right_offset");
|
||||
_.each(this.sessions, function(s) {
|
||||
s.set("bottom_position", self.get("bottom_offset"));
|
||||
s.set("right_position", current);
|
||||
current += s.$().outerWidth(true);
|
||||
});
|
||||
},
|
||||
destroy: function() {
|
||||
$(window).off("unload", this.unload);
|
||||
$(window).off("focus", this.window_focus);
|
||||
$(window).off("blur", this.window_blur);
|
||||
return this._super();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var Conversation = Widget.extend({
|
||||
className: "openerp_style oe_im_chatview",
|
||||
events: {
|
||||
"keydown input": "keydown",
|
||||
"click .oe_im_chatview_close": "click_close",
|
||||
"click .oe_im_chatview_header": "click_header"
|
||||
},
|
||||
init: function(parent, c_manager, session, options) {
|
||||
this._super(parent);
|
||||
this.c_manager = c_manager;
|
||||
this.options = options || {};
|
||||
this.loading_history = true;
|
||||
this.set("messages", []);
|
||||
this.set("session", session);
|
||||
this.set("right_position", 0);
|
||||
this.set("bottom_position", 0);
|
||||
this.set("pending", 0);
|
||||
this.inputPlaceholder = this.options.defaultInputPlaceholder;
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
self._super.apply(this, arguments);
|
||||
self.prepare_action_menu();
|
||||
// ui
|
||||
self.$().append(QWeb.render("im_chat.Conversation", {widget: self}));
|
||||
self.on("change:right_position", self, self.calc_pos);
|
||||
self.on("change:bottom_position", self, self.calc_pos);
|
||||
self.on("change:pending", self, _.bind(function() {
|
||||
if (self.get("pending") === 0) {
|
||||
self.$(".nbr_messages").text("");
|
||||
} else {
|
||||
self.$(".nbr_messages").text("(" + self.get("pending") + ")");
|
||||
}
|
||||
}, self));
|
||||
self.full_height = self.$().height();
|
||||
self.calc_pos();
|
||||
// business
|
||||
self.bind_action_menu();
|
||||
self.on("change:session", self, self.session_update);
|
||||
self.on("change:messages", self, self.render_messages);
|
||||
self.$('.oe_im_chatview_content').on('scroll',function(){
|
||||
if($(this).scrollTop() === 0){
|
||||
self.message_history();
|
||||
}
|
||||
});
|
||||
if(self.options['load_history']){ // load history if asked
|
||||
self.message_history();
|
||||
}
|
||||
// prepare the header and the correct state
|
||||
self.session_update();
|
||||
},
|
||||
// action menu
|
||||
_add_action: function(label, style_class, icon_fa_class, callback){
|
||||
this.actions.push({
|
||||
'label': label,
|
||||
'class': style_class,
|
||||
'icon_class': icon_fa_class,
|
||||
'callback': callback,
|
||||
});
|
||||
},
|
||||
prepare_action_menu: function(){
|
||||
// override this method to add action with _add_action()
|
||||
this.actions = [];
|
||||
},
|
||||
bind_action_menu: function(){
|
||||
var self = this;
|
||||
_.each(this.actions, function(action){
|
||||
if(action.callback){
|
||||
self.$('.button_option_group .' + action.class).on('click', _.bind(action.callback, self));
|
||||
}
|
||||
});
|
||||
},
|
||||
// ui
|
||||
show: function(){
|
||||
this.$().animate({
|
||||
height: this.full_height
|
||||
});
|
||||
this.set("pending", 0);
|
||||
},
|
||||
hide: function(){
|
||||
this.$().animate({
|
||||
height: this.$(".oe_im_chatview_header").outerHeight()
|
||||
});
|
||||
},
|
||||
calc_pos: function() {
|
||||
this.$().css("right", this.get("right_position"));
|
||||
this.$().css("bottom", this.get("bottom_position"));
|
||||
},
|
||||
// session
|
||||
session_update_state: function(state){
|
||||
var session = this.get('session');
|
||||
session.state = state;
|
||||
this.set('session', session);
|
||||
},
|
||||
session_update: function(){
|
||||
// built the name
|
||||
var names = [];
|
||||
_.each(this.get("session").users, function(user){
|
||||
if( (session.uid !== user.id) && !(_.isUndefined(session.uid) && !user.id) ){
|
||||
names.push(user.name);
|
||||
}
|
||||
});
|
||||
this.$(".header_name").text(names.join(", "));
|
||||
this.$(".header_name").attr('title', names.join(", "));
|
||||
// update the fold state
|
||||
if(this.get("session").state){
|
||||
if(this.get("session").state === 'closed'){
|
||||
this.destroy();
|
||||
}else{
|
||||
if(this.get("session").state === 'open'){
|
||||
this.show();
|
||||
}else{
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// messages
|
||||
message_history: function(){
|
||||
var self = this;
|
||||
if(this.loading_history){
|
||||
var data = {uuid: self.get("session").uuid, limit: NBR_LIMIT_HISTORY};
|
||||
var lastid = _.first(this.get("messages")) ? _.first(this.get("messages")).id : false;
|
||||
if(lastid){
|
||||
data.last_id = lastid;
|
||||
}
|
||||
session.rpc("/im_chat/history", data).then(function(messages){
|
||||
if(messages){
|
||||
self.insert_messages(messages);
|
||||
if(messages.length != NBR_LIMIT_HISTORY){
|
||||
self.loading_history = false;
|
||||
}
|
||||
}else{
|
||||
self.loading_history = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
message_receive: function(message) {
|
||||
if (this.get('session').state === 'open') {
|
||||
this.set("pending", 0);
|
||||
} else {
|
||||
this.set("pending", this.get("pending") + 1);
|
||||
}
|
||||
this.insert_messages([message]);
|
||||
this._go_bottom();
|
||||
},
|
||||
message_send: function(message, type) {
|
||||
var self = this;
|
||||
var send_it = function() {
|
||||
return session.rpc("/im_chat/post", {uuid: self.get("session").uuid, message_type: type, message_content: message});
|
||||
};
|
||||
var tries = 0;
|
||||
send_it().fail(function(error, e) {
|
||||
e.preventDefault();
|
||||
tries += 1;
|
||||
if (tries < 3)
|
||||
return send_it();
|
||||
});
|
||||
},
|
||||
insert_messages: function(messages){
|
||||
var self = this;
|
||||
// avoid duplicated messages
|
||||
messages = _.filter(messages, function(m){ return !_.contains(_.pluck(self.get("messages"), 'id'), m.id) ; });
|
||||
// escape the message content and set the timezone
|
||||
_.map(messages, function(m){
|
||||
if(!m.from_id){
|
||||
m.from_id = [false,self. get_anonymous_name()];
|
||||
}
|
||||
m.create_date = moment(time.str_to_datetime(m.create_date)).format('YYYY-MM-DD HH:mm:ss');
|
||||
return m;
|
||||
});
|
||||
this.set("messages", _.sortBy(this.get("messages").concat(messages), function(m){ return m.id; }));
|
||||
},
|
||||
render_messages: function(){
|
||||
var self = this;
|
||||
var res = {};
|
||||
var last_date_day, last_user_id = -1;
|
||||
_.each(this.get("messages"), function(current){
|
||||
// add the url of the avatar for all users in the conversation
|
||||
current.from_id[2] = session.url(_.str.sprintf("/im_chat/image/%s/%s", self.get('session').uuid, current.from_id[0]));
|
||||
var date_day = current.create_date.split(" ")[0];
|
||||
if(date_day !== last_date_day){
|
||||
res[date_day] = [];
|
||||
last_user_id = -1;
|
||||
}
|
||||
last_date_day = date_day;
|
||||
if(current.type == "message"){ // traditionnal message
|
||||
if(last_user_id === current.from_id[0]){
|
||||
_.last(res[date_day]).push(current);
|
||||
}else{
|
||||
res[date_day].push([current]);
|
||||
}
|
||||
last_user_id = current.from_id[0];
|
||||
}else{ // meta message
|
||||
res[date_day].push([current]);
|
||||
last_user_id = -1;
|
||||
}
|
||||
});
|
||||
// render and set the content of the chatview
|
||||
// TODO jem : when refactoring this, don't forget to pre-process date in this function before render quweb template
|
||||
// since, moment will not be define in qweb on the website pages, because the helper (see csn) is in core.js and cannot be
|
||||
// imported in the frontend.
|
||||
this.$('.oe_im_chatview_content_bubbles').html($(QWeb.render("im_chat.Conversation_content", {"list": res})));
|
||||
this._go_bottom();
|
||||
},
|
||||
// utils and event
|
||||
get_anonymous_name: function(){
|
||||
var name = this.options["defaultUsername"];
|
||||
_.each(this.get('session').users, function(u){
|
||||
if(!u.id){
|
||||
name = u.name;
|
||||
}
|
||||
});
|
||||
return name;
|
||||
},
|
||||
keydown: function(e) {
|
||||
if(e && e.which == 27) {
|
||||
if(this.$el.prev().find('.oe_im_chatview_input').length > 0){
|
||||
this.$el.prev().find('.oe_im_chatview_input').focus();
|
||||
}else{
|
||||
this.$el.next().find('.oe_im_chatview_input').focus();
|
||||
}
|
||||
e.stopPropagation();
|
||||
this.session_update_state('closed');
|
||||
}
|
||||
if(e && e.which !== 13) {
|
||||
return;
|
||||
}
|
||||
var mes = this.$("input").val();
|
||||
if (! mes.trim()) {
|
||||
return;
|
||||
}
|
||||
this.$("input").val("");
|
||||
this.message_send(mes, "message");
|
||||
},
|
||||
_go_bottom: function() {
|
||||
this.$(".oe_im_chatview_content").scrollTop(this.$(".oe_im_chatview_content").get(0).scrollHeight);
|
||||
},
|
||||
focus: function() {
|
||||
this.$(".oe_im_chatview_input").focus();
|
||||
},
|
||||
click_header: function(event){
|
||||
var classes = event.target.className.split(' ');
|
||||
if(_.contains(classes, 'header_name') || _.contains(classes, 'oe_im_chatview_header')){
|
||||
this.session_update_state();
|
||||
}
|
||||
},
|
||||
click_close: function(event) {
|
||||
this.session_update_state('closed');
|
||||
},
|
||||
destroy: function() {
|
||||
this.trigger("destroyed", this.get('session').uuid);
|
||||
return this._super();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
Conversation: Conversation,
|
||||
ConversationManager: ConversationManager,
|
||||
};
|
||||
|
||||
});
|
||||
@@ -2,16 +2,21 @@
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="im_chat.Conversation">
|
||||
<div class="oe_im_chatview_header">
|
||||
<span class="oe_im_chatview_header_name"></span>
|
||||
<span class="oe_im_chatview_nbr_messages"/>
|
||||
<span class="oe_im_chatview_right">
|
||||
<div class="btn-group oe_im_chatview_option_group">
|
||||
<span class="oe_im_chatview_options dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-cogs"></i>
|
||||
</span>
|
||||
<ul class="dropdown-menu oe_im_chatview_option_list" role="menu">
|
||||
<!-- options -->
|
||||
</ul>
|
||||
<span class="header_name"></span>
|
||||
<span class="nbr_messages"/>
|
||||
<span class="right_buttons">
|
||||
<!-- options -->
|
||||
<div t-if="widget.actions.length !== 0" class="btn-group button_option_group">
|
||||
<span class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-cogs"></i>
|
||||
</span>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<t t-foreach="widget.actions" t-as="action">
|
||||
<li t-att-class="action.class">
|
||||
<i t-att-class="action.icon_class"></i> <t t-esc="action.label"/>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="oe_im_chatview_close">×</div>
|
||||
</span>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/im_chat/static/src/css/im_common.css"/>
|
||||
<link rel="stylesheet" href="/im_chat/static/src/css/im_chat.css"/>
|
||||
<script type="text/javascript" src="/im_chat/static/src/js/im_chat_common.js"></script>
|
||||
<script type="text/javascript" src="/im_chat/static/src/js/im_chat.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -2,12 +2,12 @@ odoo.define('im_livechat.im_livechat', function (require) {
|
||||
"use strict";
|
||||
|
||||
var bus = require('bus.bus');
|
||||
var im_chat = require('im_chat.im_chat');
|
||||
var core = require('web.core');
|
||||
var session = require('web.session');
|
||||
var user_session = require('web.session');
|
||||
var time = require('web.time');
|
||||
var utils = require('web.utils');
|
||||
var Widget = require('web.Widget');
|
||||
var im_chat_common = require('im_chat.im_chat_common');
|
||||
|
||||
var _t = core._t;
|
||||
var QWeb = core.qweb;
|
||||
@@ -19,16 +19,12 @@ var COOKIE_NAME = 'livechat_conversation';
|
||||
The state of anonymous session is hold by the client and not the server.
|
||||
Override the method managing the state of normal conversation.
|
||||
*/
|
||||
im_chat.Conversation.include({
|
||||
im_chat_common.Conversation.include({
|
||||
init: function(){
|
||||
this._super.apply(this, arguments);
|
||||
this.shown = true;
|
||||
this.loading_history = true; // since the session is kept after a refresh, anonymous can reload their history
|
||||
this.feedback = false;
|
||||
},
|
||||
define_options: function(){
|
||||
// no options for anonymous user
|
||||
},
|
||||
show: function(){
|
||||
this._super.apply(this, arguments);
|
||||
this.shown = true;
|
||||
@@ -37,7 +33,8 @@ im_chat.Conversation.include({
|
||||
this._super.apply(this, arguments);
|
||||
this.shown = false;
|
||||
},
|
||||
update_fold_state: function(state){
|
||||
session_update_state: function(state){
|
||||
// set manually the new state
|
||||
if(state === 'closed'){
|
||||
this.destroy();
|
||||
}else{
|
||||
@@ -53,10 +50,9 @@ im_chat.Conversation.include({
|
||||
}
|
||||
}
|
||||
}
|
||||
var session = this.get('session');
|
||||
session.state = state;
|
||||
this.set('session', session);
|
||||
session.set_cookie(COOKIE_NAME, JSON.stringify(session), 60*60);
|
||||
// session and cookie update
|
||||
this._super(state);
|
||||
utils.set_cookie(COOKIE_NAME, JSON.stringify(this.get('session')), 60*60);
|
||||
},
|
||||
click_close: function(event) {
|
||||
if(!this.feedback && (this.get('messages').length > 1)){
|
||||
@@ -68,21 +64,22 @@ im_chat.Conversation.include({
|
||||
this.feedback.on("feedback_sent", this, this.click_close);
|
||||
}else{
|
||||
this._super.apply(this, arguments);
|
||||
session.set_cookie(COOKIE_NAME, "", -1);
|
||||
utils.set_cookie(COOKIE_NAME, "", -1);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// To avoid exeption when the anonymous has close his
|
||||
// conversation and when operator send him a message.
|
||||
im_chat.ConversationManager.include({
|
||||
received_message: function(message) {
|
||||
im_chat_common.ConversationManager.include({
|
||||
message_receive: function(message) {
|
||||
try{
|
||||
this._super(message);
|
||||
}catch(e){}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var LiveSupport = Widget.extend({
|
||||
init: function(server_url, db, channel, options, rule) {
|
||||
this._super();
|
||||
@@ -93,11 +90,11 @@ var LiveSupport = Widget.extend({
|
||||
defaultMessage: _t("How may I help you?"),
|
||||
defaultUsername: _t("Visitor"),
|
||||
});
|
||||
session.setup(server_url, {use_cors: false});
|
||||
user_session.setup(server_url, {use_cors: false});
|
||||
this.load_template(db, channel, options, rule);
|
||||
},
|
||||
_get_template_list: function(){
|
||||
return ['/im_live_chat/static/src/xml/im_live_chat.xml', '/im_chat/static/src/xml/im_chat.xml'];
|
||||
return ['/im_livechat/static/src/xml/im_livechat.xml', '/im_chat/static/src/xml/im_chat.xml'];
|
||||
},
|
||||
load_template: function(db, channel, options, rule){
|
||||
var self = this;
|
||||
@@ -105,7 +102,7 @@ var LiveSupport = Widget.extend({
|
||||
var defs = [];
|
||||
var templates = this._get_template_list();
|
||||
_.each(templates, function(tmpl){
|
||||
defs.push(session.rpc('/web/proxy/load', {path: tmpl}).then(function(xml) {
|
||||
defs.push(user_session.rpc('/web/proxy/load', {path: tmpl}).then(function(xml) {
|
||||
QWeb.add_template(xml);
|
||||
}));
|
||||
});
|
||||
@@ -119,7 +116,7 @@ var LiveSupport = Widget.extend({
|
||||
if(cookie){
|
||||
self.build_button(channel, options, JSON.parse(cookie), rule);
|
||||
}else{
|
||||
session.rpc("/im_livechat/available", {db: db, channel: channel}).then(function(activated) {
|
||||
user_session.rpc("/im_livechat/available", {db: db, channel: channel}).then(function(activated) {
|
||||
if(activated){
|
||||
self.build_button(channel, options, false, rule);
|
||||
}
|
||||
@@ -154,11 +151,8 @@ var ChatButton = Widget.extend({
|
||||
start: function() {
|
||||
this.$().append(QWeb.render("im_livechat.chatButton", {widget: this}));
|
||||
// set up the manager
|
||||
this.manager = new im_chat.ConversationManager(this, this.options);
|
||||
this.manager = new im_chat_common.ConversationManager(this, this.options);
|
||||
this.manager.set("bottom_offset", $('.oe_chat_button').outerHeight());
|
||||
this.manager.notification = function(notif){ // override the notification default function
|
||||
alert(notif);
|
||||
};
|
||||
if(this.session){
|
||||
this.set_conversation(this.session);
|
||||
}
|
||||
@@ -166,17 +160,17 @@ var ChatButton = Widget.extend({
|
||||
click: function() {
|
||||
var self = this;
|
||||
if (!this.conv){
|
||||
session.rpc("/im_livechat/get_session", {
|
||||
"channel_id" : self.channel,
|
||||
user_session.rpc("/im_livechat/get_session", {
|
||||
"channel_id" : self.channel,
|
||||
"anonymous_name" : this.options.defaultUsername
|
||||
}, {shadow: true}).then(function(session) {
|
||||
if (! session) {
|
||||
self.manager.notification(self.no_session_message);
|
||||
alert(self.no_session_message);
|
||||
return;
|
||||
}
|
||||
session.state = 'open';
|
||||
// save the session in a cookie
|
||||
session.set_cookie(COOKIE_NAME, JSON.stringify(session), 60*60);
|
||||
utils.set_cookie(COOKIE_NAME, JSON.stringify(session), 60*60);
|
||||
// create the conversation with the received session
|
||||
self.set_conversation(session, true);
|
||||
});
|
||||
@@ -188,7 +182,7 @@ var ChatButton = Widget.extend({
|
||||
if(session.state === 'closed'){
|
||||
return;
|
||||
}
|
||||
this.conv = this.manager.apply_session(session);
|
||||
this.conv = this.manager.session_apply(session);
|
||||
this.conv.on("destroyed", this, function() {
|
||||
bus.bus.stop_polling();
|
||||
delete self.conv;
|
||||
@@ -207,7 +201,7 @@ var ChatButton = Widget.extend({
|
||||
if(this.session.users.length > 0){
|
||||
if (self.options.defaultMessage) {
|
||||
setTimeout(function(){
|
||||
self.conv.received_message({
|
||||
self.conv.message_receive({
|
||||
id : 1,
|
||||
type: "message",
|
||||
message: self.options.defaultMessage,
|
||||
@@ -229,7 +223,7 @@ var Feedback = Widget.extend({
|
||||
this.conversation = parent;
|
||||
this.reason = false;
|
||||
this.rating = false;
|
||||
this.server_origin = session.origin;
|
||||
this.server_origin = user_session.origin;
|
||||
},
|
||||
start: function(){
|
||||
this._super.apply(this.arguments);
|
||||
@@ -263,7 +257,7 @@ var Feedback = Widget.extend({
|
||||
_send_feedback: function(close){
|
||||
var self = this;
|
||||
var uuid = this.conversation.get('session').uuid;
|
||||
return session.rpc("/rating/livechat/feedback", {uuid: uuid, rate: this.rating, reason : this.reason}).then(function(res) {
|
||||
return user_session.rpc("/rating/livechat/feedback", {uuid: uuid, rate: this.rating, reason : this.reason}).then(function(res) {
|
||||
if(close){
|
||||
self.trigger("feedback_sent"); // will close the conversation
|
||||
}
|
||||
|
||||
@@ -96,9 +96,36 @@
|
||||
<!-- OpenERP minimal lib -->
|
||||
<script type="text/javascript" src="/web/static/lib/underscore/underscore.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/underscore.string/lib/underscore.string.js"></script>
|
||||
<!-- jQuery -->
|
||||
<script type="text/javascript" src="/web/static/lib/jquery/jquery.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery/jquery.browser.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.timeago/jquery.timeago.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script>
|
||||
|
||||
<!-- Qweb2 lib -->
|
||||
<script type="text/javascript" src="/web/static/lib/qweb/qweb2.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/openerpframework.js"></script>
|
||||
<!-- Odoo JS Framework -->
|
||||
<script type="text/javascript" src="/web/static/src/js/boot.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/class.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/translation.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/ajax.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/time.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/mixins.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/widget.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/registry.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/session.js"></script>
|
||||
<!--<script type="text/javascript" src="/web/static/src/js/framework/model.js"></script>-->
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/utils.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/framework/core.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
odoo.define('web.session', function (require) {
|
||||
var Session = require('web.Session');
|
||||
var modules = odoo._modules;
|
||||
return new Session(undefined, undefined, {modules:modules});
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- add the internal lib -->
|
||||
<t t-call="im_livechat.internal_lib"/>
|
||||
</template>
|
||||
@@ -109,7 +136,7 @@
|
||||
<script type="text/javascript" src="/web/static/lib/moment/moment.js"></script>
|
||||
<!-- IM module -->
|
||||
<script type="text/javascript" src="/bus/static/src/js/bus.js"></script>
|
||||
<script type="text/javascript" src="/im_chat/static/src/js/im_chat.js"></script>
|
||||
<script type="text/javascript" src="/im_chat/static/src/js/im_chat_common.js"></script>
|
||||
<script type="text/javascript" src="/im_livechat/static/src/js/im_livechat.js"></script>
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="/im_chat/static/src/css/im_common.css"></link>
|
||||
@@ -118,23 +145,25 @@
|
||||
|
||||
<!-- the js code to initialize the LiveSupport object -->
|
||||
<template id="loader" name="Javascript initializing the LiveSupport">
|
||||
odoo.define('im_livechat.livesupport', function (require) {
|
||||
var im_livechat = require('im_livechat.im_livechat');
|
||||
window.livesupport = new im_livechat.LiveSupport(
|
||||
"<t t-esc="url"/>",
|
||||
"<t t-esc="dbname"/>",
|
||||
<t t-esc="channel"/>,
|
||||
{
|
||||
buttonText: "<t t-esc="buttonText"/>",
|
||||
inputPlaceholder: "<t t-esc="inputPlaceholder"/>",
|
||||
defaultMessage: "<t t-esc="defaultMessage"/>" || '',
|
||||
auto: window.oe_im_livechat_auto || false,
|
||||
defaultUsername: "<t t-esc="username"/>" || undefined,
|
||||
},
|
||||
<t t-raw="rule"/>
|
||||
);
|
||||
$(function() {
|
||||
odoo.define('im_livechat.livesupport', function (require) {
|
||||
var im_livechat = require('im_livechat.im_livechat');
|
||||
window.livesupport = new im_livechat.LiveSupport(
|
||||
"<t t-esc="url"/>",
|
||||
"<t t-esc="dbname"/>",
|
||||
<t t-esc="channel"/>,
|
||||
{
|
||||
buttonText: "<t t-esc="buttonText"/>",
|
||||
inputPlaceholder: "<t t-esc="inputPlaceholder"/>",
|
||||
defaultMessage: "<t t-esc="defaultMessage"/>" || '',
|
||||
auto: window.oe_im_livechat_auto || false,
|
||||
defaultUsername: "<t t-esc="username"/>" || undefined,
|
||||
},
|
||||
<t t-raw="rule"/>
|
||||
);
|
||||
});
|
||||
odoo.init();
|
||||
});
|
||||
|
||||
</template>
|
||||
|
||||
</data>
|
||||
|
||||
Reference in New Issue
Block a user