Added idle and inactive timeouts.

This commit is contained in:
Martin Edenhofer 2015-11-26 00:40:52 +01:00
parent 6ce3116c33
commit d71090df6e
4 changed files with 183 additions and 43 deletions

View file

@ -14,13 +14,15 @@ do($ = window.jQuery, window) ->
host: ''
debug: false
flat: false
lang: undefined,
cssAutoload: true,
cssUrl: undefined,
lang: undefined
cssAutoload: true
cssUrl: undefined
fontSize: undefined
buttonClass: 'open-zammad-chat'
inactiveClass: 'is-inactive'
title: '<strong>Chat</strong> with us!'
idleTimeout: 8
inactiveTimeout: 20
_messageCount: 0
isOpen: true
@ -48,8 +50,8 @@ do($ = window.jQuery, window) ->
'All colleges are busy.': 'Alle Kollegen sind belegt.'
'You are on waiting list position <strong>%s</strong>.': 'Sie sind in der Warteliste an der Position <strong>%s</strong>.'
'Start new conversation': 'Neue Konversation starten'
'Since you didn\'t respond in the last %s your conversation with <strong>%s</strong> got closed.': 'Da sie in den letzten %s nichts geschrieben haben wurde ihre Konversation mit <strong>%s</strong> geschlossen.'
'minutes': 'Minuten'
'Since you didn\'t respond in the last %s minutes your conversation with <strong>%s</strong> got closed.': 'Da Sie in den letzten %s Minuten nichts geschrieben haben wurde Ihre Konversation mit <strong>%s</strong> geschlossen.'
'Since you didn\'t respond in the last %s minutes your conversation got closed.': 'Da Sie in den letzten %s Minuten nichts geschrieben haben wurde Ihre Konversation geschlossen.'
sessionId: undefined
T: (string, items...) =>
@ -86,6 +88,10 @@ do($ = window.jQuery, window) ->
@options = $.extend {}, @defaults, options
# check prerequisites
if !$
@state = 'unsupported'
@log 'notice', 'Chat: no jquery found!'
return
if !window.WebSocket or !sessionStorage
@state = 'unsupported'
@log 'notice', 'Chat: Browser not supported!'
@ -143,7 +149,9 @@ do($ = window.jQuery, window) ->
@log 'debug', 'ws:onmessage', pipe
switch pipe.event
when 'chat_error'
@log 'error', pipe.data
@log 'notice', pipe.data
if pipe.data && pipe.data.state is 'chat_disabled'
@wsClose()
when 'chat_session_message'
return if pipe.data.self_written
@receiveMessage pipe.data
@ -194,6 +202,8 @@ do($ = window.jQuery, window) ->
$(".#{ @options.buttonClass }").hide()
reopenSession: (data) =>
@inactiveTimeoutStart()
unfinishedMessage = sessionStorage.getItem 'unfinished_message'
# rerender chat history
@ -236,6 +246,7 @@ do($ = window.jQuery, window) ->
@isTyping = new Date()
@send 'chat_session_typing',
session_id: @sessionId
@inactiveTimeoutStart()
onSubmit: (event) =>
event.preventDefault()
@ -243,9 +254,10 @@ do($ = window.jQuery, window) ->
sendMessage: ->
message = @input.val()
return if !message
@inactiveTimeoutStart()
sessionStorage.removeItem 'unfinished_message'
messageElement = @view('message')
@ -273,6 +285,8 @@ do($ = window.jQuery, window) ->
session_id: @sessionId
receiveMessage: (data) =>
@inactiveTimeoutStart()
# hide writing indicator
@onAgentTypingEnd()
@ -307,10 +321,10 @@ do($ = window.jQuery, window) ->
@isOpen = true
if !@sessionId
@session_init()
@sessionInit()
onOpenAnimationEnd: ->
#@showTimeout()
onOpenAnimationEnd: =>
@idleTimeoutStop()
close: (event) =>
return @state if @state is 'off' or @state is 'unsupported'
@ -319,12 +333,25 @@ do($ = window.jQuery, window) ->
# only close if session_id exists
return if !@sessionId
# send close
@send 'chat_session_close',
session_id: @sessionId
# stop timer
@inactiveTimeoutStop()
# delete input store
sessionStorage.removeItem 'unfinished_message'
# stop delay of initial queue position
if @onInitialQueueDelayId
clearTimeout(@onInitialQueueDelayId)
if event
@closeWindow()
@setSessionId undefined
closeWindow: =>
@el.removeClass('zammad-chat-is-open')
remainerHeight = @el.height() - @el.find('.zammad-chat-header').outerHeight()
@ -335,12 +362,6 @@ do($ = window.jQuery, window) ->
@disconnect()
@isOpen = false
@send 'chat_session_close',
session_id: @sessionId
@setSessionId undefined
sessionStorage.removeItem 'unfinished_message'
# restart connection
@onWebSocketOpen()
@ -448,7 +469,7 @@ do($ = window.jQuery, window) ->
scrollToBottom: ->
@el.find('.zammad-chat-body').scrollTop($('.zammad-chat-body').prop('scrollHeight'))
session_init: ->
sessionInit: ->
@send('chat_session_init')
detectHost: ->
@ -484,6 +505,7 @@ do($ = window.jQuery, window) ->
@reconnectDelayId = setTimeout(@wsConnect, 5000)
onWebSocketOpen: =>
@idleTimeoutStart()
@sessionId = sessionStorage.getItem('sessionId')
@log 'debug', 'ws connected'
@ -511,6 +533,7 @@ do($ = window.jQuery, window) ->
@addStatus @T('Chat closed by %s', data.realname)
@disableInput()
@setAgentOnlineState 'offline'
@inactiveTimeoutStop()
disconnect: ->
@showLoader()
@ -552,8 +575,11 @@ do($ = window.jQuery, window) ->
showTimeout: ->
@el.find('.zammad-chat-body').html @view('timeout')
agent: @agent.name
delay: 10
unit: @T('minutes')
delay: @options.inactiveTimeout
@close()
reload = ->
location.reload()
@el.find('.js-restart').click reload
showLoader: ->
@el.find('.zammad-chat-body').html @view('loader')()
@ -583,4 +609,31 @@ do($ = window.jQuery, window) ->
newSS.href = 'data:text/css,' + escape(styles)
document.getElementsByTagName('head')[0].appendChild(newSS)
inactiveTimeoutStart: =>
@inactiveTimeoutStop()
delay = =>
@log 'debug', "Inactive timeout of #{@options.inactiveTimeout} minutes, show timeout screen."
@state = 'off'
@setAgentOnlineState 'offline'
@showTimeout()
@wsClose()
@inactiveTimeoutStopDelayId = setTimeout(delay, @options.inactiveTimeout * 1000 * 60)
inactiveTimeoutStop: =>
return if !@inactiveTimeoutStopDelayId
clearTimeout(@inactiveTimeoutStopDelayId)
idleTimeoutStart: =>
@idleTimeoutStop()
delay = =>
@log 'debug', "Idle timeout of #{@options.idleTimeout} minutes, hide widget"
@state = 'off'
@hide()
@wsClose()
@idleTimeoutStopDelayId = setTimeout(delay, @options.idleTimeout * 1000 * 60)
idleTimeoutStop: =>
return if !@idleTimeoutStopDelayId
clearTimeout(@idleTimeoutStopDelayId)
window.ZammadChat = ZammadChat

View file

@ -20,7 +20,9 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
fontSize: void 0,
buttonClass: 'open-zammad-chat',
inactiveClass: 'is-inactive',
title: '<strong>Chat</strong> with us!'
title: '<strong>Chat</strong> with us!',
idleTimeout: 8,
inactiveTimeout: 20
};
ZammadChat.prototype._messageCount = 0;
@ -61,8 +63,8 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
'All colleges are busy.': 'Alle Kollegen sind belegt.',
'You are on waiting list position <strong>%s</strong>.': 'Sie sind in der Warteliste an der Position <strong>%s</strong>.',
'Start new conversation': 'Neue Konversation starten',
'Since you didn\'t respond in the last %s your conversation with <strong>%s</strong> got closed.': 'Da sie in den letzten %s nichts geschrieben haben wurde ihre Konversation mit <strong>%s</strong> geschlossen.',
'minutes': 'Minuten'
'Since you didn\'t respond in the last %s minutes your conversation with <strong>%s</strong> got closed.': 'Da Sie in den letzten %s Minuten nichts geschrieben haben wurde Ihre Konversation mit <strong>%s</strong> geschlossen.',
'Since you didn\'t respond in the last %s minutes your conversation got closed.': 'Da Sie in den letzten %s Minuten nichts geschrieben haben wurde Ihre Konversation geschlossen.'
}
};
@ -117,6 +119,10 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
};
function ZammadChat(options) {
this.idleTimeoutStop = bind(this.idleTimeoutStop, this);
this.idleTimeoutStart = bind(this.idleTimeoutStart, this);
this.inactiveTimeoutStop = bind(this.inactiveTimeoutStop, this);
this.inactiveTimeoutStart = bind(this.inactiveTimeoutStart, this);
this.setAgentOnlineState = bind(this.setAgentOnlineState, this);
this.onConnectionEstablished = bind(this.onConnectionEstablished, this);
this.setSessionId = bind(this.setSessionId, this);
@ -133,6 +139,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.onCloseAnimationEnd = bind(this.onCloseAnimationEnd, this);
this.closeWindow = bind(this.closeWindow, this);
this.close = bind(this.close, this);
this.onOpenAnimationEnd = bind(this.onOpenAnimationEnd, this);
this.open = bind(this.open, this);
this.renderMessage = bind(this.renderMessage, this);
this.receiveMessage = bind(this.receiveMessage, this);
@ -148,6 +155,11 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.log = bind(this.log, this);
this.T = bind(this.T, this);
this.options = $.extend({}, this.defaults, options);
if (!$) {
this.state = 'unsupported';
this.log('notice', 'Chat: no jquery found!');
return;
}
if (!window.WebSocket || !sessionStorage) {
this.state = 'unsupported';
this.log('notice', 'Chat: Browser not supported!');
@ -211,7 +223,10 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.log('debug', 'ws:onmessage', pipe);
switch (pipe.event) {
case 'chat_error':
this.log('error', pipe.data);
this.log('notice', pipe.data);
if (pipe.data && pipe.data.state === 'chat_disabled') {
this.wsClose();
}
break;
case 'chat_session_message':
if (pipe.data.self_written) {
@ -284,6 +299,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
ZammadChat.prototype.reopenSession = function(data) {
var i, len, message, ref, unfinishedMessage;
this.inactiveTimeoutStart();
unfinishedMessage = sessionStorage.getItem('unfinished_message');
if (data.agent) {
this.onConnectionEstablished(data);
@ -322,9 +338,10 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
return;
}
this.isTyping = new Date();
return this.send('chat_session_typing', {
this.send('chat_session_typing', {
session_id: this.sessionId
});
return this.inactiveTimeoutStart();
};
ZammadChat.prototype.onSubmit = function(event) {
@ -338,6 +355,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
if (!message) {
return;
}
this.inactiveTimeoutStart();
sessionStorage.removeItem('unfinished_message');
messageElement = this.view('message')({
message: message,
@ -362,6 +380,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
};
ZammadChat.prototype.receiveMessage = function(data) {
this.inactiveTimeoutStart();
this.onAgentTypingEnd();
this.maybeAddTimestamp();
return this.renderMessage({
@ -399,11 +418,13 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
}
this.isOpen = true;
if (!this.sessionId) {
return this.session_init();
return this.sessionInit();
}
};
ZammadChat.prototype.onOpenAnimationEnd = function() {};
ZammadChat.prototype.onOpenAnimationEnd = function() {
return this.idleTimeoutStop();
};
ZammadChat.prototype.close = function(event) {
if (this.state === 'off' || this.state === 'unsupported') {
@ -415,10 +436,18 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
if (!this.sessionId) {
return;
}
this.send('chat_session_close', {
session_id: this.sessionId
});
this.inactiveTimeoutStop();
sessionStorage.removeItem('unfinished_message');
if (this.onInitialQueueDelayId) {
clearTimeout(this.onInitialQueueDelayId);
}
return this.closeWindow();
if (event) {
this.closeWindow();
}
return this.setSessionId(void 0);
};
ZammadChat.prototype.closeWindow = function() {
@ -434,11 +463,6 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.el.removeClass('zammad-chat-is-visible');
this.disconnect();
this.isOpen = false;
this.send('chat_session_close', {
session_id: this.sessionId
});
this.setSessionId(void 0);
sessionStorage.removeItem('unfinished_message');
return this.onWebSocketOpen();
};
@ -555,7 +579,7 @@ 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'));
};
ZammadChat.prototype.session_init = function() {
ZammadChat.prototype.sessionInit = function() {
return this.send('chat_session_init');
};
@ -604,6 +628,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
};
ZammadChat.prototype.onWebSocketOpen = function() {
this.idleTimeoutStart();
this.sessionId = sessionStorage.getItem('sessionId');
this.log('debug', 'ws connected');
this.send('chat_status_customer', {
@ -630,7 +655,8 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
ZammadChat.prototype.onSessionClosed = function(data) {
this.addStatus(this.T('Chat closed by %s', data.realname));
this.disableInput();
return this.setAgentOnlineState('offline');
this.setAgentOnlineState('offline');
return this.inactiveTimeoutStop();
};
ZammadChat.prototype.disconnect = function() {
@ -673,11 +699,16 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
};
ZammadChat.prototype.showTimeout = function() {
return this.el.find('.zammad-chat-body').html(this.view('timeout')({
var reload;
this.el.find('.zammad-chat-body').html(this.view('timeout')({
agent: this.agent.name,
delay: 10,
unit: this.T('minutes')
delay: this.options.inactiveTimeout
}));
this.close();
reload = function() {
return location.reload();
};
return this.el.find('.js-restart').click(reload);
};
ZammadChat.prototype.showLoader = function() {
@ -709,6 +740,49 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
return document.getElementsByTagName('head')[0].appendChild(newSS);
};
ZammadChat.prototype.inactiveTimeoutStart = function() {
var delay;
this.inactiveTimeoutStop();
delay = (function(_this) {
return function() {
_this.log('debug', "Inactive timeout of " + _this.options.inactiveTimeout + " minutes, show timeout screen.");
_this.state = 'off';
_this.setAgentOnlineState('offline');
_this.showTimeout();
return _this.wsClose();
};
})(this);
return this.inactiveTimeoutStopDelayId = setTimeout(delay, this.options.inactiveTimeout * 1000 * 60);
};
ZammadChat.prototype.inactiveTimeoutStop = function() {
if (!this.inactiveTimeoutStopDelayId) {
return;
}
return clearTimeout(this.inactiveTimeoutStopDelayId);
};
ZammadChat.prototype.idleTimeoutStart = function() {
var delay;
this.idleTimeoutStop();
delay = (function(_this) {
return function() {
_this.log('debug', "Idle timeout of " + _this.options.idleTimeout + " minutes, hide widget");
_this.state = 'off';
_this.hide();
return _this.wsClose();
};
})(this);
return this.idleTimeoutStopDelayId = setTimeout(delay, this.options.idleTimeout * 1000 * 60);
};
ZammadChat.prototype.idleTimeoutStop = function() {
if (!this.idleTimeoutStopDelayId) {
return;
}
return clearTimeout(this.idleTimeoutStopDelayId);
};
return ZammadChat;
})();
@ -1159,9 +1233,17 @@ window.zammadChatTemplates["timeout"] = function (__obj) {
(function() {
__out.push('<div class="zammad-chat-modal">\n <div class="zammad-chat-modal-text">\n ');
__out.push(this.T('Since you didn\'t respond in the last %s your conversation with <strong>%s</strong> got closed.', this.delay + " " + this.unit, this.agent));
if (this.agent) {
__out.push('\n ');
__out.push(this.T('Since you didn\'t respond in the last %s minutes your conversation with <strong>%s</strong> got closed.', this.delay, this.agent));
__out.push('\n ');
} else {
__out.push('\n ');
__out.push(this.T('Since you didn\'t respond in the last %s minutes your conversation got closed.', this.delay));
__out.push('\n ');
}
__out.push('<br>\n <div class="zammad-chat-button"');
__out.push('\n <br>\n <div class="zammad-chat-button js-restart"');
if (this.background) {
__out.push(__sanitize(" style='background: " + this.background + "'"));

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,11 @@
<div class="zammad-chat-modal">
<div class="zammad-chat-modal-text">
<%- @T('Since you didn\'t respond in the last %s your conversation with <strong>%s</strong> got closed.', "#{ @delay } #{ @unit }", @agent) %><br>
<div class="zammad-chat-button"<%= " style='background: #{ @background }'" if @background %>><%- @T('Start new conversation') %></div>
<% if @agent: %>
<%- @T('Since you didn\'t respond in the last %s minutes your conversation with <strong>%s</strong> got closed.', @delay, @agent) %>
<% else: %>
<%- @T('Since you didn\'t respond in the last %s minutes your conversation got closed.', @delay) %>
<% end %>
<br>
<div class="zammad-chat-button js-restart"<%= " style='background: #{ @background }'" if @background %>><%- @T('Start new conversation') %></div>
</div>
</div>