Fixed new message counter, fixed active/not active state of agent and refactoring of web socket event backends.
This commit is contained in:
parent
600649d51a
commit
1979376d21
17 changed files with 163 additions and 187 deletions
|
@ -32,6 +32,7 @@ class App.CustomerChat extends App.Controller
|
|||
(data) =>
|
||||
@meta = data
|
||||
@updateMeta()
|
||||
@interval(@pushState, 20000, 'pushState')
|
||||
)
|
||||
App.Event.bind(
|
||||
'chat_session_start'
|
||||
|
@ -43,8 +44,6 @@ class App.CustomerChat extends App.Controller
|
|||
|
||||
App.WebSocket.send(event:'chat_status_agent')
|
||||
|
||||
@interval(@pushState, 16000)
|
||||
|
||||
pushState: =>
|
||||
App.WebSocket.send(
|
||||
event:'chat_agent_state'
|
||||
|
@ -59,13 +58,23 @@ class App.CustomerChat extends App.Controller
|
|||
@navupdate '#customer_chat'
|
||||
|
||||
counter: =>
|
||||
if @meta.waiting_chat_count
|
||||
if @waitingChatCountLast isnt @meta.waiting_chat_count
|
||||
@sounds.chat_new.play()
|
||||
@waitingChatCountLast = @meta.waiting_chat_count
|
||||
counter = 0
|
||||
|
||||
return @messageCounter + @meta.waiting_chat_count
|
||||
@messageCounter
|
||||
# get count of controller messages
|
||||
if @meta.waiting_chat_count
|
||||
counter += @meta.waiting_chat_count
|
||||
|
||||
# play on changes
|
||||
if @lastWaitingChatCount isnt counter
|
||||
@sounds.chat_new.play()
|
||||
@lastWaitingChatCount = counter
|
||||
|
||||
# collect chat window messages
|
||||
for key, value of @chatWindows
|
||||
if value
|
||||
counter += value.unreadMessages()
|
||||
|
||||
@messageCounter = counter
|
||||
|
||||
switch: (state = undefined) =>
|
||||
|
||||
|
@ -85,7 +94,7 @@ class App.CustomerChat extends App.Controller
|
|||
updateNavMenu: =>
|
||||
delay = ->
|
||||
App.Event.trigger('menu:render')
|
||||
@delay(delay, 200)
|
||||
@delay(delay, 200, 'updateNavMenu')
|
||||
|
||||
updateMeta: =>
|
||||
if @meta.waiting_chat_count
|
||||
|
@ -107,7 +116,8 @@ class App.CustomerChat extends App.Controller
|
|||
chat = new chatWindow
|
||||
name: "#{session.created_at}"
|
||||
session: session
|
||||
callback: @removeChat
|
||||
removeCallback: @removeChat
|
||||
messageCallback: @updateNavMenu
|
||||
|
||||
@on 'layout-has-changed', @propagateLayoutChange
|
||||
|
||||
|
@ -115,7 +125,6 @@ class App.CustomerChat extends App.Controller
|
|||
@chatWindows[session.session_id] = chat
|
||||
|
||||
removeChat: (session_id) =>
|
||||
console.log('removeChat', session_id, @chatWindows[session_id])
|
||||
delete @chatWindows[session_id]
|
||||
|
||||
propagateLayoutChange: (event) =>
|
||||
|
@ -177,6 +186,7 @@ class chatWindow extends App.Controller
|
|||
@lastAddedType
|
||||
@isTyping = false
|
||||
@render()
|
||||
@resetUnreadMessages()
|
||||
|
||||
@on 'layout-change', @scrollToBottom
|
||||
|
||||
|
@ -231,8 +241,8 @@ class chatWindow extends App.Controller
|
|||
data:
|
||||
session_id: @session.session_id
|
||||
)
|
||||
if @callback
|
||||
@callback(@session.session_id)
|
||||
if @removeCallback
|
||||
@removeCallback(@session.session_id)
|
||||
|
||||
release: =>
|
||||
@trigger 'closed'
|
||||
|
@ -241,6 +251,7 @@ class chatWindow extends App.Controller
|
|||
clearUnread: =>
|
||||
@$('.chat-message--new').removeClass('chat-message--new')
|
||||
@updateModified(false)
|
||||
@resetUnreadMessages()
|
||||
|
||||
onKeydown: (event) =>
|
||||
TABKEY = 9;
|
||||
|
@ -304,9 +315,23 @@ class chatWindow extends App.Controller
|
|||
@addMessage(message, 'customer', !isFocused)
|
||||
|
||||
if !isFocused
|
||||
@addUnreadMessages()
|
||||
@updateModified(true)
|
||||
@sounds.message.play()
|
||||
|
||||
unreadMessages: =>
|
||||
@unreadMessagesCounter
|
||||
|
||||
addUnreadMessages: =>
|
||||
if @messageCallback
|
||||
@messageCallback(@session.session_id)
|
||||
@unreadMessagesCounter += 1
|
||||
|
||||
resetUnreadMessages: =>
|
||||
if @messageCallback
|
||||
@messageCallback(@session.session_id)
|
||||
@unreadMessagesCounter = 0
|
||||
|
||||
addMessage: (message, sender, isNew) =>
|
||||
@maybeAddTimestamp()
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ class Chat < ApplicationModel
|
|||
def self.agent_state(user_id)
|
||||
return { state: 'chat_disabled' } if !Setting.get('chat')
|
||||
actice_sessions = []
|
||||
Chat::Session.where(state: 'running').order('created_at ASC').each {|session|
|
||||
Chat::Session.where(state: 'running', user_id: user_id).order('created_at ASC').each {|session|
|
||||
session_attributes = session.attributes
|
||||
session_attributes['messages'] = []
|
||||
Chat::Message.where(chat_session_id: session.id).each { |message|
|
||||
|
@ -127,6 +127,26 @@ class Chat::Session < ApplicationModel
|
|||
def generate_session_id
|
||||
self.session_id = Digest::MD5.hexdigest(Time.zone.now.to_s + rand(99_999_999_999_999).to_s)
|
||||
end
|
||||
|
||||
def add_recipient(client_id, store = false)
|
||||
if !self.preferences[:participants]
|
||||
self.preferences[:participants] = []
|
||||
end
|
||||
return self.preferences[:participants] if self.preferences[:participants].include?(client_id)
|
||||
self.preferences[:participants].push client_id
|
||||
if store
|
||||
self.save
|
||||
end
|
||||
self.preferences[:participants]
|
||||
end
|
||||
|
||||
def send_to_recipients(message, ignore_client_id)
|
||||
self.preferences[:participants].each {|local_client_id|
|
||||
next if local_client_id == ignore_client_id
|
||||
Sessions.send(local_client_id, message)
|
||||
}
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
class Chat::Message < ApplicationModel
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
class CreateChat < ActiveRecord::Migration
|
||||
def up
|
||||
=begin
|
||||
ActiveRecord::Migration.drop_table :chats
|
||||
ActiveRecord::Migration.drop_table :chat_topics
|
||||
ActiveRecord::Migration.drop_table :chat_sessions
|
||||
ActiveRecord::Migration.drop_table :chat_messages
|
||||
ActiveRecord::Migration.drop_table :chat_agents
|
||||
=end
|
||||
create_table :chats do |t|
|
||||
t.string :name, limit: 250, null: true
|
||||
t.integer :max_queue, null: false, default: 5
|
||||
|
|
|
@ -9,8 +9,11 @@ class Sessions::Event
|
|||
return { error: "No such event #{event}" }
|
||||
end
|
||||
|
||||
instance = backend.new(data, session, client_id)
|
||||
result = instance.pre_check
|
||||
return result if result
|
||||
ActiveRecord::Base.establish_connection
|
||||
result = backend.run(data, session, client_id)
|
||||
result = instance.run
|
||||
ActiveRecord::Base.remove_connection
|
||||
result
|
||||
end
|
||||
|
|
|
@ -1,21 +1,11 @@
|
|||
class Sessions::Event::ChatAgentState
|
||||
class Sessions::Event::ChatAgentState < Sessions::Event::ChatBase
|
||||
|
||||
def self.run(data, session, _client_id)
|
||||
|
||||
# check if feature is enabled
|
||||
if !Setting.get('chat')
|
||||
return {
|
||||
event: 'chat_agent_state',
|
||||
data: {
|
||||
state: 'chat_disabled',
|
||||
},
|
||||
}
|
||||
end
|
||||
def run
|
||||
|
||||
# only agents can do this
|
||||
chat_id = 1
|
||||
chat = Chat.find_by(id: chat_id)
|
||||
if !session['id']
|
||||
if !@session['id']
|
||||
return {
|
||||
event: 'chat_agent_state',
|
||||
data: {
|
||||
|
@ -25,13 +15,13 @@ class Sessions::Event::ChatAgentState
|
|||
}
|
||||
end
|
||||
|
||||
Chat::Agent.state(session['id'], data['data']['active'])
|
||||
Chat::Agent.state(@session['id'], @data['data']['active'])
|
||||
|
||||
{
|
||||
event: 'chat_agent_state',
|
||||
data: {
|
||||
state: 'ok',
|
||||
active: data['data']['active'],
|
||||
active: @data['data']['active'],
|
||||
},
|
||||
}
|
||||
end
|
||||
|
|
25
lib/sessions/event/chat_base.rb
Normal file
25
lib/sessions/event/chat_base.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
class Sessions::Event::ChatBase
|
||||
|
||||
def initialize(data, session, client_id)
|
||||
@data = data
|
||||
@session = session
|
||||
@client_id = client_id
|
||||
|
||||
end
|
||||
|
||||
def pre_check
|
||||
|
||||
# check if feature is enabled
|
||||
if !Setting.get('chat')
|
||||
return {
|
||||
event: 'chat_error',
|
||||
data: {
|
||||
state: 'chat_disabled',
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
end
|
|
@ -1,18 +1,8 @@
|
|||
class Sessions::Event::ChatSessionClose
|
||||
class Sessions::Event::ChatSessionClose < Sessions::Event::ChatBase
|
||||
|
||||
def self.run(data, _session, _client_id)
|
||||
def run
|
||||
|
||||
# check if feature is enabled
|
||||
if !Setting.get('chat')
|
||||
return {
|
||||
event: 'chat_status_close',
|
||||
data: {
|
||||
state: 'chat_disabled',
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
if !data['data'] || !data['data']['session_id']
|
||||
if !@data['data'] || !@data['data']['session_id']
|
||||
return {
|
||||
event: 'chat_status_close',
|
||||
data: {
|
||||
|
@ -21,7 +11,7 @@ class Sessions::Event::ChatSessionClose
|
|||
}
|
||||
end
|
||||
|
||||
chat_session = Chat::Session.find_by(session_id: data['data']['session_id'])
|
||||
chat_session = Chat::Session.find_by(session_id: @data['data']['session_id'])
|
||||
if !chat_session
|
||||
return {
|
||||
event: 'chat_status_close',
|
||||
|
|
|
@ -1,16 +1,6 @@
|
|||
class Sessions::Event::ChatSessionInit
|
||||
class Sessions::Event::ChatSessionInit < Sessions::Event::ChatBase
|
||||
|
||||
def self.run(data, _session, client_id)
|
||||
|
||||
# check if feature is enabled
|
||||
if !Setting.get('chat')
|
||||
return {
|
||||
event: 'chat_session_init',
|
||||
data: {
|
||||
state: 'chat_disabled',
|
||||
},
|
||||
}
|
||||
end
|
||||
def run
|
||||
|
||||
chat_id = 1
|
||||
chat = Chat.find_by(id: chat_id)
|
||||
|
@ -29,17 +19,17 @@ class Sessions::Event::ChatSessionInit
|
|||
name: '',
|
||||
state: 'waiting',
|
||||
preferences: {
|
||||
participants: [client_id],
|
||||
participants: [@client_id],
|
||||
},
|
||||
)
|
||||
|
||||
# send update to agents
|
||||
User.where(active: true).each {|user|
|
||||
# send broadcast to agents
|
||||
Chat::Agent.where(active: true).each {|item|
|
||||
data = {
|
||||
event: 'chat_status_agent',
|
||||
data: Chat.agent_state(user.id),
|
||||
data: Chat.agent_state(item.updated_by_id),
|
||||
}
|
||||
Sessions.send_to(user.id, data)
|
||||
Sessions.send_to(item.updated_by_id, data)
|
||||
}
|
||||
|
||||
# return new session
|
||||
|
|
|
@ -1,18 +1,8 @@
|
|||
class Sessions::Event::ChatSessionMessage
|
||||
class Sessions::Event::ChatSessionMessage < Sessions::Event::ChatBase
|
||||
|
||||
def self.run(data, session, client_id)
|
||||
def run
|
||||
|
||||
# check if feature is enabled
|
||||
if !Setting.get('chat')
|
||||
return {
|
||||
event: 'chat_session_message',
|
||||
data: {
|
||||
state: 'chat_disabled',
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
if !data['data'] || !data['data']['session_id']
|
||||
if !@data['data'] || !@data['data']['session_id']
|
||||
return {
|
||||
event: 'chat_session_message',
|
||||
data: {
|
||||
|
@ -21,7 +11,7 @@ class Sessions::Event::ChatSessionMessage
|
|||
}
|
||||
end
|
||||
|
||||
chat_session = Chat::Session.find_by(session_id: data['data']['session_id'])
|
||||
chat_session = Chat::Session.find_by(session_id: @data['data']['session_id'])
|
||||
if !chat_session
|
||||
return {
|
||||
event: 'chat_session_message',
|
||||
|
@ -32,12 +22,12 @@ class Sessions::Event::ChatSessionMessage
|
|||
end
|
||||
|
||||
user_id = nil
|
||||
if session
|
||||
user_id = session['id']
|
||||
if @session
|
||||
user_id = @session['id']
|
||||
end
|
||||
chat_message = Chat::Message.create(
|
||||
chat_session_id: chat_session.id,
|
||||
content: data['data']['content'],
|
||||
content: @data['data']['content'],
|
||||
created_by_id: user_id,
|
||||
)
|
||||
message = {
|
||||
|
@ -49,10 +39,7 @@ class Sessions::Event::ChatSessionMessage
|
|||
}
|
||||
|
||||
# send to participents
|
||||
chat_session.preferences[:participants].each {|local_client_id|
|
||||
next if local_client_id == client_id
|
||||
Sessions.send(local_client_id, message)
|
||||
}
|
||||
chat_session.send_to_recipients(message, @client_id)
|
||||
|
||||
# send chat_session_init to agent
|
||||
{
|
||||
|
|
|
@ -1,16 +1,6 @@
|
|||
class Sessions::Event::ChatSessionStart
|
||||
class Sessions::Event::ChatSessionStart < Sessions::Event::ChatBase
|
||||
|
||||
def self.run(data, session, client_id)
|
||||
|
||||
# check if feature is enabled
|
||||
if !Setting.get('chat')
|
||||
return {
|
||||
event: 'chat_session_start',
|
||||
data: {
|
||||
state: 'chat_disabled',
|
||||
},
|
||||
}
|
||||
end
|
||||
def run
|
||||
|
||||
# find first in waiting list
|
||||
chat_session = Chat::Session.where(state: 'waiting').order('created_at ASC').first
|
||||
|
@ -23,9 +13,9 @@ class Sessions::Event::ChatSessionStart
|
|||
},
|
||||
}
|
||||
end
|
||||
chat_session.user_id = session['id']
|
||||
chat_session.user_id = @session['id']
|
||||
chat_session.state = 'running'
|
||||
chat_session.preferences[:participants].push client_id
|
||||
chat_session.preferences[:participants] = chat_session.add_recipient(@client_id)
|
||||
chat_session.save
|
||||
|
||||
# send chat_session_init to client
|
||||
|
@ -42,11 +32,7 @@ class Sessions::Event::ChatSessionStart
|
|||
session_id: chat_session.session_id,
|
||||
},
|
||||
}
|
||||
|
||||
chat_session.preferences[:participants].each {|local_client_id|
|
||||
next if local_client_id == client_id
|
||||
Sessions.send(local_client_id, data)
|
||||
}
|
||||
chat_session.send_to_recipients(data, @client_id)
|
||||
|
||||
# send chat_session_init to agent
|
||||
{
|
||||
|
|
|
@ -1,18 +1,8 @@
|
|||
class Sessions::Event::ChatSessionTyping
|
||||
class Sessions::Event::ChatSessionTyping < Sessions::Event::ChatBase
|
||||
|
||||
def self.run(data, session, client_id)
|
||||
def run
|
||||
|
||||
# check if feature is enabled
|
||||
if !Setting.get('chat')
|
||||
return {
|
||||
event: 'chat_session_typing',
|
||||
data: {
|
||||
state: 'chat_disabled',
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
if !data['data'] || !data['data']['session_id']
|
||||
if !@data['data'] || !@data['data']['session_id']
|
||||
return {
|
||||
event: 'chat_session_typing',
|
||||
data: {
|
||||
|
@ -21,7 +11,7 @@ class Sessions::Event::ChatSessionTyping
|
|||
}
|
||||
end
|
||||
|
||||
chat_session = Chat::Session.find_by(session_id: data['data']['session_id'])
|
||||
chat_session = Chat::Session.find_by(session_id: @data['data']['session_id'])
|
||||
if !chat_session
|
||||
return {
|
||||
event: 'chat_session_typing',
|
||||
|
@ -32,8 +22,8 @@ class Sessions::Event::ChatSessionTyping
|
|||
end
|
||||
|
||||
user_id = nil
|
||||
if session
|
||||
user_id = session['id']
|
||||
if @session
|
||||
user_id = @session['id']
|
||||
end
|
||||
message = {
|
||||
event: 'chat_session_typing',
|
||||
|
@ -44,10 +34,7 @@ class Sessions::Event::ChatSessionTyping
|
|||
}
|
||||
|
||||
# send to participents
|
||||
chat_session.preferences[:participants].each {|local_client_id|
|
||||
next if local_client_id == client_id
|
||||
Sessions.send(local_client_id, message)
|
||||
}
|
||||
chat_session.send_to_recipients(message, @client_id)
|
||||
|
||||
# send chat_session_init to agent
|
||||
{
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
class Sessions::Event::ChatStatus
|
||||
|
||||
def self.run(_data, _session, _client_id)
|
||||
|
||||
# check if feature is enabled
|
||||
if !Setting.get('chat')
|
||||
return {
|
||||
event: 'chat_status',
|
||||
data: {
|
||||
state: 'chat_disabled',
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
chat_id = 1
|
||||
chat = Chat.find_by(id: chat_id)
|
||||
if !chat
|
||||
return {
|
||||
event: 'chat_status',
|
||||
data: {
|
||||
state: 'no_such_chat',
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
{
|
||||
event: 'chat_status',
|
||||
data: chat.state,
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,26 +1,22 @@
|
|||
class Sessions::Event::ChatStatusAgent
|
||||
class Sessions::Event::ChatStatusAgent < Sessions::Event::ChatBase
|
||||
|
||||
def self.run(_data, session, _client_id)
|
||||
def run
|
||||
|
||||
# check if user has permissions
|
||||
|
||||
# check if feature is enabled
|
||||
if !Setting.get('chat')
|
||||
return {
|
||||
event: 'chat_status_agent',
|
||||
data: {
|
||||
state: 'chat_disabled',
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
# renew timestamps
|
||||
state = Chat::Agent.state(session['id'])
|
||||
Chat::Agent.state(session['id'], state)
|
||||
state = Chat::Agent.state(@session['id'])
|
||||
Chat::Agent.state(@session['id'], state)
|
||||
|
||||
|
||||
# update recipients of existing sessions
|
||||
Chat::Session.where(state: 'running', user_id: @session['id']).order('created_at ASC').each {|chat_session|
|
||||
chat_session.add_recipient(@client_id, true)
|
||||
}
|
||||
{
|
||||
event: 'chat_status_agent',
|
||||
data: Chat.agent_state(session['id']),
|
||||
data: Chat.agent_state(@session['id']),
|
||||
}
|
||||
end
|
||||
|
||||
|
|
21
lib/sessions/event/chat_status_customer.rb
Normal file
21
lib/sessions/event/chat_status_customer.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
class Sessions::Event::ChatStatusCustomer < Sessions::Event::ChatBase
|
||||
|
||||
def run
|
||||
|
||||
chat_id = 1
|
||||
chat = Chat.find_by(id: chat_id)
|
||||
if !chat
|
||||
return {
|
||||
event: 'chat_status_customer',
|
||||
data: {
|
||||
state: 'no_such_chat',
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
{
|
||||
event: 'chat_status_customer',
|
||||
data: chat.state,
|
||||
}
|
||||
end
|
||||
end
|
|
@ -64,7 +64,6 @@ do($ = window.jQuery, window) ->
|
|||
console.log('ws connected')
|
||||
@send 'chat_status_customer'
|
||||
|
||||
|
||||
@ws.onmessage = @onWebSocketMessage
|
||||
|
||||
@ws.onclose = (e) =>
|
||||
|
|
|
@ -162,7 +162,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
|
|||
this.ws.onopen = (function(_this) {
|
||||
return function() {
|
||||
console.log('ws connected');
|
||||
return _this.send('chat_status');
|
||||
return _this.send('chat_status_customer');
|
||||
};
|
||||
})(this);
|
||||
this.ws.onmessage = this.onWebSocketMessage;
|
||||
|
@ -196,7 +196,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
|
|||
};
|
||||
|
||||
ZammadChat.prototype.onWebSocketMessage = function(e) {
|
||||
var delay, i, len, pipe, pipes;
|
||||
var i, len, pipe, pipes;
|
||||
pipes = JSON.parse(e.data);
|
||||
console.log('debug', 'ws:onmessage', pipes);
|
||||
for (i = 0, len = pipes.length; i < len; i++) {
|
||||
|
@ -213,15 +213,6 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
|
|||
return;
|
||||
}
|
||||
this.onAgentTypingStart();
|
||||
if (this.stopTypingId) {
|
||||
clearTimeout(this.stopTypingId);
|
||||
}
|
||||
delay = (function(_this) {
|
||||
return function() {
|
||||
return _this.onAgentTypingEnd();
|
||||
};
|
||||
})(this);
|
||||
this.stopTypingId = setTimeout(delay, 3000);
|
||||
break;
|
||||
case 'chat_session_start':
|
||||
switch (pipe.data.state) {
|
||||
|
@ -239,7 +230,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
|
|||
this.session_id = pipe.data.session_id;
|
||||
}
|
||||
break;
|
||||
case 'chat_status':
|
||||
case 'chat_status_customer':
|
||||
switch (pipe.data.state) {
|
||||
case 'online':
|
||||
this.onReady();
|
||||
|
@ -393,6 +384,10 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
|
|||
};
|
||||
|
||||
ZammadChat.prototype.onAgentTypingStart = function() {
|
||||
if (this.stopTypingId) {
|
||||
clearTimeout(this.stopTypingId);
|
||||
}
|
||||
this.stopTypingId = setTimeout(this.onAgentTypingEnd, 3000);
|
||||
if (this.el.find('.zammad-chat-message--typing').size()) {
|
||||
return;
|
||||
}
|
||||
|
|
2
public/assets/chat/chat.min.js
vendored
2
public/assets/chat/chat.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue