diff --git a/app/assets/javascripts/app/controllers/chat.coffee b/app/assets/javascripts/app/controllers/chat.coffee index 772ce86cf..20ddf59bb 100644 --- a/app/assets/javascripts/app/controllers/chat.coffee +++ b/app/assets/javascripts/app/controllers/chat.coffee @@ -36,7 +36,6 @@ class App.CustomerChat extends App.Controller @bind( 'chat_session_start' (data) => - App.WebSocket.send(event:'chat_status_agent') if data.session @addChat(data.session) ) @@ -45,7 +44,6 @@ class App.CustomerChat extends App.Controller -> App.WebSocket.send(event:'chat_status_agent') ) - App.WebSocket.send(event:'chat_status_agent') pushState: => @@ -101,7 +99,7 @@ class App.CustomerChat extends App.Controller @delay(delay, 200, 'updateNavMenu') updateMeta: => - if @meta.waiting_chat_count && @maxChats > @currentChatCount() + if @meta.waiting_chat_count && @maxChats > @windowCount() @$('.js-acceptChat').addClass('is-clickable is-blinking') else @$('.js-acceptChat').removeClass('is-clickable is-blinking') @@ -134,11 +132,9 @@ class App.CustomerChat extends App.Controller windowCount: => count = 0 - for chat of @chatWindows - count++ - - return count + count++ + count removeChat: (session_id) => delete @chatWindows[session_id] @@ -149,15 +145,8 @@ class App.CustomerChat extends App.Controller for session_id, chat of @chatWindows chat.trigger 'layout-changed' - currentChatCount: => - currentChats = 0 - for key, value of @chatWindows - if @chatWindows[key] - currentChats += 1 - currentChats - acceptChat: => - return if @currentChatCount() >= @maxChats + return if @windowCount() >= @maxChats App.WebSocket.send(event:'chat_session_start') class CustomerChatRouter extends App.ControllerPermanent diff --git a/app/models/chat.rb b/app/models/chat.rb index d730e2927..bd1a4a64a 100644 --- a/app/models/chat.rb +++ b/app/models/chat.rb @@ -10,43 +10,56 @@ class Chat < ApplicationModel # reconnect if session_id chat_session = Chat::Session.find_by(session_id: session_id, state: %w(waiting running)) - user = nil - if chat_session && chat_session.user_id - chat_user = User.find(chat_session.user_id) - url = nil - if chat_user.image && chat_user.image != 'none' - url = "/api/v1/users/image/#{chat_user.image}" - end - user = { - name: chat_user.fullname, - avatar: url, - } - end if chat_session - session = Chat.session_state(session_id) - if session && !session.empty? + if chat_session.state == 'running' + user = nil + if chat_session.user_id + chat_user = User.find(chat_session.user_id) + url = nil + if chat_user.image && chat_user.image != 'none' + url = "/api/v1/users/image/#{chat_user.image}" + end + user = { + name: chat_user.fullname, + avatar: url, + } + + # get queue postion if needed + session = Chat.session_state(session_id) + if session + return { + state: 'reconnect', + session: session, + agent: user, + } + end + end + elsif chat_session.state == 'waiting' return { state: 'reconnect', - session: session, - agent: user, + position: chat_session.position, } end end end - if Chat::Agent.where(active: true).where('updated_at > ?', Time.zone.now - 2.minutes).count > 0 - if active_chat_count >= max_queue - return { - state: 'no_seats_available', - queue: seads_available, - } - else - return { state: 'online' } - end + # check if agents are available + available_agents = Chat::Agent.where(active: true).where('updated_at > ?', Time.zone.now - 2.minutes).count + if available_agents == 0 + return { state: 'offline' } end - { state: 'offline' } + # if all seads are used + if active_chat_count >= max_queue + return { + state: 'no_seats_available', + queue: seads_available, + } + end + + # seads are available + { state: 'online' } end def self.session_state(session_id) @@ -111,81 +124,3 @@ class Chat < ApplicationModel seads_total(diff) - active_chat_count end end - -class Chat::Topic < ApplicationModel -end - -class Chat::Agent < ApplicationModel - - def seads_available - concurrent - active_chat_count - end - - def active_chat_count - Chat::Session.where(state: %w(waiting running), user_id: updated_by_id).count - end - - def self.state(user_id, state = nil) - chat_agent = Chat::Agent.find_by( - updated_by_id: user_id - ) - if state.nil? - return false if !chat_agent - return chat_agent.active - end - if chat_agent - chat_agent.active = state - chat_agent.updated_at = Time.zone.now - chat_agent.save - else - Chat::Agent.create( - active: state, - updated_by_id: user_id, - created_by_id: user_id, - ) - end - end - - def self.create_or_update(params) - chat_agent = Chat::Agent.find_by( - updated_by_id: params[:updated_by_id] - ) - if chat_agent - chat_agent.update_attributes(params) - else - Chat::Agent.create(params) - end - end -end - -class Chat::Session < ApplicationModel - before_create :generate_session_id - store :preferences - - 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 !preferences[:participants] - preferences[:participants] = [] - end - return preferences[:participants] if preferences[:participants].include?(client_id) - preferences[:participants].push client_id - if store - save - end - preferences[:participants] - end - - def send_to_recipients(message, ignore_client_id = nil) - 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 -end diff --git a/app/models/chat/agent.rb b/app/models/chat/agent.rb new file mode 100644 index 000000000..fe3002bcb --- /dev/null +++ b/app/models/chat/agent.rb @@ -0,0 +1,42 @@ +class Chat::Agent < ApplicationModel + + def seads_available + concurrent - active_chat_count + end + + def active_chat_count + Chat::Session.where(state: %w(waiting running), user_id: updated_by_id).count + end + + def self.state(user_id, state = nil) + chat_agent = Chat::Agent.find_by( + updated_by_id: user_id + ) + if state.nil? + return false if !chat_agent + return chat_agent.active + end + if chat_agent + chat_agent.active = state + chat_agent.updated_at = Time.zone.now + chat_agent.save + else + Chat::Agent.create( + active: state, + updated_by_id: user_id, + created_by_id: user_id, + ) + end + end + + def self.create_or_update(params) + chat_agent = Chat::Agent.find_by( + updated_by_id: params[:updated_by_id] + ) + if chat_agent + chat_agent.update_attributes(params) + else + Chat::Agent.create(params) + end + end +end diff --git a/app/models/chat/message.rb b/app/models/chat/message.rb new file mode 100644 index 000000000..5076f0951 --- /dev/null +++ b/app/models/chat/message.rb @@ -0,0 +1,2 @@ +class Chat::Message < ApplicationModel +end diff --git a/app/models/chat/session.rb b/app/models/chat/session.rb new file mode 100644 index 000000000..4932fc1e1 --- /dev/null +++ b/app/models/chat/session.rb @@ -0,0 +1,38 @@ +class Chat::Session < ApplicationModel + before_create :generate_session_id + store :preferences + + 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 !preferences[:participants] + preferences[:participants] = [] + end + return preferences[:participants] if preferences[:participants].include?(client_id) + preferences[:participants].push client_id + if store + save + end + preferences[:participants] + end + + def send_to_recipients(message, ignore_client_id = nil) + preferences[:participants].each {|local_client_id| + next if local_client_id == ignore_client_id + Sessions.send(local_client_id, message) + } + true + end + + def position + return if state != 'waiting' + position = 0 + Chat::Session.where(state: 'waiting').order('created_at ASC').each {|chat_session| + position += 1 + break if chat_session.id == id + } + position + end +end diff --git a/app/models/chat/topic.rb b/app/models/chat/topic.rb new file mode 100644 index 000000000..413576f67 --- /dev/null +++ b/app/models/chat/topic.rb @@ -0,0 +1,2 @@ +class Chat::Topic < ApplicationModel +end diff --git a/lib/sessions/event/chat_session_init.rb b/lib/sessions/event/chat_session_init.rb index 6efb8d64a..853173fbb 100644 --- a/lib/sessions/event/chat_session_init.rb +++ b/lib/sessions/event/chat_session_init.rb @@ -24,13 +24,7 @@ class Sessions::Event::ChatSessionInit < Sessions::Event::ChatBase ) # send broadcast to agents - Chat::Agent.where(active: true).each {|item| - data = { - event: 'chat_status_agent', - data: Chat.agent_state(item.updated_by_id), - } - Sessions.send_to(item.updated_by_id, data) - } + broadcast_agent_state_update # return new session { diff --git a/public/assets/chat/chat.coffee b/public/assets/chat/chat.coffee index 65cd4adb7..ac57c6f6a 100644 --- a/public/assets/chat/chat.coffee +++ b/public/assets/chat/chat.coffee @@ -10,13 +10,14 @@ do($ = window.jQuery, window) -> defaults: invitationPhrase: 'Chat with us!' agentPhrase: ' is helping you' - show: false + show: true target: $('body') host: '' port: 6042 + debug: false _messageCount: 0 - isOpen: false + isOpen: true blinkOnlineInterval: null stopBlinOnlineStateTimeout: null showTimeEveryXMinutes: 1 @@ -26,7 +27,6 @@ do($ = window.jQuery, window) -> isTyping: false isOnline: true initialQueueDelay: 10000 - debug: true wsReconnectEnable: true strings: 'Online': 'Online' @@ -54,7 +54,7 @@ do($ = window.jQuery, window) -> translation log: (level, string...) => - return if !@debug && level is 'debug' + return if !@options.debug && level is 'debug' string.unshift(level) console.log.apply console, string @@ -67,6 +67,12 @@ do($ = window.jQuery, window) -> return window.zammadChatTemplates[name](options) constructor: (options) -> + + # check prerequisites + if !window.WebSocket or !sessionStorage + @log 'notice', 'Chat: Browser not supported!' + return + @options = $.extend {}, @defaults, options @el = $(@view('chat')(@options)) @options.target.append @el @@ -80,14 +86,8 @@ do($ = window.jQuery, window) -> keydown: @checkForEnter input: @onInput - if !window.WebSocket or !sessionStorage - @log 'notice', 'Chat: Browser not supported!' - return - @wsConnect() - #@onReady() - checkForEnter: (event) => if not event.shiftKey and event.keyCode is 13 event.preventDefault() @@ -146,16 +146,22 @@ do($ = window.jQuery, window) -> reopenSession: (data) => unfinishedMessage = sessionStorage.getItem 'unfinished_message' - @onConnectionEstablished(data) + # rerender chat history + if data.agent + @onConnectionEstablished(data) - for message in data.session - @renderMessage - message: message.content - id: message.id - from: if message.created_by_id then 'agent' else 'customer' + for message in data.session + @renderMessage + message: message.content + id: message.id + from: if message.created_by_id then 'agent' else 'customer' - if unfinishedMessage - @input.val unfinishedMessage + if unfinishedMessage + @input.val unfinishedMessage + + # show wait list + if data.position + @onQueue data @show() @open() @@ -276,7 +282,6 @@ do($ = window.jQuery, window) -> # stop delay of initial queue position if @onInitialQueueDelayId clearTimeout(@onInitialQueueDelayId) - #@ws.close() sessionStorage.removeItem 'sessionId' sessionStorage.removeItem 'unfinished_message' diff --git a/public/assets/chat/index.html b/public/assets/chat/index.html index 4098318cf..0fcb3460b 100644 --- a/public/assets/chat/index.html +++ b/public/assets/chat/index.html @@ -134,8 +134,11 @@ } -->