diff options
| author | niv-openerp <nicolas.vanhoren@openerp.com> | 2013-03-15 12:23:37 +0100 |
|---|---|---|
| committer | niv-openerp <nicolas.vanhoren@openerp.com> | 2013-03-15 12:23:37 +0100 |
| commit | 8ae0252436005b1651a4eaedd093e6ccb643d493 (patch) | |
| tree | ee491f6365d07ad854a0e866af545751a9a3f7c6 /addons/im/static/src | |
| parent | 296b9fbbb4a3c9b9a02b97143b36d97ad17838e1 (diff) | |
Forgot a rename
bzr revid: nicolas.vanhoren@openerp.com-20130315112337-1d5d29wegmhml5zc
Diffstat (limited to 'addons/im/static/src')
| -rw-r--r-- | addons/im/static/src/audio/Ting.mp3 | bin | 0 -> 17087 bytes | |||
| -rw-r--r-- | addons/im/static/src/audio/Ting.ogg | bin | 0 -> 12568 bytes | |||
| -rw-r--r-- | addons/im/static/src/audio/purr.mp3 | bin | 0 -> 85262 bytes | |||
| -rw-r--r-- | addons/im/static/src/audio/purr.ogg | bin | 0 -> 47390 bytes | |||
| -rw-r--r-- | addons/im/static/src/css/im.css | 258 | ||||
| -rw-r--r-- | addons/im/static/src/img/avatar/avatar.jpeg | bin | 0 -> 6462 bytes | |||
| -rwxr-xr-x | addons/im/static/src/img/button-gloss.png | bin | 0 -> 74 bytes | |||
| -rwxr-xr-x | addons/im/static/src/img/glyphicons-halflings-white.png | bin | 0 -> 8777 bytes | |||
| -rwxr-xr-x | addons/im/static/src/img/glyphicons-halflings.png | bin | 0 -> 12799 bytes | |||
| -rw-r--r-- | addons/im/static/src/img/green.png | bin | 0 -> 830 bytes | |||
| -rw-r--r-- | addons/im/static/src/img/icon.png | bin | 0 -> 16237 bytes | |||
| -rw-r--r-- | addons/im/static/src/img/logo.png | bin | 0 -> 3600 bytes | |||
| -rw-r--r-- | addons/im/static/src/img/wood.png | bin | 0 -> 102184 bytes | |||
| -rw-r--r-- | addons/im/static/src/js/im.js | 461 | ||||
| -rw-r--r-- | addons/im/static/src/xml/im.xml | 63 |
15 files changed, 782 insertions, 0 deletions
diff --git a/addons/im/static/src/audio/Ting.mp3 b/addons/im/static/src/audio/Ting.mp3 Binary files differnew file mode 100644 index 00000000000..6fd090a89ce --- /dev/null +++ b/addons/im/static/src/audio/Ting.mp3 diff --git a/addons/im/static/src/audio/Ting.ogg b/addons/im/static/src/audio/Ting.ogg Binary files differnew file mode 100644 index 00000000000..8d17ea85bd3 --- /dev/null +++ b/addons/im/static/src/audio/Ting.ogg diff --git a/addons/im/static/src/audio/purr.mp3 b/addons/im/static/src/audio/purr.mp3 Binary files differnew file mode 100644 index 00000000000..849d303c3b1 --- /dev/null +++ b/addons/im/static/src/audio/purr.mp3 diff --git a/addons/im/static/src/audio/purr.ogg b/addons/im/static/src/audio/purr.ogg Binary files differnew file mode 100644 index 00000000000..2ddc77537f1 --- /dev/null +++ b/addons/im/static/src/audio/purr.ogg diff --git a/addons/im/static/src/css/im.css b/addons/im/static/src/css/im.css new file mode 100644 index 00000000000..2f3e7dd58bb --- /dev/null +++ b/addons/im/static/src/css/im.css @@ -0,0 +1,258 @@ + +.openerp .oe_im { + position: fixed; + background-color: #E8EBEF; + width: 220px; + border-left: 1px solid #AEB9BD; +} + +/* button */ + +.openerp .oe_topbar_imbutton { + cursor: pointer; +} + +/* search stuff */ +.openerp .oe_im_frame_header { + position: relative; + background: #dedede; + background: -moz-linear-gradient(#fcfcfc, #dedede); + background: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede)); + border-bottom: 1px solid border-color !important; + padding: 5px; +} +.openerp .oe_im_frame_header .oe_im_searchbox { + width: 168px; + padding: 1px 21px 1px 19px; + font-size: 13px; + -moz-border-radius: 13px; + -webkit-border-radius: 13px; + border-radius: 13px; +} +.openerp .oe_im_frame_header .oe_im_search_icon { + position: absolute; + color: #888; + top: 2px; + left: 9px; + font-size: 28px; + font-family: "entypoRegular" !important; + font-weight: 300 !important; +} +.openerp .oe_im_frame_header .oe_im_search_clear { + display: none; + position: absolute; + right: 11px; + top: 4px; + font-size: 26px; + color: #b6b6b6; + cursor: pointer; +} +.openerp .oe_im_frame_header .oe_im_search_clear:hover { + color: #888; +} + +/* users */ + +.openerp .oe_im_users { + padding-bottom: 38px; +} +.openerp .oe_im_user { + position: relative; + padding: 2px 6px; + cursor: pointer; + font-size: 13px; + margin-bottom: 3px; +} +.openerp .oe_im_user:hover { + background: lightGrey; +} +.openerp .oe_im_user_clip { + display: inline-block; + width: 26px; + height: 26px; + margin-right: 4px; + -moz-box-shadow: 0 0 2px 1px rgba(0,0,0,0.25); + -webkit-box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.25); + box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.25); +} +.openerp .oe_im_user_avatar { + width: 26px; + height: auto; +} +.openerp .oe_im_user_name { + width: 162px; + line-height: 26px; + padding-right: 15px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + position: relative; +} + +.openerp .oe_im_user_online { + display: none; + position: absolute; + top: 9.5px; + right: 11px; + width: 11px; + height: 11px; + vertical-align: middle; + border: 0; +} + +/* conversations */ + +.openerp .oe_im_chatview { + position: fixed; + overflow: hidden; + bottom: 6px; + margin-right: 6px; + background: rgba(60, 60, 60, 0.8); + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + -moz-box-shadow: 0 0 3px rgba(0,0,0,0.3), 0 2px 4px rgba(0,0,0,0.3); + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.3); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.3); + width: 240px; +} +.openerp .oe_im_chatview .oe_im_chatview_disconnected { + display:none; + z-index: 100; + width: 100%; + background: #E8EBEF; + padding: 5px; + font-size: 11px; + color: #999; + line-height: 14px; + height: 28px; + overflow: hidden; +} +.openerp .oe_im_chatview.oe_im_chatview_disconnected_status .oe_im_chatview_disconnected { + display: block; +} +.openerp .oe_im_chatview .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)); + -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; +} +.openerp .oe_im_chatview .oe_im_chatview_close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; + font-size: 18px; + line-height: 16px; + float: right; + font-weight: bold; + color: black; + text-shadow: 0 1px 0 white; + opacity: 0.2; +} +.openerp .oe_im_chatview .oe_im_chatview_content { + overflow: auto; + height: 287px; +} +.openerp .oe_im_chatview.oe_im_chatview_disconnected_status .oe_im_chatview_content { + height: 249px; +} +.openerp .oe_im_chatview .oe_im_chatview_footer { + position: relative; + padding: 3px; + border-top: 1px solid #AEB9BD; + background: #DEDEDE; + background: -moz-linear-gradient(#FCFCFC, #DEDEDE); + background: -webkit-gradient(linear, left top, left bottom, from(#FCFCFC), to(#DEDEDE)); + -moz-border-radius: 0 0 3px 3px; + -webkit-border-radius: 0 0 3px 3px; + border-radius: 0 0 3px 3px; +} +.openerp .oe_im_chatview .oe_im_chatview_input { + width: 222px; + font-family: Lato, Helvetica, sans-serif; + font-size: 13px; + color: #333; + padding: 1px 5px; + border: 1px solid #AEB9BD; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + -moz-box-shadow: inset 0 1px 4px rgba(0,0,0,0.2); + -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.2); + box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.2); +} +.openerp .oe_im_chatview .oe_im_chatview_bubble { + background: white; + position: relative; + padding: 3px; + margin: 3px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; +} +.openerp .oe_im_chatview .oe_im_chatview_clip { + position: relative; + float: left; + width: 26px; + height: 26px; + margin-right: 4px; + -moz-box-shadow: 0 0 2px 1px rgba(0,0,0,0.25); + -webkit-box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.25); + box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.25); +} +.openerp .oe_im_chatview .oe_im_chatview_avatar { + float: left; + width: 26px; + height: auto; + clip: rect(0, 26px, 26px, 0); + max-width: 100%; + width: auto 9; + height: auto; + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} +.openerp .oe_im_chatview .oe_im_chatview_time { + position: absolute; + right: 0px; + top: 0px; + margin: 3px; + text-align: right; + line-height: 13px; + font-size: 11px; + color: #999; + width: 60px; + overflow: hidden; +} +.openerp .oe_im_chatview .oe_im_chatview_from { + margin: 0 0 2px 0; + line-height: 14px; + font-weight: bold; + font-size: 12px; + width: 140px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + color: #3A87AD; +} +.openerp .oe_im_chatview .oe_im_chatview_bubble_list { +} +.openerp .oe_im_chatview .oe_im_chatview_bubble_item { + margin: 0 0 2px 30px; + line-height: 14px; + word-wrap: break-word; +} + +.openerp .oe_im_chatview_online { + display: none; + margin-top: -4px; + width: 11px; + height: 11px; +} diff --git a/addons/im/static/src/img/avatar/avatar.jpeg b/addons/im/static/src/img/avatar/avatar.jpeg Binary files differnew file mode 100644 index 00000000000..7168794022e --- /dev/null +++ b/addons/im/static/src/img/avatar/avatar.jpeg diff --git a/addons/im/static/src/img/button-gloss.png b/addons/im/static/src/img/button-gloss.png Binary files differnew file mode 100755 index 00000000000..6f3957702fe --- /dev/null +++ b/addons/im/static/src/img/button-gloss.png diff --git a/addons/im/static/src/img/glyphicons-halflings-white.png b/addons/im/static/src/img/glyphicons-halflings-white.png Binary files differnew file mode 100755 index 00000000000..3bf6484a29d --- /dev/null +++ b/addons/im/static/src/img/glyphicons-halflings-white.png diff --git a/addons/im/static/src/img/glyphicons-halflings.png b/addons/im/static/src/img/glyphicons-halflings.png Binary files differnew file mode 100755 index 00000000000..a9969993201 --- /dev/null +++ b/addons/im/static/src/img/glyphicons-halflings.png diff --git a/addons/im/static/src/img/green.png b/addons/im/static/src/img/green.png Binary files differnew file mode 100644 index 00000000000..01fb373c251 --- /dev/null +++ b/addons/im/static/src/img/green.png diff --git a/addons/im/static/src/img/icon.png b/addons/im/static/src/img/icon.png Binary files differnew file mode 100644 index 00000000000..07d2503ce4e --- /dev/null +++ b/addons/im/static/src/img/icon.png diff --git a/addons/im/static/src/img/logo.png b/addons/im/static/src/img/logo.png Binary files differnew file mode 100644 index 00000000000..aca5f4c60d8 --- /dev/null +++ b/addons/im/static/src/img/logo.png diff --git a/addons/im/static/src/img/wood.png b/addons/im/static/src/img/wood.png Binary files differnew file mode 100644 index 00000000000..22f2450d3ad --- /dev/null +++ b/addons/im/static/src/img/wood.png diff --git a/addons/im/static/src/js/im.js b/addons/im/static/src/js/im.js new file mode 100644 index 00000000000..547ee2c3026 --- /dev/null +++ b/addons/im/static/src/js/im.js @@ -0,0 +1,461 @@ + +openerp.im = function(instance) { + + var USERS_LIMIT = 20; + var ERROR_DELAY = 5000; + + var _t = instance.web._t, + _lt = instance.web._lt; + var QWeb = instance.web.qweb; + + instance.web.UserMenu.include({ + do_update: function(){ + var self = this; + this.update_promise.then(function() { + var im = new instance.im.InstantMessaging(self); + im.appendTo(instance.client.$el); + var button = new instance.im.ImTopButton(this); + button.on("clicked", im, im.switch_display); + button.appendTo(instance.webclient.$el.find('.oe_systray')); + }); + return this._super.apply(this, arguments); + }, + }); + + instance.im.ImTopButton = instance.web.Widget.extend({ + template:'ImTopButton', + events: { + "click": "clicked", + }, + clicked: function() { + this.trigger("clicked"); + }, + }); + + instance.im.InstantMessaging = instance.web.Widget.extend({ + template: "InstantMessaging", + events: { + "keydown .oe_im_searchbox": "input_change", + "keyup .oe_im_searchbox": "input_change", + "change .oe_im_searchbox": "input_change", + }, + init: function(parent) { + this._super(parent); + this.shown = false; + this.set("right_offset", 0); + this.set("current_search", ""); + this.users = []; + this.c_manager = new instance.im.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 instance.web.DropMisordered(); + }, + start: function() { + 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.on("change:current_search", this, this.search_changed); + this.search_changed(); + + var self = this; + + return this.c_manager.start_polling(); + }, + calc_box: function() { + var $topbar = instance.client.$(".oe_topbar"); + var top = $topbar.offset().top + $topbar.height(); + top = Math.max(top - $(window).scrollTop(), 0); + this.$el.css("top", top); + this.$el.css("bottom", 0); + }, + input_change: function() { + this.set("current_search", this.$(".oe_im_searchbox").val()); + }, + search_changed: function(e) { + var users = new instance.web.Model("im.user"); + var self = this; + return this.user_search_dm.add(users.call("search_users", + [[["name", "ilike", this.get("current_search")], ["id", "<>", instance.session.uid]], + ["name", "user", "uuid", "im_status"], USERS_LIMIT], {context:new instance.web.CompoundContext()})).then(function(result) { + self.c_manager.add_to_user_cache(result); + self.$(".oe_im_input").val(""); + var old_users = self.users; + self.users = []; + _.each(result, function(user) { + var widget = new instance.im.UserWidget(self, self.c_manager.get_user(user.id)); + widget.appendTo(self.$(".oe_im_users")); + widget.on("activate_user", self, self.activate_user); + self.users.push(widget); + }); + _.each(old_users, function(user) { + user.destroy(); + }); + }); + }, + switch_display: function() { + var fct = _.bind(function(place) { + this.set("right_offset", place + this.$el.outerWidth()); + }, this); + var opt = { + step: fct, + }; + if (this.shown) { + this.$el.animate({ + right: -this.$el.outerWidth(), + }, opt); + } else { + if (! this.c_manager.get_activated()) { + this.do_warn("Instant Messaging is not activated on this server.", ""); + return; + } + this.$el.animate({ + right: 0, + }, opt); + } + this.shown = ! this.shown; + }, + activate_user: function(user) { + this.c_manager.activate_user(user, true); + }, + }); + + instance.im.UserWidget = instance.web.Widget.extend({ + "template": "UserWidget", + events: { + "click": "activate_user", + }, + init: function(parent, user) { + this._super(parent); + this.user = user; + this.user.add_watcher(); + }, + start: function() { + var change_status = function() { + this.$(".oe_im_user_online").toggle(this.user.get("im_status") === true); + }; + this.user.on("change:im_status", this, change_status); + change_status.call(this); + }, + activate_user: function() { + this.trigger("activate_user", this.user); + }, + destroy: function() { + this.user.remove_watcher(); + this._super(); + }, + }); + + instance.im.ImUser = instance.web.Class.extend(instance.web.PropertiesMixin, { + init: function(parent, user_rec) { + instance.web.PropertiesMixin.init.call(this, parent); + user_rec.image_url = instance.session.url("/im/static/src/img/avatar/avatar.jpeg"); + if (user_rec.user) + user_rec.image_url = instance.session.url('/web/binary/image', {model:'res.users', field: 'image_small', id: user_rec.user[0]}); + this.set(user_rec); + this.set("watcher_count", 0); + this.on("change:watcher_count", this, function() { + if (this.get("watcher_count") === 0) + this.destroy(); + }); + }, + destroy: function() { + this.trigger("destroyed"); + instance.web.PropertiesMixin.destroy.call(this); + }, + add_watcher: function() { + this.set("watcher_count", this.get("watcher_count") + 1); + }, + remove_watcher: function() { + this.set("watcher_count", this.get("watcher_count") - 1); + }, + }); + + instance.im.ConversationManager = instance.web.Controller.extend({ + init: function(parent) { + this._super(parent); + this.set("right_offset", 0); + this.conversations = []; + this.users = {}; + this.on("change:right_offset", this, this.calc_positions); + this.set("window_focus", true); + this.set("waiting_messages", 0); + this.focus_hdl = _.bind(function() { + this.set("window_focus", true); + }, this); + $(window).bind("focus", this.focus_hdl); + this.blur_hdl = _.bind(function() { + this.set("window_focus", false); + }, this); + $(window).bind("blur", this.blur_hdl); + this.on("change:window_focus", this, this.window_focus_change); + this.window_focus_change(); + this.on("change:waiting_messages", this, this.messages_change); + this.messages_change(); + this.create_ting(); + this.activated = false; + this.users_cache = {}; + this.last = null; + this.unload_event_handler = _.bind(this.unload, this); + }, + start_polling: function() { + var self = this; + return new instance.web.Model("im.user").call("get_by_user_id", [instance.session.uid]).then(function(my_id) { + self.my_id = my_id["id"]; + return self.ensure_users([self.my_id]).then(function() { + var me = self.users_cache[self.my_id]; + delete self.users_cache[self.my_id]; + self.me = me; + self.rpc("/longpolling/im/activated", {}, {shadow: true}).then(function(activated) { + if (activated) { + self.activated = true; + $(window).on("unload", self.unload_event_handler); + self.poll(); + } + }, function(a, e) { + e.preventDefault(); + }); + }); + }); + }, + unload: function() { + return new instance.web.Model("im.user").call("im_disconnect", [], {context: new instance.web.CompoundContext()}); + }, + ensure_users: function(user_ids) { + var no_cache = {}; + _.each(user_ids, function(el) { + if (! this.users_cache[el]) + no_cache[el] = el; + }, this); + var self = this; + if (_.size(no_cache) === 0) + return $.when(); + else + return new instance.web.Model("im.user").call("read", [_.values(no_cache), ["name", "user", "uuid", "im_status"]], + {context: new instance.web.CompoundContext()}).then(function(users) { + self.add_to_user_cache(users); + }); + }, + add_to_user_cache: function(user_recs) { + _.each(user_recs, function(user_rec) { + if (! this.users_cache[user_rec.id]) { + var user = new instance.im.ImUser(this, user_rec); + this.users_cache[user_rec.id] = user; + user.on("destroyed", this, function() { + delete this.users_cache[user_rec.id]; + }); + } + }, this); + }, + get_user: function(user_id) { + return this.users_cache[user_id]; + }, + poll: function() { + var self = this; + var user_ids = _.map(this.users_cache, function(el) { + return el.get("id"); + }); + this.rpc("/longpolling/im/poll", { + last: this.last, + users_watch: user_ids, + context: instance.web.pyeval.eval('context', {}), + }, {shadow: true}).then(function(result) { + _.each(result.users_status, function(el) { + if (self.get_user(el.id)) + self.get_user(el.id).set(el); + }); + self.last = result.last; + var user_ids = _.pluck(_.pluck(result.res, "from_id"), 0); + self.ensure_users(user_ids).then(function() { + _.each(result.res, function(mes) { + var user = self.get_user(mes.from_id[0]); + self.received_message(mes, user); + }); + self.poll(); + }); + }, function(unused, e) { + e.preventDefault(); + setTimeout(_.bind(self.poll, self), ERROR_DELAY); + }); + }, + get_activated: function() { + return this.activated; + }, + create_ting: function() { + var kitten = jQuery.param !== undefined && jQuery.deparam(jQuery.param.querystring()).kitten !== undefined; + this.ting = new Audio(instance.webclient.session.origin + "/im/static/src/audio/" + (kitten ? "purr" : "Ting") + + (new Audio().canPlayType("audio/ogg; codecs=vorbis") ? ".ogg" : ".mp3")); + }, + window_focus_change: function() { + if (this.get("window_focus")) { + this.set("waiting_messages", 0); + } + }, + messages_change: function() { + if (! instance.webclient.set_title_part) + return; + instance.webclient.set_title_part("aa_im_messages", this.get("waiting_messages") === 0 ? undefined : + _.str.sprintf(_t("%d Messages"), this.get("waiting_messages"))); + }, + activate_user: function(user, focus) { + var conv = this.users[user.get('id')]; + if (! conv) { + conv = new instance.im.Conversation(this, user, this.me); + conv.appendTo(instance.client.$el); + conv.on("destroyed", this, function() { + this.conversations = _.without(this.conversations, conv); + delete this.users[conv.user.get('id')]; + this.calc_positions(); + }); + this.conversations.push(conv); + this.users[user.get('id')] = conv; + this.calc_positions(); + } + if (focus) + conv.focus(); + return conv; + }, + received_message: function(message, user) { + if (! this.get("window_focus")) { + this.set("waiting_messages", this.get("waiting_messages") + 1); + this.ting.play(); + this.create_ting(); + } + var conv = this.activate_user(user); + conv.received_message(message); + }, + calc_positions: function() { + var current = this.get("right_offset"); + _.each(_.range(this.conversations.length), function(i) { + this.conversations[i].set("right_position", current); + current += this.conversations[i].$el.outerWidth(true); + }, this); + }, + destroy: function() { + $(window).off("unload", this.unload_event_handler); + $(window).unbind("blur", this.blur_hdl); + $(window).unbind("focus", this.focus_hdl); + this._super(); + }, + }); + + instance.im.Conversation = instance.web.Widget.extend({ + "template": "Conversation", + events: { + "keydown input": "send_message", + "click .oe_im_chatview_close": "destroy", + "click .oe_im_chatview_header": "show_hide", + }, + init: function(parent, user, me) { + this._super(parent); + this.me = me; + this.user = user; + this.user.add_watcher(); + this.set("right_position", 0); + this.shown = true; + this.set("pending", 0); + }, + start: function() { + var change_status = function() { + this.$el.toggleClass("oe_im_chatview_disconnected_status", this.user.get("im_status") === false); + this.$(".oe_im_chatview_online").toggle(this.user.get("im_status") === true); + this._go_bottom(); + }; + this.user.on("change:im_status", this, change_status); + change_status.call(this); + + this.on("change:right_position", this, this.calc_pos); + this.full_height = this.$el.height(); + this.calc_pos(); + this.on("change:pending", this, _.bind(function() { + if (this.get("pending") === 0) { + this.$(".oe_im_chatview_nbr_messages").text(""); + } else { + this.$(".oe_im_chatview_nbr_messages").text("(" + this.get("pending") + ")"); + } + }, this)); + }, + show_hide: function() { + if (this.shown) { + this.$el.animate({ + height: this.$(".oe_im_chatview_header").outerHeight(), + }); + } else { + this.$el.animate({ + height: this.full_height, + }); + } + this.shown = ! this.shown; + if (this.shown) { + this.set("pending", 0); + } + }, + calc_pos: function() { + this.$el.css("right", this.get("right_position")); + }, + received_message: function(message) { + if (this.shown) { + this.set("pending", 0); + } else { + this.set("pending", this.get("pending") + 1); + } + this._add_bubble(this.user, message.message, message.date); + }, + send_message: function(e) { + if(e && e.which !== 13) { + return; + } + var mes = this.$("input").val(); + this.$("input").val(""); + var send_it = _.bind(function() { + var model = new instance.web.Model("im.message"); + return model.call("post", [mes, this.user.get('id')], + {context: new instance.web.CompoundContext()}); + }, this); + var tries = 0; + send_it().then(_.bind(function() { + this._add_bubble(this.me, mes, instance.web.datetime_to_str(new Date())); + }, this), function(error, e) { + e.preventDefault(); + tries += 1; + if (tries < 3) + return send_it(); + }); + }, + _add_bubble: function(user, item, date) { + var items = [item]; + if (user === this.last_user) { + this.last_bubble.remove(); + items = this.last_items.concat(items); + } + this.last_user = user; + this.last_items = items; + date = instance.web.str_to_datetime(date); + var now = new Date(); + var diff = now - date; + if (diff > (1000 * 60 * 60 * 24)) { + date = $.timeago(date); + } else { + date = date.toString(Date.CultureInfo.formatPatterns.shortTime); + } + + this.last_bubble = $(QWeb.render("Conversation.bubble", {"items": items, "user": user, "time": date})); + $(this.$(".oe_im_chatview_content").children()[0]).append(this.last_bubble); + this._go_bottom(); + }, + _go_bottom: function() { + this.$(".oe_im_chatview_content").scrollTop($(this.$(".oe_im_chatview_content").children()[0]).height()); + }, + focus: function() { + this.$(".oe_im_chatview_input").focus(); + }, + destroy: function() { + this.user.remove_watcher(); + this.trigger("destroyed"); + return this._super(); + }, + }); + +}
\ No newline at end of file diff --git a/addons/im/static/src/xml/im.xml b/addons/im/static/src/xml/im.xml new file mode 100644 index 00000000000..795d1b3e698 --- /dev/null +++ b/addons/im/static/src/xml/im.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- vim:fdl=1: +--> +<templates xml:space="preserve"> + <t t-name="InstantMessaging"> + <div class="oe_im"> + <div class="oe_im_frame_header"> + <span class="oe_e oe_im_search_icon">ô</span> + <input class="oe_im_searchbox" t-att-placeholder="_t('Search users...')"/> + <span class="oe_e oe_im_search_clear">[</span> + </div> + <div class="oe_im_users"></div> + <div class="oe_im_content"></div> + </div> + </t> + <t t-name="ImTopButton"> + <div t-att-title='_t("Display Instant Messaging")' class="oe_topbar_item oe_topbar_imbutton"> + <span class="oe_e">+</span> + </div> + </t> + <t t-name="UserWidget"> + <div class="oe_im_user"> + <span class="oe_im_user_clip"> + <img t-att-src='widget.user.get("image_url")' class="oe_im_user_avatar"/> + </span> + <span class="oe_im_user_name"><t t-esc="widget.user.get('name')"/></span> + <img t-att-src="_s +'/im/static/src/img/green.png'" class="oe_im_user_online"/> + </div> + </t> + <t t-name="Conversation"> + <div class="oe_im_chatview"> + <div class="oe_im_chatview_header"> + <img t-att-src="_s +'/im/static/src/img/green.png'" class="oe_im_chatview_online"/> + <t t-esc="widget.user.get('name') || 'Anonymous'"/> + <scan class="oe_im_chatview_nbr_messages" /> + <button class="oe_im_chatview_close">×</button> + </div> + <div class="oe_im_chatview_disconnected"> + <t t-esc='_.str.sprintf(_t("%s is offline. He/She will receive your messages on his/her next connection."), widget.user.get("name") || "Anonymous")'/> + </div> + <div class="oe_im_chatview_content"> + <div></div> + </div> + <div class="oe_im_chatview_footer"> + <input class="oe_im_chatview_input" t-att-placeholder="_t('Say something...')" /> + </div> + </div> + </t> + <t t-name="Conversation.bubble"> + <div class="oe_im_chatview_bubble"> + <div class="oe_im_chatview_clip"> + <img class="oe_im_chatview_avatar" t-att-src='user.get("image_url")'/> + </div> + <div class="oe_im_chatview_from"><t t-esc="user.get('name') || 'Anonymous'"/></div> + <div class="oe_im_chatview_bubble_list"> + <t t-foreach="items" t-as="item"> + <div class="oe_im_chatview_bubble_item"><t t-esc="item"/></div> + </t> + </div> + <div class="oe_im_chatview_time"><t t-esc="time"/></div> + </div> + </t> +</templates>
\ No newline at end of file |
