chat: add waiting screen, outsource agent view, add basic socket communication

This commit is contained in:
Felix Niklas 2015-11-02 16:48:16 +01:00
parent d67bae2005
commit 806ffb70c7
9 changed files with 489 additions and 98 deletions

View file

@ -5,10 +5,11 @@ do($ = window.jQuery, window) ->
defaults: defaults:
invitationPhrase: '<strong>Chat</strong> with us!' invitationPhrase: '<strong>Chat</strong> with us!'
agentPhrase: '%%agent%% is helping you' agentPhrase: ' is helping you'
show: true show: true
target: $('body') target: $('body')
_messageCount: 0
isOpen: false isOpen: false
blinkOnlineInterval: null blinkOnlineInterval: null
stopBlinOnlineStateTimeout: null stopBlinOnlineStateTimeout: null
@ -25,15 +26,20 @@ do($ = window.jQuery, window) ->
'Connection re-established': 'Connection re-established' 'Connection re-established': 'Connection re-established'
'Today': 'Today' 'Today': 'Today'
T: (string) -> T: (string) =>
return @strings[string] return @strings[string]
view: (name) -> view: (name) =>
return window.zammadChatTemplates[name] return (options) =>
if !options
options = {}
options.T = @T
return window.zammadChatTemplates[name](options)
constructor: (el, options) -> constructor: (el, options) ->
@options = $.extend {}, @defaults, options @options = $.extend {}, @defaults, options
@el = $(@view('chat')()) @el = $(@view('chat')(@options))
@options.target.append @el @options.target.append @el
@setAgentOnlineState @isOnline @setAgentOnlineState @isOnline
@ -46,19 +52,18 @@ do($ = window.jQuery, window) ->
).autoGrow { extraLine: false } ).autoGrow { extraLine: false }
if !window.WebSocket if !window.WebSocket
console.log('no websockets available') console.log('Zammad Chat: Browser not supported')
return return
zammad_host = 'ws://localhost:6042' zammad_host = 'ws://localhost:6042'
@ws = new window.WebSocket(zammad_host) @ws = new window.WebSocket(zammad_host)
console.log("connecting ws #{zammad_host}") console.log("Connecting to #{zammad_host}")
@ws.onopen = => @ws.onopen = =>
console.log('ws connected') console.log('ws connected')
@send "chat_status"
@ws.onmessage = (e) => @ws.onmessage = @onWebSocketMessage
pipe = JSON.parse( e.data )
console.log 'debug', 'ws:onmessage', pipe
@ws.onclose = (e) => @ws.onclose = (e) =>
console.log 'debug', 'close websocket connection' console.log 'debug', 'close websocket connection'
@ -71,6 +76,44 @@ do($ = window.jQuery, window) ->
event.preventDefault() event.preventDefault()
@sendMessage() @sendMessage()
send: (action, data) =>
pipe = JSON.stringify
action: action
data: data
@ws.send pipe
onWebSocketMessage: (e) =>
pipe = JSON.parse( e.data )
console.log 'debug', 'ws:onmessage', pipe
switch pipe.action
when 'message'
@receiveMessage pipe.data
when 'typing_start'
@onAgentTypingStart()
when 'typing_end'
@onAgentTypingEnd()
when 'chat_init'
switch pipe.data.state
when 'ok'
@onConnectionEstablished pipe.data.agent
when 'queue'
@onQueue pipe.data.position
when 'chat_status'
switch pipe.data.state
when 'ok'
@onReady()
when 'offline'
console.log 'Zammad Chat: No agent online'
when 'chat_disabled'
console.log 'Zammad Chat: Chat is disabled'
when 'no_seats_available'
console.log 'Zammad Chat: Too many clients in queue. Clients in queue: ', pipe.data.queue
onReady: =>
@show() if @options.show
onInput: => onInput: =>
# remove unread-state from messages # remove unread-state from messages
@el.find('.zammad-chat-message--unread') @el.find('.zammad-chat-message--unread')
@ -86,10 +129,12 @@ do($ = window.jQuery, window) ->
onTypingStart: -> onTypingStart: ->
# send typing start event # send typing start event
@isTyping = true @isTyping = true
@send 'typing_start'
onTypingEnd: => onTypingEnd: =>
# send typing end event # send typing end event
@isTyping = false @isTyping = false
@send 'typing_end'
onSubmit: (event) => onSubmit: (event) =>
event.preventDefault() event.preventDefault()
@ -98,28 +143,35 @@ do($ = window.jQuery, window) ->
sendMessage: -> sendMessage: ->
message = @el.find('.zammad-chat-input').val() message = @el.find('.zammad-chat-input').val()
if message if !message
messageElement = @view('message') return
message: message
from: 'customer'
@maybeAddTimestamp() messageElement = @view('message')
message: message
from: 'customer'
id: @_messageCount++
# add message before message typing loader @maybeAddTimestamp()
if @el.find('.zammad-chat-message--typing').size()
@lastAddedType = 'typing-placeholder'
@el.find('.zammad-chat-message--typing').before messageElement
else
@lastAddedType = 'message--customer'
@el.find('.zammad-chat-body').append messageElement
@el.find('.zammad-chat-input').val('') # add message before message typing loader
@scrollToBottom() if @el.find('.zammad-chat-message--typing').size()
@lastAddedType = 'typing-placeholder'
@el.find('.zammad-chat-message--typing').before messageElement
else
@lastAddedType = 'message--customer'
@el.find('.zammad-chat-body').append messageElement
@isTyping = false @el.find('.zammad-chat-input').val('')
# send message event @scrollToBottom()
receiveMessage: (message) => @isTyping = false
# send message event
@send 'message',
body: message
id: @_messageCount
receiveMessage: (data) =>
# hide writing indicator # hide writing indicator
@onAgentTypingEnd() @onAgentTypingEnd()
@ -128,7 +180,8 @@ do($ = window.jQuery, window) ->
@lastAddedType = 'message--agent' @lastAddedType = 'message--agent'
unread = document.hidden ? " zammad-chat-message--unread" : "" unread = document.hidden ? " zammad-chat-message--unread" : ""
@el.find('.zammad-chat-body').append @view('message') @el.find('.zammad-chat-body').append @view('message')
message: message message: data.body
id: data.id
from: 'agent' from: 'agent'
@scrollToBottom() @scrollToBottom()
@ -136,15 +189,18 @@ do($ = window.jQuery, window) ->
if @isOpen then @close() else @open() if @isOpen then @close() else @open()
open: -> open: ->
@showLoader()
@el @el
.addClass('zammad-chat-is-open') .addClass('zammad-chat-is-open')
.animate { bottom: 0 }, 500, @onOpenAnimationEnd .animate { bottom: 0 }, 500, @onOpenAnimationEnd
onOpenAnimationEnd: => onOpenAnimationEnd: =>
@isOpen = true @isOpen = true
setTimeout @onConnectionEstablished, 1180 #setTimeout @onQueue, 1180
setTimeout @onAgentTypingStart, 2000 # setTimeout @onConnectionEstablished, 1180
setTimeout @receiveMessage, 5000, "Hello! How can I help you?" # setTimeout @onAgentTypingStart, 2000
# setTimeout @receiveMessage, 5000, "Hello! How can I help you?"
@connect() @connect()
close: -> close: ->
@ -166,8 +222,15 @@ do($ = window.jQuery, window) ->
@el.css 'bottom', -remainerHeight @el.css 'bottom', -remainerHeight
onQueue: (position) =>
console.log "onQueue", position
@inQueue = true
@el.find('.zammad-chat-body').html @view('waiting')
position: position
onAgentTypingStart: => onAgentTypingStart: =>
# never display two loaders # never display two typing indicators
return if @el.find('.zammad-chat-message--typing').size() return if @el.find('.zammad-chat-message--typing').size()
@maybeAddTimestamp() @maybeAddTimestamp()
@ -212,7 +275,7 @@ do($ = window.jQuery, window) ->
@el.find('.zammad-chat-body').scrollTop($('.zammad-chat-body').prop('scrollHeight')) @el.find('.zammad-chat-body').scrollTop($('.zammad-chat-body').prop('scrollHeight'))
connect: -> connect: ->
@send('chat_init')
reconnect: => reconnect: =>
# set status to connecting # set status to connecting
@ -227,17 +290,26 @@ do($ = window.jQuery, window) ->
@addStatus @T('Connection re-established') @addStatus @T('Connection re-established')
disconnect: -> disconnect: ->
@el.find('.zammad-chat-loader').removeClass('zammad-chat-is-hidden'); @showLoader()
@el.find('.zammad-chat-welcome').removeClass('zammad-chat-is-hidden'); @el.find('.zammad-chat-welcome').removeClass('zammad-chat-is-hidden')
@el.find('.zammad-chat-agent').addClass('zammad-chat-is-hidden'); @el.find('.zammad-chat-agent').addClass('zammad-chat-is-hidden')
@el.find('.zammad-chat-agent-status').addClass('zammad-chat-is-hidden'); @el.find('.zammad-chat-agent-status').addClass('zammad-chat-is-hidden')
onConnectionEstablished: => onConnectionEstablished: (agent) =>
@el.find('.zammad-chat-loader').addClass('zammad-chat-is-hidden'); @inQueue = false
@el.find('.zammad-chat-welcome').addClass('zammad-chat-is-hidden'); @agent = agent
@el.find('.zammad-chat-agent').removeClass('zammad-chat-is-hidden');
@el.find('.zammad-chat-agent-status').removeClass('zammad-chat-is-hidden'); @el.find('.zammad-chat-agent').html @view('agent')
@el.find('.zammad-chat-input').focus(); agent: agent
@el.find('.zammad-chat-body').empty()
@el.find('.zammad-chat-welcome').addClass('zammad-chat-is-hidden')
@el.find('.zammad-chat-agent').removeClass('zammad-chat-is-hidden')
@el.find('.zammad-chat-agent-status').removeClass('zammad-chat-is-hidden')
@el.find('.zammad-chat-input').focus()
showLoader: ->
@el.find('.zammad-chat-body').html @view('loader')()
setAgentOnlineState: (state) => setAgentOnlineState: (state) =>
@isOnline = state @isOnline = state

View file

@ -1,7 +1,7 @@
if (!window.zammadChatTemplates) { if (!window.zammadChatTemplates) {
window.zammadChatTemplates = {}; window.zammadChatTemplates = {};
} }
window.zammadChatTemplates["chat"] = function (__obj) { window.zammadChatTemplates["agent"] = function (__obj) {
if (!__obj) __obj = {}; if (!__obj) __obj = {};
var __out = [], __capture = function(callback) { var __out = [], __capture = function(callback) {
var out = __out, result; var out = __out, result;
@ -40,7 +40,19 @@ window.zammadChatTemplates["chat"] = function (__obj) {
} }
(function() { (function() {
(function() { (function() {
__out.push('<div class="zammad-chat">\n <div class="zammad-chat-header">\n <div class="zammad-chat-header-controls">\n <span class="zammad-chat-agent-status zammad-chat-is-hidden" data-status="online">Online</span>\n <span class="zammad-chat-toggle">\n <svg class="zammad-chat-toggle-icon-open" viewBox="0 0 13 7"><path d="M10.807 7l1.4-1.428-5-4.9L6.5-.02l-.7.7-4.9 4.9 1.414 1.413L6.5 2.886 10.807 7z" fill-rule="evenodd"/></svg>\n <svg class="zammad-chat-toggle-icon-close" viewBox="0 0 13 7"><path d="M6.554 4.214L2.246 0l-1.4 1.428 5 4.9.708.693.7-.7 4.9-4.9L10.74.008 6.553 4.214z" fill-rule="evenodd"/></svg>\n </span>\n </div>\n <div class="zammad-chat-agent zammad-chat-is-hidden">\n <img class="zammad-chat-agent-avatar" src="https://s3.amazonaws.com/uifaces/faces/twitter/joshaustin/128.jpg">\n <span class="zammad-chat-agent-sentence">\n <span class="zammad-chat-agent-name">Adam</span> is helping you.\n </span>\n </div>\n <div class="zammad-chat-welcome">\n <svg class="zammad-chat-icon" viewBox="0 0 24 24"><path d="M2 5C2 4 3 3 4 3h16c1 0 2 1 2 2v10C22 16 21 17 20 17H4C3 17 2 16 2 15V5zM12 17l6 4v-4h-6z" fill-rule="evenodd"/></svg>\n <span class="zammad-chat-welcome-text"><strong>Chat</strong> with us!</span>\n </div>\n </div>\n <div class="zammad-chat-loader">\n <span class="zammad-chat-loading-animation">\n <span class="zammad-chat-loading-circle"></span>\n <span class="zammad-chat-loading-circle"></span>\n <span class="zammad-chat-loading-circle"></span>\n </span>\n <span class="zammad-chat-loader-text">Connecting</span>\n </div>\n <div class="zammad-chat-body"></div>\n <form class="zammad-chat-controls">\n <textarea class="zammad-chat-input" rows="1" placeholder="Compose your message..."></textarea>\n <button type="submit" class="zammad-chat-send">Send</button>\n </form>\n</div>'); __out.push('<img class="zammad-chat-agent-avatar" src="');
__out.push(__sanitize(this.agent.avatar));
__out.push('">\n<span class="zammad-chat-agent-sentence">\n <span class="zammad-chat-agent-name">');
__out.push(__sanitize(this.agent.name));
__out.push('</span> ');
__out.push(this.agentPhrase);
__out.push('\n</span>');
}).call(this); }).call(this);
@ -56,11 +68,13 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
ZammadChat = (function() { ZammadChat = (function() {
ZammadChat.prototype.defaults = { ZammadChat.prototype.defaults = {
invitationPhrase: '<strong>Chat</strong> with us!', invitationPhrase: '<strong>Chat</strong> with us!',
agentPhrase: '%%agent%% is helping you', agentPhrase: ' is helping you',
show: true, show: true,
target: $('body') target: $('body')
}; };
ZammadChat.prototype._messageCount = 0;
ZammadChat.prototype.isOpen = false; ZammadChat.prototype.isOpen = false;
ZammadChat.prototype.blinkOnlineInterval = null; ZammadChat.prototype.blinkOnlineInterval = null;
@ -92,7 +106,15 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
}; };
ZammadChat.prototype.view = function(name) { ZammadChat.prototype.view = function(name) {
return window.zammadChatTemplates[name]; return (function(_this) {
return function(options) {
if (!options) {
options = {};
}
options.T = _this.T;
return window.zammadChatTemplates[name](options);
};
})(this);
}; };
function ZammadChat(el, options) { function ZammadChat(el, options) {
@ -102,6 +124,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.reconnect = bind(this.reconnect, this); this.reconnect = bind(this.reconnect, this);
this.onAgentTypingEnd = bind(this.onAgentTypingEnd, this); this.onAgentTypingEnd = bind(this.onAgentTypingEnd, this);
this.onAgentTypingStart = bind(this.onAgentTypingStart, this); this.onAgentTypingStart = bind(this.onAgentTypingStart, this);
this.onQueue = bind(this.onQueue, this);
this.onCloseAnimationEnd = bind(this.onCloseAnimationEnd, this); this.onCloseAnimationEnd = bind(this.onCloseAnimationEnd, this);
this.onOpenAnimationEnd = bind(this.onOpenAnimationEnd, this); this.onOpenAnimationEnd = bind(this.onOpenAnimationEnd, this);
this.toggle = bind(this.toggle, this); this.toggle = bind(this.toggle, this);
@ -109,9 +132,15 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.onSubmit = bind(this.onSubmit, this); this.onSubmit = bind(this.onSubmit, this);
this.onTypingEnd = bind(this.onTypingEnd, this); this.onTypingEnd = bind(this.onTypingEnd, this);
this.onInput = bind(this.onInput, this); this.onInput = bind(this.onInput, this);
this.onReady = bind(this.onReady, this);
this.onWebSocketMessage = bind(this.onWebSocketMessage, this);
this.send = bind(this.send, this);
this.checkForEnter = bind(this.checkForEnter, this); this.checkForEnter = bind(this.checkForEnter, this);
this.view = bind(this.view, this);
this.T = bind(this.T, this);
var zammad_host;
this.options = $.extend({}, this.defaults, options); this.options = $.extend({}, this.defaults, options);
this.el = $(this.view('chat')()); this.el = $(this.view('chat')(this.options));
this.options.target.append(this.el); this.options.target.append(this.el);
this.setAgentOnlineState(this.isOnline); this.setAgentOnlineState(this.isOnline);
this.el.find('.zammad-chat-header').click(this.toggle); this.el.find('.zammad-chat-header').click(this.toggle);
@ -122,6 +151,30 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
}).autoGrow({ }).autoGrow({
extraLine: false extraLine: false
}); });
if (!window.WebSocket) {
console.log('Zammad Chat: Browser not supported');
return;
}
zammad_host = 'ws://localhost:6042';
this.ws = new window.WebSocket(zammad_host);
console.log("Connecting to " + zammad_host);
this.ws.onopen = (function(_this) {
return function() {
console.log('ws connected');
return _this.send("chat_status");
};
})(this);
this.ws.onmessage = this.onWebSocketMessage;
this.ws.onclose = (function(_this) {
return function(e) {
return console.log('debug', 'close websocket connection');
};
})(this);
this.ws.onerror = (function(_this) {
return function(e) {
return console.log('debug', 'ws:onerror', e);
};
})(this);
} }
ZammadChat.prototype.checkForEnter = function(event) { ZammadChat.prototype.checkForEnter = function(event) {
@ -131,6 +184,54 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
} }
}; };
ZammadChat.prototype.send = function(action, data) {
var pipe;
pipe = JSON.stringify({
action: action,
data: data
});
return this.ws.send(pipe);
};
ZammadChat.prototype.onWebSocketMessage = function(e) {
var pipe;
pipe = JSON.parse(e.data);
console.log('debug', 'ws:onmessage', pipe);
switch (pipe.action) {
case 'message':
return this.receiveMessage(pipe.data);
case 'typing_start':
return this.onAgentTypingStart();
case 'typing_end':
return this.onAgentTypingEnd();
case 'chat_init':
switch (pipe.data.state) {
case 'ok':
return this.onConnectionEstablished(pipe.data.agent);
case 'queue':
return this.onQueue(pipe.data.position);
}
break;
case 'chat_status':
switch (pipe.data.state) {
case 'ok':
return this.onReady();
case 'offline':
return console.log('Zammad Chat: No agent online');
case 'chat_disabled':
return console.log('Zammad Chat: Chat is disabled');
case 'no_seats_available':
return console.log('Zammad Chat: Too many clients in queue. Clients in queue: ', pipe.data.queue);
}
}
};
ZammadChat.prototype.onReady = function() {
if (this.options.show) {
return this.show();
}
};
ZammadChat.prototype.onInput = function() { ZammadChat.prototype.onInput = function() {
this.el.find('.zammad-chat-message--unread').removeClass('zammad-chat-message--unread'); this.el.find('.zammad-chat-message--unread').removeClass('zammad-chat-message--unread');
if (this.inputTimeout) { if (this.inputTimeout) {
@ -143,11 +244,13 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
}; };
ZammadChat.prototype.onTypingStart = function() { ZammadChat.prototype.onTypingStart = function() {
return this.isTyping = true; this.isTyping = true;
return this.send('typing_start');
}; };
ZammadChat.prototype.onTypingEnd = function() { ZammadChat.prototype.onTypingEnd = function() {
return this.isTyping = false; this.isTyping = false;
return this.send('typing_end');
}; };
ZammadChat.prototype.onSubmit = function(event) { ZammadChat.prototype.onSubmit = function(event) {
@ -158,26 +261,32 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
ZammadChat.prototype.sendMessage = function() { ZammadChat.prototype.sendMessage = function() {
var message, messageElement; var message, messageElement;
message = this.el.find('.zammad-chat-input').val(); message = this.el.find('.zammad-chat-input').val();
if (message) { if (!message) {
messageElement = this.view('message')({ return;
message: message,
from: 'customer'
});
this.maybeAddTimestamp();
if (this.el.find('.zammad-chat-message--typing').size()) {
this.lastAddedType = 'typing-placeholder';
this.el.find('.zammad-chat-message--typing').before(messageElement);
} else {
this.lastAddedType = 'message--customer';
this.el.find('.zammad-chat-body').append(messageElement);
}
this.el.find('.zammad-chat-input').val('');
this.scrollToBottom();
return this.isTyping = false;
} }
messageElement = this.view('message')({
message: message,
from: 'customer',
id: this._messageCount++
});
this.maybeAddTimestamp();
if (this.el.find('.zammad-chat-message--typing').size()) {
this.lastAddedType = 'typing-placeholder';
this.el.find('.zammad-chat-message--typing').before(messageElement);
} else {
this.lastAddedType = 'message--customer';
this.el.find('.zammad-chat-body').append(messageElement);
}
this.el.find('.zammad-chat-input').val('');
this.scrollToBottom();
this.isTyping = false;
return this.send('message', {
body: message,
id: this._messageCount
});
}; };
ZammadChat.prototype.receiveMessage = function(message) { ZammadChat.prototype.receiveMessage = function(data) {
var ref, unread; var ref, unread;
this.onAgentTypingEnd(); this.onAgentTypingEnd();
this.maybeAddTimestamp(); this.maybeAddTimestamp();
@ -186,7 +295,8 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
" zammad-chat-message--unread": "" " zammad-chat-message--unread": ""
}; };
this.el.find('.zammad-chat-body').append(this.view('message')({ this.el.find('.zammad-chat-body').append(this.view('message')({
message: message, message: data.body,
id: data.id,
from: 'agent' from: 'agent'
})); }));
return this.scrollToBottom(); return this.scrollToBottom();
@ -201,6 +311,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
}; };
ZammadChat.prototype.open = function() { ZammadChat.prototype.open = function() {
this.showLoader();
return this.el.addClass('zammad-chat-is-open').animate({ return this.el.addClass('zammad-chat-is-open').animate({
bottom: 0 bottom: 0
}, 500, this.onOpenAnimationEnd); }, 500, this.onOpenAnimationEnd);
@ -208,9 +319,6 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
ZammadChat.prototype.onOpenAnimationEnd = function() { ZammadChat.prototype.onOpenAnimationEnd = function() {
this.isOpen = true; this.isOpen = true;
setTimeout(this.onConnectionEstablished, 1180);
setTimeout(this.onAgentTypingStart, 2000);
setTimeout(this.receiveMessage, 5000, "Hello! How can I help you?");
return this.connect(); return this.connect();
}; };
@ -239,6 +347,14 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
return this.el.css('bottom', -remainerHeight); return this.el.css('bottom', -remainerHeight);
}; };
ZammadChat.prototype.onQueue = function(position) {
console.log("onQueue", position);
this.inQueue = true;
return this.el.find('.zammad-chat-body').html(this.view('waiting')({
position: position
}));
};
ZammadChat.prototype.onAgentTypingStart = function() { ZammadChat.prototype.onAgentTypingStart = function() {
if (this.el.find('.zammad-chat-message--typing').size()) { if (this.el.find('.zammad-chat-message--typing').size()) {
return; return;
@ -287,7 +403,9 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
return this.el.find('.zammad-chat-body').scrollTop($('.zammad-chat-body').prop('scrollHeight')); return this.el.find('.zammad-chat-body').scrollTop($('.zammad-chat-body').prop('scrollHeight'));
}; };
ZammadChat.prototype.connect = function() {}; ZammadChat.prototype.connect = function() {
return this.send('chat_init');
};
ZammadChat.prototype.reconnect = function() { ZammadChat.prototype.reconnect = function() {
this.lastAddedType = 'status'; this.lastAddedType = 'status';
@ -302,20 +420,29 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
}; };
ZammadChat.prototype.disconnect = function() { ZammadChat.prototype.disconnect = function() {
this.el.find('.zammad-chat-loader').removeClass('zammad-chat-is-hidden'); this.showLoader();
this.el.find('.zammad-chat-welcome').removeClass('zammad-chat-is-hidden'); this.el.find('.zammad-chat-welcome').removeClass('zammad-chat-is-hidden');
this.el.find('.zammad-chat-agent').addClass('zammad-chat-is-hidden'); this.el.find('.zammad-chat-agent').addClass('zammad-chat-is-hidden');
return this.el.find('.zammad-chat-agent-status').addClass('zammad-chat-is-hidden'); return this.el.find('.zammad-chat-agent-status').addClass('zammad-chat-is-hidden');
}; };
ZammadChat.prototype.onConnectionEstablished = function() { ZammadChat.prototype.onConnectionEstablished = function(agent) {
this.el.find('.zammad-chat-loader').addClass('zammad-chat-is-hidden'); this.inQueue = false;
this.agent = agent;
this.el.find('.zammad-chat-agent').html(this.view('agent')({
agent: agent
}));
this.el.find('.zammad-chat-body').empty();
this.el.find('.zammad-chat-welcome').addClass('zammad-chat-is-hidden'); this.el.find('.zammad-chat-welcome').addClass('zammad-chat-is-hidden');
this.el.find('.zammad-chat-agent').removeClass('zammad-chat-is-hidden'); this.el.find('.zammad-chat-agent').removeClass('zammad-chat-is-hidden');
this.el.find('.zammad-chat-agent-status').removeClass('zammad-chat-is-hidden'); this.el.find('.zammad-chat-agent-status').removeClass('zammad-chat-is-hidden');
return this.el.find('.zammad-chat-input').focus(); return this.el.find('.zammad-chat-input').focus();
}; };
ZammadChat.prototype.showLoader = function() {
return this.el.find('.zammad-chat-body').html(this.view('loader')());
};
ZammadChat.prototype.setAgentOnlineState = function(state) { ZammadChat.prototype.setAgentOnlineState = function(state) {
this.isOnline = state; this.isOnline = state;
return this.el.find('.zammad-chat-agent-status').toggleClass('zammad-chat-is-online', state).text(state ? this.T('Online') : this.T('Offline')); return this.el.find('.zammad-chat-agent-status').toggleClass('zammad-chat-is-online', state).text(state ? this.T('Online') : this.T('Offline'));
@ -406,6 +533,116 @@ jQuery.fn.autoGrow = function(options) {
}); });
}; };
if (!window.zammadChatTemplates) {
window.zammadChatTemplates = {};
}
window.zammadChatTemplates["chat"] = function (__obj) {
if (!__obj) __obj = {};
var __out = [], __capture = function(callback) {
var out = __out, result;
__out = [];
callback.call(this);
result = __out.join('');
__out = out;
return __safe(result);
}, __sanitize = function(value) {
if (value && value.ecoSafe) {
return value;
} else if (typeof value !== 'undefined' && value != null) {
return __escape(value);
} else {
return '';
}
}, __safe, __objSafe = __obj.safe, __escape = __obj.escape;
__safe = __obj.safe = function(value) {
if (value && value.ecoSafe) {
return value;
} else {
if (!(typeof value !== 'undefined' && value != null)) value = '';
var result = new String(value);
result.ecoSafe = true;
return result;
}
};
if (!__escape) {
__escape = __obj.escape = function(value) {
return ('' + value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
};
}
(function() {
(function() {
__out.push('<div class="zammad-chat">\n <div class="zammad-chat-header">\n <div class="zammad-chat-header-controls">\n <span class="zammad-chat-agent-status zammad-chat-is-hidden" data-status="online">Online</span>\n <span class="zammad-chat-toggle">\n <svg class="zammad-chat-toggle-icon-open" viewBox="0 0 13 7"><path d="M10.807 7l1.4-1.428-5-4.9L6.5-.02l-.7.7-4.9 4.9 1.414 1.413L6.5 2.886 10.807 7z" fill-rule="evenodd"/></svg>\n <svg class="zammad-chat-toggle-icon-close" viewBox="0 0 13 7"><path d="M6.554 4.214L2.246 0l-1.4 1.428 5 4.9.708.693.7-.7 4.9-4.9L10.74.008 6.553 4.214z" fill-rule="evenodd"/></svg>\n </span>\n </div>\n <div class="zammad-chat-agent zammad-chat-is-hidden">\n \n </div>\n <div class="zammad-chat-welcome">\n <svg class="zammad-chat-icon" viewBox="0 0 24 24"><path d="M2 5C2 4 3 3 4 3h16c1 0 2 1 2 2v10C22 16 21 17 20 17H4C3 17 2 16 2 15V5zM12 17l6 4v-4h-6z" fill-rule="evenodd"/></svg>\n <span class="zammad-chat-welcome-text">');
__out.push(this.invitationPhrase);
__out.push('</span>\n </div>\n </div>\n <div class="zammad-chat-body"></div>\n <form class="zammad-chat-controls">\n <textarea class="zammad-chat-input" rows="1" placeholder="Compose your message..."></textarea>\n <button type="submit" class="zammad-chat-send">Send</button>\n </form>\n</div>');
}).call(this);
}).call(__obj);
__obj.safe = __objSafe, __obj.escape = __escape;
return __out.join('');
};
if (!window.zammadChatTemplates) {
window.zammadChatTemplates = {};
}
window.zammadChatTemplates["loader"] = function (__obj) {
if (!__obj) __obj = {};
var __out = [], __capture = function(callback) {
var out = __out, result;
__out = [];
callback.call(this);
result = __out.join('');
__out = out;
return __safe(result);
}, __sanitize = function(value) {
if (value && value.ecoSafe) {
return value;
} else if (typeof value !== 'undefined' && value != null) {
return __escape(value);
} else {
return '';
}
}, __safe, __objSafe = __obj.safe, __escape = __obj.escape;
__safe = __obj.safe = function(value) {
if (value && value.ecoSafe) {
return value;
} else {
if (!(typeof value !== 'undefined' && value != null)) value = '';
var result = new String(value);
result.ecoSafe = true;
return result;
}
};
if (!__escape) {
__escape = __obj.escape = function(value) {
return ('' + value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
};
}
(function() {
(function() {
__out.push('<div class="zammad-chat-modal">\n <span class="zammad-chat-loading-animation">\n <span class="zammad-chat-loading-circle"></span>\n <span class="zammad-chat-loading-circle"></span>\n <span class="zammad-chat-loading-circle"></span>\n </span>\n <span class="zammad-chat-modal-text">');
__out.push(__sanitize(this.T('Connecting')));
__out.push('</span>\n</div>');
}).call(this);
}).call(__obj);
__obj.safe = __objSafe, __obj.escape = __escape;
return __out.join('');
};
if (!window.zammadChatTemplates) { if (!window.zammadChatTemplates) {
window.zammadChatTemplates = {}; window.zammadChatTemplates = {};
} }
@ -574,3 +811,58 @@ window.zammadChatTemplates["typingIndicator"] = function (__obj) {
__obj.safe = __objSafe, __obj.escape = __escape; __obj.safe = __objSafe, __obj.escape = __escape;
return __out.join(''); return __out.join('');
}; };
if (!window.zammadChatTemplates) {
window.zammadChatTemplates = {};
}
window.zammadChatTemplates["waiting"] = function (__obj) {
if (!__obj) __obj = {};
var __out = [], __capture = function(callback) {
var out = __out, result;
__out = [];
callback.call(this);
result = __out.join('');
__out = out;
return __safe(result);
}, __sanitize = function(value) {
if (value && value.ecoSafe) {
return value;
} else if (typeof value !== 'undefined' && value != null) {
return __escape(value);
} else {
return '';
}
}, __safe, __objSafe = __obj.safe, __escape = __obj.escape;
__safe = __obj.safe = function(value) {
if (value && value.ecoSafe) {
return value;
} else {
if (!(typeof value !== 'undefined' && value != null)) value = '';
var result = new String(value);
result.ecoSafe = true;
return result;
}
};
if (!__escape) {
__escape = __obj.escape = function(value) {
return ('' + value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
};
}
(function() {
(function() {
__out.push('<div class="zammad-chat-modal">\n <div class="zammad-chat-modal-text">\n <span class="zammad-chat-loading-animation">\n <span class="zammad-chat-loading-circle"></span>\n <span class="zammad-chat-loading-circle"></span>\n <span class="zammad-chat-loading-circle"></span>\n </span>\n Leider sind gerade alle Mitarbeiter belegt.<br>\n Warteliste-Position: <strong>');
__out.push(__sanitize(this.position));
__out.push('</strong>\n </div>\n</div>');
}).call(this);
}).call(__obj);
__obj.safe = __objSafe, __obj.escape = __escape;
return __out.join('');
};

File diff suppressed because one or more lines are too long

View file

@ -123,7 +123,7 @@
-webkit-transform: scale(1); -webkit-transform: scale(1);
transform: scale(1); } } transform: scale(1); } }
.zammad-chat-loader { .zammad-chat-modal {
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
@ -144,12 +144,15 @@
-ms-flex-pack: center; -ms-flex-pack: center;
justify-content: center; } justify-content: center; }
.zammad-chat-loader-text { .zammad-chat-modal-text {
font-size: 1.3em; } font-size: 1.3em;
line-height: 1.45; }
.zammad-chat-modal-text .zammad-chat-loading-animation {
font-size: 0.7em; }
.zammad-chat-loader .zammad-chat-loading-animation { .zammad-chat-modal .zammad-chat-loading-animation {
margin-right: 8px; margin-right: 8px;
margin-left: -19px; } vertical-align: middle; }
.zammad-chat-body { .zammad-chat-body {
padding: 0.5em 1em; padding: 0.5em 1em;
@ -192,6 +195,9 @@
.zammad-chat-message--typing .zammad-chat-message-body { .zammad-chat-message--typing .zammad-chat-message-body {
white-space: normal; } white-space: normal; }
.zammad-chat-loading-animation {
display: inline-block; }
.zammad-chat-loading-circle { .zammad-chat-loading-circle {
background: #c5dded; background: #c5dded;
border-radius: 100%; border-radius: 100%;

View file

@ -154,7 +154,7 @@ $baseTextColor: if($luminance < 0.2, white, black);
to { opacity: 1; transform: scale(1); } to { opacity: 1; transform: scale(1); }
} }
.zammad-chat-loader { .zammad-chat-modal {
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
@ -170,13 +170,18 @@ $baseTextColor: if($luminance < 0.2, white, black);
justify-content: center; justify-content: center;
} }
.zammad-chat-loader-text { .zammad-chat-modal-text {
font-size: 1.3em; font-size: 1.3em;
line-height: 1.45;
.zammad-chat-loading-animation {
font-size: 0.7em;
}
} }
.zammad-chat-loader .zammad-chat-loading-animation { .zammad-chat-modal .zammad-chat-loading-animation {
margin-right: 8px; margin-right: 8px;
margin-left: -19px; vertical-align: middle;
} }
.zammad-chat-body { .zammad-chat-body {
@ -232,6 +237,10 @@ $baseTextColor: if($luminance < 0.2, white, black);
white-space: normal; white-space: normal;
} }
.zammad-chat-loading-animation {
display: inline-block;
}
.zammad-chat-loading-circle { .zammad-chat-loading-circle {
background: desaturate(lightenMax($themeColor, 50%, 85%), 15%); background: desaturate(lightenMax($themeColor, 50%, 85%), 15%);
border-radius: 100%; border-radius: 100%;

View file

@ -0,0 +1,4 @@
<img class="zammad-chat-agent-avatar" src="<%= @agent.avatar %>">
<span class="zammad-chat-agent-sentence">
<span class="zammad-chat-agent-name"><%= @agent.name %></span> <%- @agentPhrase %>
</span>

View file

@ -8,24 +8,13 @@
</span> </span>
</div> </div>
<div class="zammad-chat-agent zammad-chat-is-hidden"> <div class="zammad-chat-agent zammad-chat-is-hidden">
<img class="zammad-chat-agent-avatar" src="https://s3.amazonaws.com/uifaces/faces/twitter/joshaustin/128.jpg">
<span class="zammad-chat-agent-sentence">
<span class="zammad-chat-agent-name">Adam</span> is helping you.
</span>
</div> </div>
<div class="zammad-chat-welcome"> <div class="zammad-chat-welcome">
<svg class="zammad-chat-icon" viewBox="0 0 24 24"><path d="M2 5C2 4 3 3 4 3h16c1 0 2 1 2 2v10C22 16 21 17 20 17H4C3 17 2 16 2 15V5zM12 17l6 4v-4h-6z" fill-rule="evenodd"/></svg> <svg class="zammad-chat-icon" viewBox="0 0 24 24"><path d="M2 5C2 4 3 3 4 3h16c1 0 2 1 2 2v10C22 16 21 17 20 17H4C3 17 2 16 2 15V5zM12 17l6 4v-4h-6z" fill-rule="evenodd"/></svg>
<span class="zammad-chat-welcome-text"><strong>Chat</strong> with us!</span> <span class="zammad-chat-welcome-text"><%- @invitationPhrase %></span>
</div> </div>
</div> </div>
<div class="zammad-chat-loader">
<span class="zammad-chat-loading-animation">
<span class="zammad-chat-loading-circle"></span>
<span class="zammad-chat-loading-circle"></span>
<span class="zammad-chat-loading-circle"></span>
</span>
<span class="zammad-chat-loader-text">Connecting</span>
</div>
<div class="zammad-chat-body"></div> <div class="zammad-chat-body"></div>
<form class="zammad-chat-controls"> <form class="zammad-chat-controls">
<textarea class="zammad-chat-input" rows="1" placeholder="Compose your message..."></textarea> <textarea class="zammad-chat-input" rows="1" placeholder="Compose your message..."></textarea>

View file

@ -0,0 +1,8 @@
<div class="zammad-chat-modal">
<span class="zammad-chat-loading-animation">
<span class="zammad-chat-loading-circle"></span>
<span class="zammad-chat-loading-circle"></span>
<span class="zammad-chat-loading-circle"></span>
</span>
<span class="zammad-chat-modal-text"><%= @T('Connecting') %></span>
</div>

View file

@ -0,0 +1,11 @@
<div class="zammad-chat-modal">
<div class="zammad-chat-modal-text">
<span class="zammad-chat-loading-animation">
<span class="zammad-chat-loading-circle"></span>
<span class="zammad-chat-loading-circle"></span>
<span class="zammad-chat-loading-circle"></span>
</span>
Leider sind gerade alle Mitarbeiter belegt.<br>
Warteliste-Position: <strong><%= @position %></strong>
</div>
</div>