quiq-chat
Advanced tools
Comparing version 1.8.2 to 1.8.3
@@ -141,8 +141,9 @@ 'use strict'; | ||
var bypassUrls = ['/session/web', '/agents-available']; | ||
var bypassUrls = ['/session/web', '/agents-available', '/chat']; | ||
var retryCount = 0; | ||
var timedOut = false; | ||
var timerId = void 0; | ||
var stubbornFetch = (function (url, fetchRequest) { | ||
var retryCount = 0; | ||
var timedOut = false; | ||
var timerId = void 0; | ||
var delayIfNeeded = function delayIfNeeded() { | ||
@@ -260,3 +261,3 @@ return new Promise(function (resolve) { | ||
var version = "1.8.2"; | ||
var version = "1.8.3"; | ||
@@ -315,2 +316,8 @@ // | ||
// | ||
var _onNewSession = void 0; | ||
var registerNewSessionCallback = function registerNewSessionCallback(callback) { | ||
_onNewSession = callback; | ||
}; | ||
var joinChat$1 = function joinChat() { | ||
@@ -368,4 +375,16 @@ quiqFetch(getUrlForContactPoint() + '/join', { method: 'POST', credentials: 'include' }); | ||
/** | ||
* Creates a new session and tracking ID | ||
* @param host - Host against which to call /generate | ||
* @param sessionChange - Indicates whether this is being called to replace old session. Results in newSession callback being fired. | ||
*/ | ||
var login = function login(host) { | ||
return quiqFetch(getSessionApiUrl(host) + '/generate', { credentials: 'include', method: 'POST' }); | ||
return quiqFetch(getSessionApiUrl(host) + '/generate', { | ||
credentials: 'include', | ||
method: 'POST' | ||
}, { | ||
responseType: 'JSON' | ||
}).then(function (res) { | ||
if (_onNewSession) _onNewSession(res.tokenId); | ||
}); | ||
}; | ||
@@ -614,2 +633,7 @@ | ||
this.onNewSession = function (callback) { | ||
_this2.callbacks.onNewSession = callback; | ||
return _this2; | ||
}; | ||
this.onBurn = function (callback) { | ||
@@ -656,11 +680,3 @@ _this2.callbacks.onBurn = callback; | ||
connectSocket({ | ||
socketUrl: wsInfo.url, | ||
callbacks: { | ||
onConnectionLoss: _this2._handleConnectionLoss, | ||
onConnectionEstablish: _this2._handleConnectionEstablish, | ||
onMessage: _this2._handleWebsocketMessage, | ||
onBurn: _this2._handleBurnItDown | ||
} | ||
}); | ||
_this2._connectSocket(wsInfo); | ||
@@ -823,2 +839,63 @@ if (_this2.callbacks.onConnectionStatusChange) { | ||
this._connectSocket = function (wsInfo) { | ||
connectSocket({ | ||
socketUrl: wsInfo.url, | ||
callbacks: { | ||
onConnectionLoss: _this2._handleConnectionLoss, | ||
onConnectionEstablish: _this2._handleConnectionEstablish, | ||
onMessage: _this2._handleWebsocketMessage, | ||
onBurn: _this2._handleBurnItDown | ||
} | ||
}); | ||
}; | ||
this._handleNewSession = function () { | ||
var _ref8 = _asyncToGenerator$1(regeneratorRuntime.mark(function _callee5(newTrackingId) { | ||
var wsInfo; | ||
return regeneratorRuntime.wrap(function _callee5$(_context5) { | ||
while (1) { | ||
switch (_context5.prev = _context5.next) { | ||
case 0: | ||
if (!(_this2.trackingId && newTrackingId !== _this2.trackingId)) { | ||
_context5.next = 10; | ||
break; | ||
} | ||
// Clear message and events caches (tracking ID is different now, so we essentially have a new Conversation) | ||
_this2.textMessages = []; | ||
_this2.events = []; | ||
_this2.userIsRegistered = false; | ||
if (_this2.callbacks.onNewSession) { | ||
_this2.callbacks.onNewSession(); | ||
} | ||
// Disconnect/reconnect websocket | ||
// (Connection establishment handler will refresh messages) | ||
disconnectSocket(); // Ensure we only have one websocket connection open | ||
_context5.next = 8; | ||
return fetchWebsocketInfo(); | ||
case 8: | ||
wsInfo = _context5.sent; | ||
_this2._connectSocket(wsInfo); | ||
case 10: | ||
_this2.trackingId = newTrackingId; | ||
case 11: | ||
case 'end': | ||
return _context5.stop(); | ||
} | ||
} | ||
}, _callee5, _this2); | ||
})); | ||
return function (_x3) { | ||
return _ref8.apply(this, arguments); | ||
}; | ||
}(); | ||
this._handleWebsocketMessage = function (message) { | ||
@@ -832,10 +909,5 @@ if (message.messageType === MessageTypes.CHAT_MESSAGE) { | ||
case MessageTypes.LEAVE: | ||
case MessageTypes.REGISTER: | ||
_this2._processNewMessagesAndEvents([], [message.data]); | ||
break; | ||
case MessageTypes.REGISTER: | ||
if (_this2.callbacks.onRegistration) { | ||
_this2.callbacks.onRegistration(); | ||
} | ||
_this2.userIsRegistered = true; | ||
break; | ||
case MessageTypes.AGENT_TYPING: | ||
@@ -871,16 +943,16 @@ if (_this2.callbacks.onAgentTyping) { | ||
this._handleConnectionEstablish = _asyncToGenerator$1(regeneratorRuntime.mark(function _callee5() { | ||
var _ref9, messages, events; | ||
this._handleConnectionEstablish = _asyncToGenerator$1(regeneratorRuntime.mark(function _callee6() { | ||
var _ref10, messages, events; | ||
return regeneratorRuntime.wrap(function _callee5$(_context5) { | ||
return regeneratorRuntime.wrap(function _callee6$(_context6) { | ||
while (1) { | ||
switch (_context5.prev = _context5.next) { | ||
switch (_context6.prev = _context6.next) { | ||
case 0: | ||
_context5.next = 2; | ||
_context6.next = 2; | ||
return getConversation(); | ||
case 2: | ||
_ref9 = _context5.sent; | ||
messages = _ref9.messages; | ||
events = _ref9.events; | ||
_ref10 = _context6.sent; | ||
messages = _ref10.messages; | ||
events = _ref10.events; | ||
@@ -898,6 +970,6 @@ | ||
case 'end': | ||
return _context5.stop(); | ||
return _context6.stop(); | ||
} | ||
} | ||
}, _callee5, _this2); | ||
}, _callee6, _this2); | ||
})); | ||
@@ -912,11 +984,27 @@ | ||
var sortedMessages = sortByTimestamp(_this2.textMessages.concat(newMessages)); | ||
var sortedEvents = sortByTimestamp(_this2.events.concat(newEvents)); | ||
// Apparently, it's possible (though not common) to receive duplicate messages in transcript response. | ||
// We need to take union of new and current messages to account for this | ||
_this2.textMessages = sortedMessages; | ||
_this2.events = sortedEvents; | ||
// If we found new messages, sort them, update cached textMessages, and send callback | ||
if (newMessages.length) { | ||
_this2.textMessages = sortByTimestamp(lodash.unionBy(_this2.textMessages, newMessages, 'id')); | ||
if (newMessages.length && _this2.callbacks.onNewMessages && sendNewMessageCallback) { | ||
_this2.callbacks.onNewMessages(_this2.textMessages); | ||
if (_this2.callbacks.onNewMessages && sendNewMessageCallback) { | ||
_this2.callbacks.onNewMessages(_this2.textMessages); | ||
} | ||
} | ||
// If we found new events, sort them, update cached events, and check if a new registration event was received. Fire callback if so. | ||
if (newEvents.length) { | ||
_this2.events = sortByTimestamp(lodash.unionBy(_this2.events, newEvents, 'id')); | ||
if (newEvents.find(function (e) { | ||
return e.type === MessageTypes.REGISTER; | ||
})) { | ||
if (_this2.callbacks.onRegistration) { | ||
_this2.callbacks.onRegistration(); | ||
} | ||
_this2.userIsRegistered = true; | ||
} | ||
} | ||
}; | ||
@@ -931,2 +1019,3 @@ | ||
this.connected = false; | ||
this.trackingId = null; | ||
@@ -951,2 +1040,5 @@ setGlobals({ | ||
}); | ||
// Register with apiCalls for new session events | ||
registerNewSessionCallback(this._handleNewSession); | ||
}; | ||
@@ -953,0 +1045,0 @@ |
{ | ||
"name": "quiq-chat", | ||
"version": "1.8.2", | ||
"version": "1.8.3", | ||
"description": "Library to help with network requests to create a webchat client for Quiq Messaging", | ||
@@ -5,0 +5,0 @@ "main": "build/quiq-chat.js", |
@@ -33,2 +33,4 @@ // @flow | ||
const testTrackingId = 'dsafdsafaweufh'; | ||
describe('QuiqChatClient', () => { | ||
@@ -39,2 +41,3 @@ const onNewMessages = jest.fn(); | ||
const onErrorResolved = jest.fn(); | ||
const onNewSession = jest.fn(); | ||
const onConnectionStatusChange = jest.fn(); | ||
@@ -62,2 +65,3 @@ const onBurn = jest.fn(); | ||
.onRegistration(onRegistration) | ||
.onNewSession(onNewSession) | ||
.onBurn(onBurn); | ||
@@ -177,2 +181,80 @@ | ||
describe('handling new session', () => { | ||
describe('when no trackingId is defined, i.e., this is first session', () => { | ||
it('updates cached trackingId', () => { | ||
if (!client) { | ||
throw new Error('Client should be defined'); | ||
} | ||
client._handleNewSession(testTrackingId); | ||
expect(client.trackingId).toBe(testTrackingId); | ||
}); | ||
it('does NOT fire new session callback', () => { | ||
if (!client) { | ||
throw new Error('Client should be defined'); | ||
} | ||
client._handleNewSession(testTrackingId); | ||
expect(client.callbacks.onNewSession).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
describe('when trackingId has not changed, i.e. session was refreshed', () => { | ||
beforeEach(() => { | ||
if (!client) { | ||
throw new Error('Client should be defined'); | ||
} | ||
client.trackingId = testTrackingId; | ||
}); | ||
it('updates cached trackingId', () => { | ||
if (!client) { | ||
throw new Error('Client should be defined'); | ||
} | ||
client._handleNewSession(testTrackingId); | ||
expect(client.trackingId).toBe(testTrackingId); | ||
}); | ||
it('does NOT fire new session callback', () => { | ||
if (!client) { | ||
throw new Error('Client should be defined'); | ||
} | ||
client._handleNewSession(testTrackingId); | ||
expect(client.callbacks.onNewSession).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
describe('when trackingId has changed, i.e. new conversation', () => { | ||
beforeEach(() => { | ||
if (!client) { | ||
throw new Error('Client should be defined'); | ||
} | ||
client.trackingId = 'oldId'; | ||
}); | ||
it('updates cached trackingId', async () => { | ||
if (!client) { | ||
throw new Error('Client should be defined'); | ||
} | ||
await client._handleNewSession(testTrackingId); | ||
expect(client.trackingId).toBe(testTrackingId); | ||
}); | ||
it('does fire new session callback', () => { | ||
if (!client) { | ||
throw new Error('Client should be defined'); | ||
} | ||
client._handleNewSession(testTrackingId); | ||
expect(client.callbacks.onNewSession).toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); | ||
describe('getting new Register event', () => { | ||
@@ -179,0 +261,0 @@ const newEvent = {type: 'Register', id: 'reg1', timestamp: 3}; |
@@ -7,2 +7,8 @@ // @flow | ||
let _onNewSession: (newTrackingId: string) => any; | ||
export const registerNewSessionCallback = (callback: (newTrackingId: string) => any) => { | ||
_onNewSession = callback; | ||
}; | ||
export const joinChat = () => { | ||
@@ -62,4 +68,20 @@ quiqFetch(`${getUrlForContactPoint()}/join`, {method: 'POST', credentials: 'include'}); | ||
/** | ||
* Creates a new session and tracking ID | ||
* @param host - Host against which to call /generate | ||
* @param sessionChange - Indicates whether this is being called to replace old session. Results in newSession callback being fired. | ||
*/ | ||
export const login = (host?: string) => | ||
quiqFetch(`${getSessionApiUrl(host)}/generate`, {credentials: 'include', method: 'POST'}); | ||
quiqFetch( | ||
`${getSessionApiUrl(host)}/generate`, | ||
{ | ||
credentials: 'include', | ||
method: 'POST', | ||
}, | ||
{ | ||
responseType: 'JSON', | ||
}, | ||
).then(res => { | ||
if (_onNewSession) _onNewSession(res.tokenId); | ||
}); | ||
@@ -66,0 +88,0 @@ export const validateSession = () => quiqFetch(getSessionApiUrl(), {credentials: 'include'}); |
@@ -8,3 +8,3 @@ // @flow | ||
import {registerCallbacks, onInit} from './stubbornFetch'; | ||
import {differenceBy, last, partition} from 'lodash'; | ||
import {differenceBy, unionBy, last, partition} from 'lodash'; | ||
import {sortByTimestamp} from './utils'; | ||
@@ -30,2 +30,3 @@ import type {QuiqChatCallbacks} from 'types'; | ||
userIsRegistered: boolean; | ||
trackingId: ?string; | ||
@@ -40,2 +41,3 @@ constructor(host: string, contactPoint: string) { | ||
this.connected = false; | ||
this.trackingId = null; | ||
@@ -60,2 +62,5 @@ setGlobals({ | ||
}); | ||
// Register with apiCalls for new session events | ||
API.registerNewSessionCallback(this._handleNewSession); | ||
} | ||
@@ -103,2 +108,7 @@ | ||
onNewSession = (callback: () => void): QuiqChatClient => { | ||
this.callbacks.onNewSession = callback; | ||
return this; | ||
}; | ||
onBurn = (callback: () => void): QuiqChatClient => { | ||
@@ -127,11 +137,3 @@ this.callbacks.onBurn = callback; | ||
const wsInfo: {url: string} = await API.fetchWebsocketInfo(); | ||
connectSocket({ | ||
socketUrl: wsInfo.url, | ||
callbacks: { | ||
onConnectionLoss: this._handleConnectionLoss, | ||
onConnectionEstablish: this._handleConnectionEstablish, | ||
onMessage: this._handleWebsocketMessage, | ||
onBurn: this._handleBurnItDown, | ||
}, | ||
}); | ||
this._connectSocket(wsInfo); | ||
@@ -216,3 +218,35 @@ if (this.callbacks.onConnectionStatusChange) { | ||
/** Private Members **/ | ||
_connectSocket = (wsInfo: {url: string}) => { | ||
connectSocket({ | ||
socketUrl: wsInfo.url, | ||
callbacks: { | ||
onConnectionLoss: this._handleConnectionLoss, | ||
onConnectionEstablish: this._handleConnectionEstablish, | ||
onMessage: this._handleWebsocketMessage, | ||
onBurn: this._handleBurnItDown, | ||
}, | ||
}); | ||
}; | ||
_handleNewSession = async (newTrackingId: string) => { | ||
if (this.trackingId && newTrackingId !== this.trackingId) { | ||
// Clear message and events caches (tracking ID is different now, so we essentially have a new Conversation) | ||
this.textMessages = []; | ||
this.events = []; | ||
this.userIsRegistered = false; | ||
if (this.callbacks.onNewSession) { | ||
this.callbacks.onNewSession(); | ||
} | ||
// Disconnect/reconnect websocket | ||
// (Connection establishment handler will refresh messages) | ||
disconnectSocket(); // Ensure we only have one websocket connection open | ||
const wsInfo: {url: string} = await API.fetchWebsocketInfo(); | ||
this._connectSocket(wsInfo); | ||
} | ||
this.trackingId = newTrackingId; | ||
}; | ||
_handleWebsocketMessage = (message: AtmosphereMessage) => { | ||
@@ -226,10 +260,5 @@ if (message.messageType === MessageTypes.CHAT_MESSAGE) { | ||
case MessageTypes.LEAVE: | ||
case MessageTypes.REGISTER: | ||
this._processNewMessagesAndEvents([], [message.data]); | ||
break; | ||
case MessageTypes.REGISTER: | ||
if (this.callbacks.onRegistration) { | ||
this.callbacks.onRegistration(); | ||
} | ||
this.userIsRegistered = true; | ||
break; | ||
case MessageTypes.AGENT_TYPING: | ||
@@ -282,14 +311,28 @@ if (this.callbacks.onAgentTyping) { | ||
): void => { | ||
const newMessages = differenceBy(messages, this.textMessages, 'id'); | ||
const newEvents = differenceBy(events, this.events, 'id'); | ||
const newMessages: Array<TextMessage> = differenceBy(messages, this.textMessages, 'id'); | ||
const newEvents: Array<Event> = differenceBy(events, this.events, 'id'); | ||
const sortedMessages = sortByTimestamp(this.textMessages.concat(newMessages)); | ||
const sortedEvents = sortByTimestamp(this.events.concat(newEvents)); | ||
// Apparently, it's possible (though not common) to receive duplicate messages in transcript response. | ||
// We need to take union of new and current messages to account for this | ||
this.textMessages = sortedMessages; | ||
this.events = sortedEvents; | ||
// If we found new messages, sort them, update cached textMessages, and send callback | ||
if (newMessages.length) { | ||
this.textMessages = sortByTimestamp(unionBy(this.textMessages, newMessages, 'id')); | ||
if (newMessages.length && this.callbacks.onNewMessages && sendNewMessageCallback) { | ||
this.callbacks.onNewMessages(this.textMessages); | ||
if (this.callbacks.onNewMessages && sendNewMessageCallback) { | ||
this.callbacks.onNewMessages(this.textMessages); | ||
} | ||
} | ||
// If we found new events, sort them, update cached events, and check if a new registration event was received. Fire callback if so. | ||
if (newEvents.length) { | ||
this.events = sortByTimestamp(unionBy(this.events, newEvents, 'id')); | ||
if (newEvents.find(e => e.type === MessageTypes.REGISTER)) { | ||
if (this.callbacks.onRegistration) { | ||
this.callbacks.onRegistration(); | ||
} | ||
this.userIsRegistered = true; | ||
} | ||
} | ||
}; | ||
@@ -296,0 +339,0 @@ } |
@@ -25,8 +25,9 @@ // @flow | ||
const bypassUrls = ['/session/web', '/agents-available']; | ||
const bypassUrls = ['/session/web', '/agents-available', '/chat']; | ||
let retryCount = 0; | ||
let timedOut = false; | ||
let timerId; | ||
export default (url: string, fetchRequest: RequestOptions) => { | ||
let retryCount = 0; | ||
let timedOut = false; | ||
let timerId; | ||
const delayIfNeeded = () => | ||
@@ -33,0 +34,0 @@ new Promise(resolve => { |
@@ -54,2 +54,3 @@ // @flow | ||
onBurn?: () => void, | ||
onNewSession?: () => void, | ||
}; | ||
@@ -56,0 +57,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
744155
59
8492