quiq-chat
Advanced tools
Comparing version 1.23.1 to 1.24.0
{ | ||
"name": "quiq-chat", | ||
"version": "1.23.1", | ||
"version": "1.24.0", | ||
"description": | ||
@@ -11,4 +11,3 @@ "Library to help with network requests to create a webchat client for Quiq Messaging", | ||
"lint": "node ./node_modules/eslint/bin/eslint.js src/", | ||
"test": | ||
"flow && jest --env=jsdom --coverage && node ./node_modules/eslint/bin/eslint.js src/ --quiet", | ||
"test": "flow && jest --env=jsdom --coverage && node ./node_modules/eslint/bin/eslint.js src/ --quiet", | ||
"flow": "flow", | ||
@@ -21,16 +20,25 @@ "jest": "jest --watch --env=jsdom", | ||
"lint-staged": { | ||
"src/**/*.js": ["prettier --write", "git add"] | ||
"src/**/*.js": [ | ||
"prettier --write", | ||
"git add" | ||
] | ||
}, | ||
"jest": { | ||
"modulePaths": ["src", "node_modules"], | ||
"modulePaths": [ | ||
"src", | ||
"node_modules" | ||
], | ||
"rootDir": ".", | ||
"setupFiles": ["./jest.setup.js"], | ||
"setupFiles": [ | ||
"./jest.setup.js" | ||
], | ||
"notify": true | ||
}, | ||
"keywords": ["quiq"], | ||
"keywords": [ | ||
"quiq" | ||
], | ||
"author": "nate.norberg@goquiq.com", | ||
"license": "MIT", | ||
"dependencies": { | ||
"atmosphere.js": | ||
"https://github.com/Quiq/atmosphere-js/tarball/cf35c913143cf2c391b6bcb694ae8a47794a1b88", | ||
"atmosphere.js": "https://github.com/Quiq/atmosphere-js/tarball/cf35c913143cf2c391b6bcb694ae8a47794a1b88", | ||
"isomorphic-fetch": "2.2.1", | ||
@@ -37,0 +45,0 @@ "jwt-decode": "2.2.0", |
@@ -139,3 +139,3 @@ # quiq-chat [![Build Status](https://travis-ci.org/Quiq/quiq-chat.svg?branch=master)](https://travis-ci.org/Quiq/quiq-chat) [![npm version](https://badge.fury.io/js/quiq-chat.svg)](https://badge.fury.io/js/quiq-chat) [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) | ||
#### updateMessagePreview(text:string, typing:boolean) => void | ||
#### updateTypingIndicator(text:string, typing:boolean) => void | ||
Sends a message to Quiq Messaging that the end user is typing and what they've typed in the message field | ||
@@ -142,0 +142,0 @@ |
@@ -408,3 +408,3 @@ // @flow | ||
describe('sendMessage', () => { | ||
describe('sendTextMessage', () => { | ||
beforeEach(() => { | ||
@@ -435,7 +435,7 @@ jest.clearAllMocks(); | ||
QuiqChatClient.connected = true; | ||
QuiqChatClient.sendMessage('text'); | ||
QuiqChatClient.sendTextMessage('text'); | ||
}); | ||
it('proxies call on send messagewqw', () => { | ||
expect(API.addMessage).toBeCalledWith('text'); | ||
expect(API.sendTextMessage).toBeCalledWith('text'); | ||
}); | ||
@@ -452,3 +452,3 @@ | ||
describe('updateMessagePreview', () => { | ||
describe('updateTypingIndicator', () => { | ||
it('proxies call', () => { | ||
@@ -459,4 +459,4 @@ if (!QuiqChatClient) { | ||
QuiqChatClient.updateMessagePreview('text', true); | ||
expect(API.updateMessagePreview).toBeCalledWith('text', true); | ||
QuiqChatClient.updateTypingIndicator('text', true); | ||
expect(API.updateTypingIndicator).toBeCalledWith('text', true); | ||
}); | ||
@@ -463,0 +463,0 @@ }); |
@@ -12,3 +12,3 @@ // @flow | ||
import {setAccessToken, getAccessToken, getTrackingId} from './storage'; | ||
import type {Conversation, ChatMetadata, EmailTranscriptPayload} from 'types'; | ||
import type {Conversation, ChatMetadata, UploadDirective, EmailTranscriptPayload} from 'types'; | ||
import logger from './logging'; | ||
@@ -34,3 +34,3 @@ import Raven from 'raven-js'; | ||
export const addMessage = (text: string) => | ||
export const sendTextMessage = (text: string) => | ||
quiqFetch(`${getUrlForContactPoint()}/send-message`, { | ||
@@ -41,2 +41,33 @@ method: 'POST', | ||
export const sendAttachmentMessage = (uploadId: string): Promise<{id: string}> => | ||
quiqFetch( | ||
`${getUrlForContactPoint()}/send-attachment`, | ||
{ | ||
method: 'POST', | ||
body: JSON.stringify({uploadId}), | ||
}, | ||
{ | ||
responseType: 'JSON', | ||
requestType: 'JSON', | ||
}, | ||
); | ||
export type UploadDirectivesResponse = { | ||
uploads: Array<UploadDirective>, | ||
}; | ||
export const getAttachmentMessageUploadDirectives = ( | ||
numUploads: number = 1, | ||
): Promise<UploadDirectivesResponse> => | ||
quiqFetch( | ||
`${getUrlForContactPoint()}/prepare-uploads`, | ||
{ | ||
method: 'POST', | ||
body: JSON.stringify({numUploads}), | ||
}, | ||
{ | ||
responseType: 'JSON', | ||
requestType: 'JSON', | ||
}, | ||
); | ||
export const fetchWebsocketInfo = (): Promise<{url: string, protocol: string}> => | ||
@@ -51,3 +82,3 @@ quiqFetch(`${getUrlForContactPoint()}/socket-info`, undefined, {responseType: 'JSON'}); | ||
export const updateMessagePreview = (text: string, typing: boolean) => | ||
export const updateTypingIndicator = (text: string, typing: boolean) => | ||
quiqFetch(`${getUrlForContactPoint()}/typing`, { | ||
@@ -54,0 +85,0 @@ method: 'POST', |
@@ -6,2 +6,3 @@ // @flow | ||
TEXT: 'Text', | ||
ATTACHMENT: 'Attachment', | ||
CHAT_MESSAGE: 'ChatMessage', | ||
@@ -8,0 +9,0 @@ JOIN: 'Join', |
@@ -11,3 +11,2 @@ // @flow | ||
import {registerCallbacks, onInit, setClientInactive} from './stubbornFetch'; | ||
import type {ChatMessage, BurnItDownMessage, TextMessage, ApiError, Event} from './types'; | ||
import differenceBy from 'lodash/differenceBy'; | ||
@@ -23,3 +22,13 @@ import unionBy from 'lodash/unionBy'; | ||
} from './Utils/utils'; | ||
import type {QuiqChatCallbacks, ConversationResult, EmailTranscriptPayload} from 'types'; | ||
import * as S3 from './services/S3'; | ||
import type { | ||
ConversationElement, | ||
ConversationMessage, | ||
BurnItDownMessage, | ||
ApiError, | ||
Event, | ||
QuiqChatCallbacks, | ||
ConversationResult, | ||
EmailTranscriptPayload, | ||
} from './types'; | ||
import * as storage from './storage'; | ||
@@ -32,7 +41,9 @@ import logger from './logging'; | ||
const log = logger('QuiqChatClient'); | ||
const getConversation = async (): Promise<ConversationResult> => { | ||
const conversationMessageTypes = [MessageTypes.TEXT, MessageTypes.ATTACHMENT]; | ||
const conversation = await API.fetchConversation(); | ||
const partitionedConversation = partition(conversation.messages, { | ||
type: MessageTypes.TEXT, | ||
}); | ||
const partitionedConversation = partition(conversation.messages, m => | ||
conversationMessageTypes.includes(m.type), | ||
); | ||
const messages = partitionedConversation[0]; | ||
@@ -53,3 +64,3 @@ const events = partitionedConversation[1]; | ||
callbacks: QuiqChatCallbacks = {}; | ||
textMessages: Array<TextMessage> = []; | ||
messages: Array<ConversationMessage> = []; | ||
events: Array<Event> = []; | ||
@@ -76,3 +87,3 @@ connected: boolean = false; | ||
onNewMessages = (callback: (messages: Array<TextMessage>) => void): QuiqChatClient => { | ||
onNewMessages = (callback: (messages: Array<ConversationMessage>) => void): QuiqChatClient => { | ||
this.callbacks.onNewMessages = callback; | ||
@@ -154,4 +165,4 @@ return this; | ||
// Send all messages in initial newMessages callback | ||
if (this.callbacks.onNewMessages && this.textMessages.length) { | ||
this.callbacks.onNewMessages(this.textMessages); | ||
if (this.callbacks.onNewMessages && this.messages.length) { | ||
this.callbacks.onNewMessages(this.messages); | ||
} | ||
@@ -179,3 +190,3 @@ | ||
getMessages = async (cache: boolean = true): Promise<Array<TextMessage>> => { | ||
getMessages = async (cache: boolean = true): Promise<Array<ConversationMessage>> => { | ||
if (!cache || !this.connected) { | ||
@@ -186,3 +197,3 @@ const conversation = await getConversation(); | ||
return this.textMessages; | ||
return this.messages; | ||
}; | ||
@@ -211,3 +222,3 @@ | ||
sendMessage = async (text: string) => { | ||
sendTextMessage = async (text: string) => { | ||
if (!this.connected) { | ||
@@ -218,3 +229,3 @@ this._establishWebSocketConnection(() => { | ||
return API.addMessage(text); | ||
return API.sendTextMessage(text); | ||
}); | ||
@@ -225,3 +236,3 @@ } else { | ||
return API.addMessage(text); | ||
return API.sendTextMessage(text); | ||
} | ||
@@ -234,6 +245,32 @@ }; | ||
updateMessagePreview = (text: string, typing: boolean) => { | ||
return API.updateMessagePreview(text, typing); | ||
sendAttachmentMessage = async ( | ||
file: File, | ||
progressCallback: (progress: number) => void, | ||
): Promise<string> => { | ||
if (!this.connected) { | ||
await this._establishWebSocketConnection(); | ||
storage.setQuiqChatContainerVisible(true); | ||
storage.setQuiqUserIsSubscribed(true); | ||
} | ||
// Returns an array of directives, but we'll always be asking for only 1 here | ||
const uploadDirectives = await API.getAttachmentMessageUploadDirectives(); | ||
const uploadDirective = uploadDirectives.uploads[0]; | ||
const {url, formEntries} = uploadDirective.directive; | ||
try { | ||
await S3.uploadAttachment(file, url, formEntries, progressCallback); | ||
} catch (e) { | ||
// Remove temporary attachment message, since this upload failed | ||
log.error(`An error sending attachment message: ${e.message}`, {exception: e}); | ||
throw e; | ||
} | ||
const {id} = await API.sendAttachmentMessage(uploadDirective.uploadId); | ||
return id; | ||
}; | ||
updateTypingIndicator = (text: string, typing: boolean) => { | ||
return API.updateTypingIndicator(text, typing); | ||
}; | ||
sendRegistration = async (fields: {[string]: string}) => { | ||
@@ -356,3 +393,3 @@ storage.setQuiqChatContainerVisible(true); | ||
// Clear message and events caches (tracking ID is different now, so we essentially have a new Conversation) | ||
this.textMessages = []; | ||
this.messages = []; | ||
this.events = []; | ||
@@ -373,6 +410,7 @@ this.userIsRegistered = false; | ||
_handleWebsocketMessage = (message: ChatMessage | BurnItDownMessage) => { | ||
_handleWebsocketMessage = (message: ConversationElement | BurnItDownMessage) => { | ||
if (message.messageType === MessageTypes.CHAT_MESSAGE) { | ||
switch (message.data.type) { | ||
case MessageTypes.TEXT: | ||
case MessageTypes.ATTACHMENT: | ||
this._processNewMessages([message.data]); | ||
@@ -449,8 +487,8 @@ break; | ||
_processNewMessages = ( | ||
newMessages: Array<TextMessage>, | ||
newMessages: Array<ConversationMessage>, | ||
sendNewMessageCallback: boolean = true, | ||
): void => { | ||
const newFilteredMessages: Array<TextMessage> = differenceBy( | ||
const newFilteredMessages: Array<ConversationMessage> = differenceBy( | ||
newMessages, | ||
this.textMessages, | ||
this.messages, | ||
'id', | ||
@@ -461,6 +499,6 @@ ); | ||
if (newFilteredMessages.length > 0) { | ||
this.textMessages = sortByTimestamp(unionBy(this.textMessages, newFilteredMessages, 'id')); | ||
this.messages = sortByTimestamp(unionBy(this.messages, newFilteredMessages, 'id')); | ||
if (this.callbacks.onNewMessages && sendNewMessageCallback) { | ||
this.callbacks.onNewMessages(this.textMessages); | ||
this.callbacks.onNewMessages(this.messages); | ||
} | ||
@@ -467,0 +505,0 @@ } |
@@ -12,13 +12,7 @@ // @flow | ||
export type TextMessageType = 'Text'; | ||
export type AttachmentMessageType = 'Attachment'; | ||
export type WebsocketMessageType = 'ChatMessage' | 'BurnItDown'; | ||
export type UserEventTypes = 'Join' | 'Leave'; | ||
export type MessageStatusType = 'pending' | 'delivered'; | ||
export type TextMessage = { | ||
authorType: AuthorType, | ||
text: string, | ||
id: string, | ||
timestamp: number, | ||
type: TextMessageType, | ||
}; | ||
export type IsomorphicFetchNetworkError = { | ||
@@ -30,8 +24,2 @@ message: string, | ||
export type Event = { | ||
id: string, | ||
timestamp: number, | ||
type: EventType, | ||
}; | ||
export type AgentTypingMessage = { | ||
@@ -55,3 +43,3 @@ type: 'AgentTyping', | ||
export type QuiqChatCallbacks = { | ||
onNewMessages?: (messages: Array<TextMessage>) => void, | ||
onNewMessages?: (messages: Array<ConversationMessage>) => void, | ||
onAgentTyping?: (typing: boolean) => void, | ||
@@ -99,3 +87,3 @@ onError?: (error: ?ApiError) => void, | ||
events: Array<Event>, | ||
messages: Array<TextMessage>, | ||
messages: Array<ConversationMessage>, | ||
isSubscribed: boolean, | ||
@@ -178,8 +166,42 @@ isRegistered: boolean, | ||
export type ChatMessage = { | ||
export type UploadDirective = { | ||
uploadId: string, | ||
directive: { | ||
url: string, | ||
formEntries: Array<{name: string, value: string}>, | ||
}, | ||
}; | ||
export type ConversationElement = { | ||
tenantId: string, | ||
messageType: 'ChatMessage', | ||
data: Event | TextMessage | AgentTypingMessage, | ||
data: Event | ConversationMessage | AgentTypingMessage, | ||
}; | ||
export type ConversationMessage = TextMessage | AttachmentMessage; | ||
export type TextMessage = { | ||
authorType: AuthorType, | ||
text: string, | ||
id: string, | ||
timestamp: number, | ||
type: TextMessageType, | ||
}; | ||
export type AttachmentMessage = { | ||
id: string, | ||
timestamp: number, | ||
type: AttachmentMessageType, | ||
authorType: AuthorType, | ||
url: string, | ||
contentType: string, | ||
status?: MessageStatusType, | ||
}; | ||
export type Event = { | ||
id: string, | ||
timestamp: number, | ||
type: EventType, | ||
}; | ||
export type BurnItDownMessage = { | ||
@@ -208,3 +230,3 @@ tenantId: string, | ||
onConnectionEstablish?: () => ?Promise<void>, | ||
onMessage?: (message: ChatMessage | BurnItDownMessage) => void, | ||
onMessage?: (message: ConversationElement | BurnItDownMessage) => void, | ||
onTransportFailure?: (errorMsg: string, req: AtmosphereRequest) => void, | ||
@@ -211,0 +233,0 @@ onClose?: () => void, |
@@ -49,2 +49,3 @@ // @flow | ||
}; | ||
export const burnItDown = (message?: BurnItDownResponse) => { | ||
@@ -51,0 +52,0 @@ try { |
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 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
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
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
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
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 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
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
1908573
141
11729