@@ -8,8 +8,12 @@ import {
8
8
ChatOrchestrator ,
9
9
ITask ,
10
10
ActionFunction ,
11
+ ReplacedActionFunction ,
12
+ ChatOrchestratorEvent ,
13
+ ChatChannelHelper ,
11
14
} from '@twilio/flex-ui' ;
12
15
import { callTypes } from 'hrm-form-definitions' ;
16
+ import type { ChatOrchestrationsEvents } from '@twilio/flex-ui/src/ChatOrchestrator' ;
13
17
14
18
import { DEFAULT_TRANSFER_MODE , getConfig } from '../HrmFormPlugin' ;
15
19
import {
@@ -50,7 +54,6 @@ export const loadCurrentDefinitionVersion = async () => {
50
54
51
55
/**
52
56
* Given a taskSid, retrieves the state of the form (stored in redux) for that task
53
- * @param {string } taskSid
54
57
*/
55
58
const getStateContactForms = ( taskSid : string ) => {
56
59
return Manager . getInstance ( ) . store . getState ( ) [ namespace ] [ contactFormsBase ] . tasks [ taskSid ] ;
@@ -77,7 +80,6 @@ const fromActionFunction = (fun: ActionFunction) => async (payload: ActionPayloa
77
80
78
81
/**
79
82
* Initializes an empty form (in redux store) for the task within payload
80
- * @param {{ task: any } } payload
81
83
*/
82
84
export const initializeContactForm = ( payload : ActionPayload ) => {
83
85
const { currentDefinitionVersion } = Manager . getInstance ( ) . store . getState ( ) [ namespace ] [ configurationBase ] ;
@@ -115,11 +117,9 @@ const handleTransferredTask = async (task: ITask) => {
115
117
116
118
export const getTaskLanguage = ( { helplineLanguage } ) => ( { task } ) => task . attributes . language || helplineLanguage ;
117
119
118
- /**
119
- * @param {string } messageKey
120
- * @returns {(setupObject: ReturnType<typeof getConfig> & { translateUI: (language: string) => Promise<void>; getMessage: (messageKey: string) => (language: string) => Promise<string>; }) => import('@twilio/flex-ui').ActionFunction }
121
- */
122
- const sendMessageOfKey = messageKey => setupObject => async payload => {
120
+ const sendMessageOfKey = ( messageKey : string ) => ( setupObject : SetupObject ) : ActionFunction => async (
121
+ payload : ActionPayload ,
122
+ ) => {
123
123
const { getMessage } = setupObject ;
124
124
const taskLanguage = getTaskLanguage ( setupObject ) ( payload ) ;
125
125
const message = await getMessage ( messageKey ) ( taskLanguage ) ;
@@ -196,10 +196,8 @@ const safeTransfer = async (transferFunction: () => Promise<any>, task: ITask):
196
196
197
197
/**
198
198
* Custom override for TransferTask action. Saves the form to share with another counseler (if possible) and then starts the transfer
199
- * @param {ReturnType<typeof getConfig> & { translateUI: (language: string) => Promise<void>; getMessage: (messageKey: string) => (language: string) => Promise<string>; } } setupObject
200
- * @returns {import('@twilio/flex-ui').ReplacedActionFunction }
201
199
*/
202
- export const customTransferTask = ( setupObject : SetupObject ) => async (
200
+ export const customTransferTask = ( setupObject : SetupObject ) : ReplacedActionFunction => async (
203
201
payload : ActionPayloadWithOptions ,
204
202
original : ActionFunction ,
205
203
) => {
@@ -249,7 +247,6 @@ export const hangupCall = fromActionFunction(saveEndMillis);
249
247
250
248
/**
251
249
* Override for WrapupTask action. Sends a message before leaving (if it should) and saves the end time of the conversation
252
- * @param {ReturnType<typeof getConfig> & { translateUI: (language: string) => Promise<void>; getMessage: (messageKey: string) => (language: string) => Promise<string>; } } setupObject
253
250
*/
254
251
export const wrapupTask = ( setupObject : SetupObject ) =>
255
252
fromActionFunction ( async payload => {
@@ -259,11 +256,9 @@ export const wrapupTask = (setupObject: SetupObject) =>
259
256
await saveEndMillis ( payload ) ;
260
257
} ) ;
261
258
262
- /**
263
- * @param {ReturnType<typeof getConfig> & { translateUI: (language: string) => Promise<void>; getMessage: (messageKey: string) => (language: string) => Promise<string>; } } setupObject
264
- * @returns {import('@twilio/flex-ui').ActionFunction }
265
- */
266
- const decreaseChatCapacity = ( setupObject : SetupObject ) => async ( payload : ActionPayload ) : Promise < void > => {
259
+ const decreaseChatCapacity = ( setupObject : SetupObject ) : ActionFunction => async (
260
+ payload : ActionPayload ,
261
+ ) : Promise < void > => {
267
262
const { featureFlags } = setupObject ;
268
263
const { task } = payload ;
269
264
if ( featureFlags . enable_manual_pulling && task . taskChannelUniqueName === 'chat' ) await adjustChatCapacity ( 'decrease' ) ;
@@ -277,26 +272,36 @@ const isAseloCustomChannelTask = (task: CustomITask) =>
277
272
( < string [ ] > Object . values ( customChannelTypes ) ) . includes ( task . channelType ) ;
278
273
279
274
/**
280
- * @param {ReturnType<typeof getConfig> & { translateUI: (language: string) => Promise<void>; getMessage: (messageKey: string) => (language: string) => Promise<string>; } } setupObject
275
+ * This function manipulates the default chat orchetrations to allow our implementation of post surveys.
276
+ * Since we rely on the same chat channel as the original contact for it, we don't want it to be "deactivated" by Flex.
277
+ * Hence this function modifies the following orchestration events:
278
+ * - task wrapup: removes DeactivateChatChannel
279
+ * - task completed: removes DeactivateChatChannel
281
280
*/
281
+ const setChatOrchestrationsForPostSurvey = ( ) => {
282
+ const setExcludedDeactivateChatChannel = ( event : keyof ChatOrchestrationsEvents ) => {
283
+ const excludeDeactivateChatChannel = ( orchestrations : ChatOrchestratorEvent [ ] ) =>
284
+ orchestrations . filter ( e => e !== ChatOrchestratorEvent . DeactivateChatChannel ) ;
285
+
286
+ const defaultOrchestrations = ChatOrchestrator . getOrchestrations ( event ) ;
287
+
288
+ if ( Array . isArray ( defaultOrchestrations ) ) {
289
+ ChatOrchestrator . setOrchestrations ( event , task => {
290
+ return isAseloCustomChannelTask ( task )
291
+ ? defaultOrchestrations
292
+ : excludeDeactivateChatChannel ( defaultOrchestrations ) ;
293
+ } ) ;
294
+ }
295
+ } ;
296
+
297
+ setExcludedDeactivateChatChannel ( 'wrapup' ) ;
298
+ setExcludedDeactivateChatChannel ( 'completed' ) ;
299
+ } ;
300
+
282
301
export const setUpPostSurvey = ( setupObject : SetupObject ) => {
283
302
const { featureFlags } = setupObject ;
284
303
if ( featureFlags . enable_post_survey ) {
285
- const maybeExcludeDeactivateChatChannel = event => {
286
- const defaultOrchestrations = ChatOrchestrator . getOrchestrations ( event ) ;
287
- if ( Array . isArray ( defaultOrchestrations ) ) {
288
- const excludeDeactivateChatChannel = defaultOrchestrations . filter ( e => e !== 'DeactivateChatChannel' ) ;
289
-
290
- ChatOrchestrator . setOrchestrations (
291
- event ,
292
- // Instead than setting the orchestrations as a list of actions (ChatOrchestration[]), we can set it to a callback with type (task: ITask) => ChatOrchestration[]
293
- task => ( isAseloCustomChannelTask ( task ) ? defaultOrchestrations : excludeDeactivateChatChannel ) ,
294
- ) ;
295
- }
296
-
297
- maybeExcludeDeactivateChatChannel ( 'wrapup' ) ;
298
- maybeExcludeDeactivateChatChannel ( 'completed' ) ;
299
- } ;
304
+ setChatOrchestrationsForPostSurvey ( ) ;
300
305
}
301
306
} ;
302
307
@@ -326,6 +331,15 @@ export const afterWrapupTask = (setupObject: SetupObject) => async (payload: Act
326
331
const { featureFlags } = setupObject ;
327
332
328
333
if ( featureFlags . enable_post_survey ) {
334
+ if ( TaskHelper . isChatBasedTask ( payload . task ) ) {
335
+ const channelState = StateHelper . getChatChannelStateForTask ( payload . task ) ;
336
+
337
+ channelState . source ?. removeAllListeners ( 'messageAdded' ) ;
338
+ channelState . source ?. removeAllListeners ( 'typingStarted' ) ;
339
+ channelState . source ?. removeAllListeners ( 'typingEnded' ) ;
340
+ }
341
+
342
+ // TODO: make this occur in taskrouter callback
329
343
await triggerPostSurvey ( setupObject , payload ) ;
330
344
}
331
345
} ;
0 commit comments