Fixed new message counter, fixed active/not active state of agent and refactoring of web socket event backends.

This commit is contained in:
Martin Edenhofer 2015-11-11 14:10:26 +01:00
parent 600649d51a
commit 1979376d21
17 changed files with 163 additions and 187 deletions

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View 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

View file

@ -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',

View file

@ -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

View file

@ -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
{

View file

@ -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
{

View file

@ -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
{

View file

@ -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

View file

@ -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

View 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

View file

@ -64,7 +64,6 @@ do($ = window.jQuery, window) ->
console.log('ws connected')
@send 'chat_status_customer'
@ws.onmessage = @onWebSocketMessage
@ws.onclose = (e) =>

View file

@ -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;
}

File diff suppressed because one or more lines are too long