Added chat settings. Improved chat event backends.

This commit is contained in:
Martin Edenhofer 2015-11-25 10:33:39 +01:00
parent 838b91792c
commit 39b37e048b
16 changed files with 262 additions and 120 deletions

View file

@ -3,6 +3,7 @@ class App.CustomerChat extends App.Controller
events:
'click .js-acceptChat': 'acceptChat'
'click .js-settings': 'settings'
sounds:
chat_new: new Audio('assets/sounds/chat_new.mp3')
@ -19,7 +20,11 @@ class App.CustomerChat extends App.Controller
return
@chatWindows = {}
@maxChats = 4
@maxChatWindows = 4
preferences = @Session.get('preferences')
if preferences && preferences.chat && preferences.chat.max_windows
@maxChatWindows = parseInt(preferences.chat.max_windows)
@pushStateStarted = false
@messageCounter = 0
@meta =
@ -33,6 +38,8 @@ class App.CustomerChat extends App.Controller
# update navbar on new status
@bind('chat_status_agent', (data) =>
if data.assets
App.Collection.loadAssets(data.assets)
@meta = data
@updateMeta()
if !@pushStateStarted
@ -42,6 +49,7 @@ class App.CustomerChat extends App.Controller
# add new chat window
@bind('chat_session_start', (data) =>
console.log('chat_session_start', data)
if data.session
@addChat(data.session)
)
@ -103,6 +111,16 @@ class App.CustomerChat extends App.Controller
if state is undefined
return @meta.active
# check if min one chat is active
if state
preferences = @Session.get('preferences')
if !preferences || !preferences.chat || !preferences.chat.active || _.isEmpty(preferences.chat.active)
@notify(
type: 'error'
msg: App.i18n.translateContent('To be able to chat you need to select min. one chat topic in settings!')
)
return
@meta.active = state
# write state
@ -118,7 +136,7 @@ class App.CustomerChat extends App.Controller
@delay(delay, 200, 'updateNavMenu')
updateMeta: =>
if @meta.waiting_chat_count && @maxChats > @windowCount()
if @meta.waiting_chat_count && @maxChatWindows > @windowCount()
@$('.js-acceptChat').addClass('is-clickable is-blinking')
else
@$('.js-acceptChat').removeClass('is-clickable is-blinking')
@ -130,6 +148,7 @@ class App.CustomerChat extends App.Controller
if @meta.active_sessions
for session in @meta.active_sessions
@addChat(session)
console.log('updateMeta aC', session)
@meta.active_sessions = false
@updateNavMenu()
@ -165,9 +184,14 @@ class App.CustomerChat extends App.Controller
chat.trigger 'layout-changed'
acceptChat: =>
return if @windowCount() >= @maxChats
return if @windowCount() >= @maxChatWindows
App.WebSocket.send(event:'chat_session_start')
settings: ->
new Setting(
maxChatWindows: @maxChatWindows
)
class CustomerChatRouter extends App.ControllerPermanent
constructor: (params) ->
super
@ -243,12 +267,21 @@ class ChatWindow extends App.Controller
@el.addClass('is-open')
# @addMessage 'Hello. My name is Roger, how can I help you?', 'agent'
if @session && @session.messages
for message in @session.messages
if message.created_by_id
@addMessage message.content, 'agent'
else
@addMessage message.content, 'customer'
if @session
if @session.messages
for message in @session.messages
if message.created_by_id
@addMessage message.content, 'agent'
else
@addMessage message.content, 'customer'
# send init reply
if !@session.messages || _.isEmpty(@session.messages)
preferences = @Session.get('preferences')
if preferences.chat && preferences.chat.phrase
phrases = preferences.chat.phrase[@session.chat_id]
if phrases
@sendMessage phrases
focus: =>
@input.focus()
@ -319,11 +352,12 @@ class ChatWindow extends App.Controller
event.preventDefault()
@sendMessage()
sendMessage: =>
content = @input.html()
sendMessage: (content) =>
if !content
content = @input.html()
return if !content
#@trigger "answer", @input.html()
console.log('send', content, @session.session_id)
App.WebSocket.send(
event:'chat_session_message'
data:
@ -431,6 +465,63 @@ class ChatWindow extends App.Controller
scrollToBottom: ->
@scrollHolder.scrollTop(@scrollHolder.prop('scrollHeight'))
class Setting extends App.ControllerModalNice
buttonClose: true
buttonCancel: true
buttonSubmit: true
head: 'Settings'
content: =>
preferences = @Session.get('preferences')
if !preferences
preferences = {}
if !preferences.chat
preferences.chat = {}
if !preferences.chat.active
preferences.chat.active = {}
if !preferences.chat.phrase
preferences.chat.phrase = {}
if !preferences.chat.max_windows
preferences.chat.max_windows = @maxChatWindows
App.view('customer_chat/setting')(
chats: App.Chat.all()
preferences: preferences
)
submit: (e) =>
e.preventDefault()
params = @formParam(e.target)
@formDisable(e)
# get data
@ajax(
id: 'preferences'
type: 'PUT'
url: "#{@apiPath}/users/preferences"
data: JSON.stringify({user:params})
processData: true
success: @success
error: @error
)
success: (data, status, xhr) =>
App.User.full(
App.Session.get('id'),
=>
@close()
,
true
)
error: (xhr, status, error) =>
data = JSON.parse(xhr.responseText)
@notify(
type: 'error'
msg: App.i18n.translateContent(data.message)
)
App.Config.set( 'customer_chat', CustomerChatRouter, 'Routes' )
App.Config.set( 'CustomerChat', { controller: 'CustomerChat', authentication: true }, 'permanentTask' )

View file

@ -6,8 +6,8 @@ class App.Chat extends App.Model
@configure_attributes = [
{ name: 'name', display: 'Name', tag: 'input', type: 'text', limit: 100, null: false },
{ name: 'note', display: 'Note', tag: 'textarea', limit: 250, null: true },
{ name: 'public', display: 'Public', tag: 'boolean', default: true },
{ name: 'max_queue', display: 'Max. Queue', tag: 'input', default: 5 },
#{ name: 'public', display: 'Public', tag: 'boolean', default: true },
{ name: 'max_queue', display: 'Max. clients in waitlist', tag: 'input', default: 2 },
{ name: 'active', display: 'Active', tag: 'active', default: true },
{ name: 'created_by_id', display: 'Created by', relation: 'User', readonly: 1 },
{ name: 'created_at', display: 'Created', tag: 'datetime', readonly: 1 },

View file

@ -1,19 +1,19 @@
<div class="page-header">
<div class="page-header-title">
<h1><%- @T('Chat Widget') %> <small></small></h1>
<h1><%- @T('Chat') %> <small></small></h1>
</div>
</div>
<div class="page-content">
<p><%- @T('You can create chat widgets for your webpages to allow visitors to chat with you.') %></p>
<h2><%- @T('Channels') %></h2>
<p><%- @T('You can create multiple chat channels.') %></p>
<h2><%- @T('Topics') %></h2>
<p><%- @T('You can create multiple chat topics.') %></p>
<table class="settings-list">
<thead>
<tr>
<th style="white-space: nowrap;"><%- @T('Name') %></th>
<th style="white-space: nowrap;"><%- @T('Note') %></th>
<th style="white-space: nowrap;"><%- @T('Max Queue') %></th>
<th style="white-space: nowrap;"><%- @T('Max. clients in waitlist') %></th>
<th style="white-space: nowrap;"><%- @T('Delete') %></th>
</tr>
</thead>
@ -41,7 +41,7 @@
<% end %>
<tr>
<td colspan="4" class="settings-list-action-cell js-add">
<%- @Icon('plus-small') %> Add Channel
<%- @Icon('plus-small') %> <%- @T('Add') %>
</tbody>
</table>
@ -57,7 +57,7 @@
</div>
</div>
</div>
<label for="preview-iframe" class="formGroup-label"><%- @T('Preview') %></label>
<div class="browser chat-demo js-browser">
<div class="browser-head">
@ -149,7 +149,7 @@
<div class="zammad-switch">
<input name="flat" type="checkbox" id="form-chat-flat">
<label for="form-chat-flat"></label>
</div>
</div>
</div>
</div>
</fieldset>
@ -222,7 +222,7 @@ $(function() {
</ol>
<%- marked(@T('When you turn on debugging by setting the option `debug` to `true` the reason gets printed to the javascript console.')) %>
</p>
<h3><%- @T('Options') %></h3>
<table class="settings-list">

View file

@ -17,7 +17,7 @@
</div>
</div>
<div class="page-header-meta">
<div class="btn btn--action" data-type="settings"><%- @T('Settings') %></div>
<div class="btn btn--action js-settings"><%- @T('Settings') %></div>
</div>
</div>
<div class="chat-workspace"></div>

View file

@ -0,0 +1,34 @@
<table class="settings-list">
<thead>
<tr>
<th><%- @T('Topic') %>
<th width="100%"><%- @T('Greeting') %>
<th><%- @T('Enabled') %>
</th></tr>
</thead>
<% for chat in @chats: %>
<tr>
<td><%= chat.name %>
<td><label class="inline-label"><input name="chat::phrase::<%- chat.id %>" value="<%= @preferences.chat.phrase[chat.id] %>" class="form-control form-control--small" placeholder="<%- @T('Hello, my name is %s, what can I do for you?', App.Session.get('firstname')) %>"/></label>
<td>
<label class="inline-label checkbox-replacement">
<input type="checkbox" name="chat::active::<%- chat.id %>" <% if @preferences.chat.active[chat.id]: %>checked<% end %>>
<%- @Icon('checkbox', 'icon-unchecked') %>
<%- @Icon('checkbox-checked', 'icon-checked') %>
</label>
<% end %>
</tbody>
<div class="select form-group">
<label for="a"><%- @T('Max. concurrent chats') %></label>
<div class="u-positionOrigin">
<select id="a" class="form-control" name="chat::max_windows">
<% for count in [1..20]: %>
<option value="<%- count %>" <% if parseInt(@preferences.chat.max_windows) is count: %>selected<% end %>><%- count %></option>
<% end %>
</select>
<%- @Icon('arrow-down', 'dropdown-arrow') %>
</div>
</div>
</table>

View file

@ -572,10 +572,11 @@ curl http://localhost/api/v1/users/preferences.json -v -u #{login}:#{password} -
return
end
if params[:user]
user = User.find(current_user.id)
params[:user].each {|key, value|
current_user.preferences[key.to_sym] = value
user.preferences[key.to_sym] = value
}
current_user.save
user.save
end
render json: { message: 'ok' }, status: :ok
end

View file

@ -51,10 +51,10 @@ class Chat < ApplicationModel
end
# if all seads are used
if Chat.active_chat_count >= max_queue
if Chat.waiting_chat_count >= max_queue
return {
state: 'no_seats_available',
queue: Chat.seads_available,
queue: Chat.waiting_chat_count,
}
end
@ -64,6 +64,10 @@ class Chat < ApplicationModel
def self.agent_state(user_id)
return { state: 'chat_disabled' } if !Setting.get('chat')
assets = {}
Chat.where(active: true).each {|chat|
assets = chat.assets(assets)
}
{
waiting_chat_count: waiting_chat_count,
running_chat_count: running_chat_count,
@ -71,7 +75,8 @@ class Chat < ApplicationModel
active_agents: active_agents,
seads_available: seads_available,
seads_total: seads_total,
active: Chat::Agent.state(user_id)
active: Chat::Agent.state(user_id),
assets: assets,
}
end

View file

@ -2,18 +2,8 @@ class Sessions::Event::ChatAgentState < Sessions::Event::ChatBase
def run
# only agents can do this
chat_id = 1
chat = Chat.find_by(id: chat_id)
if !@session['id']
return {
event: 'chat_agent_state',
data: {
state: 'failed',
message: 'No such user_id.'
},
}
end
# check if user has permissions
return if !agent_permission_check
Chat::Agent.state(@session['id'], @data['data']['active'])

View file

@ -53,4 +53,92 @@ class Sessions::Event::ChatBase
}
end
def agent_permission_check
if !@session
error = {
event: 'chat_error',
data: {
state: 'no_session',
},
}
Sessions.send(@client_id, error)
return
end
if !@session['id']
error = {
event: 'chat_error',
data: {
state: 'no_session_user_id',
},
}
Sessions.send(@client_id, error)
return
end
user = User.lookup(id: @session['id'])
if !user
error = {
event: 'chat_error',
data: {
state: 'no_such_user',
},
}
Sessions.send(@client_id, error)
return
end
if !user.role?('Agent')
error = {
event: 'chat_error',
data: {
state: 'no_permission',
},
}
Sessions.send(@client_id, error)
return
end
true
end
def current_chat_session
Chat::Session.find_by(session_id: @data['data']['session_id'])
end
def check_chat_session_exists
if !@data['data'] || !@data['data']['session_id']
error = {
event: 'chat_error',
data: {
state: 'Need session_id.',
},
}
Sessions.send(@client_id, error)
return
end
return true if current_chat_session
error = {
event: 'chat_error',
data: {
state: "No such session id #{@data['data']['session_id']}",
},
}
Sessions.send(@client_id, error)
false
end
def current_chat
Chat.find_by(id: @data['data']['chat_id'])
end
def check_chat_exists
chat = current_chat
return true if chat
error = {
event: 'chat_error',
data: {
state: 'no_such_chat',
},
}
Sessions.send(@client_id, error)
false
end
end

View file

@ -2,24 +2,7 @@ class Sessions::Event::ChatSessionClose < Sessions::Event::ChatBase
def run
if !@data['data'] || !@data['data']['session_id']
return {
event: 'chat_status_close',
data: {
state: 'Need session_id.',
},
}
end
chat_session = Chat::Session.find_by(session_id: @data['data']['session_id'])
if !chat_session
return {
event: 'chat_status_close',
data: {
state: "No such session id #{@data['data']['session_id']}",
},
}
end
return if !check_chat_session_exists
realname = 'Anonymous'
if @session && @session['id']
@ -28,6 +11,7 @@ class Sessions::Event::ChatSessionClose < Sessions::Event::ChatBase
# check count of participents
participents_count = 0
chat_session = current_chat_session
if chat_session.preferences[:participents]
participents_count = chat_session.preferences[:participents].count
end

View file

@ -1,21 +1,11 @@
class Sessions::Event::ChatSessionInit < Sessions::Event::ChatBase
def run
chat_id = 1
chat = Chat.find_by(id: chat_id)
if !chat
return {
event: 'chat_session_init',
data: {
state: 'no_such_chat',
},
}
end
return if !check_chat_exists
# create chat session
chat_session = Chat::Session.create(
chat_id: chat_id,
chat_id: @data['data']['chat_id'],
name: '',
state: 'waiting',
preferences: {

View file

@ -1,25 +1,8 @@
class Sessions::Event::ChatSessionMessage < Sessions::Event::ChatBase
def run
if !@data['data'] || !@data['data']['session_id']
return {
event: 'chat_session_message',
data: {
state: 'Need session_id.',
},
}
end
chat_session = Chat::Session.find_by(session_id: @data['data']['session_id'])
if !chat_session
return {
event: 'chat_session_message',
data: {
state: "No such session id #{@data['data']['session_id']}",
},
}
end
return if !check_chat_session_exists
chat_session = current_chat_session
user_id = nil
if @session

View file

@ -1,6 +1,7 @@
class Sessions::Event::ChatSessionStart < Sessions::Event::ChatBase
def run
agent_permission_check
# find first in waiting list
chat_session = Chat::Session.where(state: 'waiting').order('created_at ASC').first
@ -34,6 +35,7 @@ class Sessions::Event::ChatSessionStart < Sessions::Event::ChatBase
state: 'ok',
agent: user,
session_id: chat_session.session_id,
chat_id: chat_session.chat_id,
},
}
chat_session.send_to_recipients(data)

View file

@ -1,25 +1,8 @@
class Sessions::Event::ChatSessionTyping < Sessions::Event::ChatBase
def run
if !@data['data'] || !@data['data']['session_id']
return {
event: 'chat_session_typing',
data: {
state: 'Need session_id.',
},
}
end
chat_session = Chat::Session.find_by(session_id: @data['data']['session_id'])
if !chat_session
return {
event: 'chat_session_typing',
data: {
state: "No such session id #{@data['data']['session_id']}",
},
}
end
return if !check_chat_session_exists
chat_session = current_chat_session
user_id = nil
if @session

View file

@ -3,6 +3,7 @@ class Sessions::Event::ChatStatusAgent < Sessions::Event::ChatBase
def run
# check if user has permissions
return if !agent_permission_check
# renew timestamps
state = Chat::Agent.state(@session['id'])

View file

@ -1,17 +1,7 @@
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
return if !check_chat_exists
# check if it's a chat sessin reconnect
session_id = nil
@ -24,7 +14,7 @@ class Sessions::Event::ChatStatusCustomer < Sessions::Event::ChatBase
end
{
event: 'chat_status_customer',
data: chat.customer_state(session_id),
data: current_chat.customer_state(session_id),
}
end
end