botframework-webchat-core 4.14.1 → 4.15.2-main.20220413.af6e8a3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.yml +4 -198
- package/.prettierrc.yml +1 -1
- package/babel.config.json +1 -1
- package/lib/actions/clearSuggestedActions.js +1 -1
- package/lib/actions/connect.js +1 -1
- package/lib/actions/connectionStatusUpdate.js +1 -1
- package/lib/actions/deleteActivity.d.ts +13 -0
- package/lib/actions/deleteActivity.d.ts.map +1 -0
- package/lib/actions/deleteActivity.js +5 -3
- package/lib/actions/disconnect.js +1 -1
- package/lib/actions/dismissNotification.js +1 -1
- package/lib/actions/emitTypingIndicator.js +1 -1
- package/lib/actions/incomingActivity.d.ts +14 -0
- package/lib/actions/incomingActivity.d.ts.map +1 -0
- package/lib/actions/incomingActivity.js +5 -3
- package/lib/actions/markActivity.d.ts +17 -0
- package/lib/actions/markActivity.d.ts.map +1 -0
- package/lib/actions/markActivity.js +5 -3
- package/lib/actions/postActivity.d.ts +52 -0
- package/lib/actions/postActivity.d.ts.map +1 -0
- package/lib/actions/postActivity.js +5 -3
- package/lib/actions/queueIncomingActivity.js +1 -1
- package/lib/actions/sagaError.js +1 -1
- package/lib/actions/sendEvent.js +1 -1
- package/lib/actions/sendFiles.js +1 -1
- package/lib/actions/sendMessage.js +1 -1
- package/lib/actions/sendMessageBack.js +1 -1
- package/lib/actions/sendPostBack.js +1 -1
- package/lib/actions/setDictateInterims.js +1 -1
- package/lib/actions/setDictateState.js +1 -1
- package/lib/actions/setLanguage.js +1 -1
- package/lib/actions/setNotification.js +2 -2
- package/lib/actions/setReferenceGrammarID.js +1 -1
- package/lib/actions/setSendBox.js +1 -1
- package/lib/actions/setSendTimeout.js +1 -1
- package/lib/actions/setSendTypingIndicator.js +1 -1
- package/lib/actions/setSuggestedActions.js +1 -1
- package/lib/actions/startDictate.js +1 -1
- package/lib/actions/startSpeakingActivity.js +1 -1
- package/lib/actions/stopDictate.js +1 -1
- package/lib/actions/stopSpeakingActivity.js +1 -1
- package/lib/actions/submitSendBox.js +1 -1
- package/lib/actions/updateConnectionStatus.js +1 -1
- package/lib/constants/ActivityClientState.js +1 -1
- package/lib/createStore.d.ts.map +1 -1
- package/lib/createStore.js +5 -5
- package/lib/definitions/speakingActivity.js +4 -2
- package/lib/index.d.ts +17 -15
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +12 -3
- package/lib/reducer.d.ts +1 -2
- package/lib/reducer.d.ts.map +1 -1
- package/lib/reducer.js +1 -4
- package/lib/reducers/activities.d.ts +10 -0
- package/lib/reducers/activities.d.ts.map +1 -0
- package/lib/reducers/activities.js +65 -45
- package/lib/reducers/notifications.js +21 -14
- package/lib/reducers/typing.js +13 -7
- package/lib/sagas/connectionStatusToNotificationSaga.js +1 -1
- package/lib/sagas/effects/observeOnce.js +1 -1
- package/lib/sagas/effects/whileConnected.js +1 -1
- package/lib/sagas/observeActivitySaga.js +21 -3
- package/lib/sagas/postActivitySaga.js +51 -72
- package/lib/sagas/queueIncomingActivitySaga.js +1 -1
- package/lib/sagas/sendFilesToPostActivitySaga.js +4 -4
- package/lib/sagas/sendMessageBackToPostActivitySaga.js +1 -1
- package/lib/sagas/sendTypingIndicatorOnSetSendBoxSaga.js +1 -1
- package/lib/sagas/speakActivityAndStartDictateOnIncomingActivityFromOthersSaga.js +1 -1
- package/lib/sagas/startSpeakActivityOnPostActivitySaga.js +1 -1
- package/lib/sagas/stopSpeakingActivityOnInputSaga.js +1 -1
- package/lib/sagas/submitSendBoxSaga.js +1 -1
- package/lib/selectors/activities.js +1 -1
- package/lib/selectors/combineSelectors.js +9 -4
- package/lib/selectors/dictateState.js +1 -1
- package/lib/selectors/language.js +1 -1
- package/lib/selectors/notifications.js +1 -1
- package/lib/selectors/sendBoxValue.js +1 -1
- package/lib/selectors/sendTimeout.js +1 -1
- package/lib/selectors/sendTypingIndicator.js +1 -1
- package/lib/selectors/shouldSpeakIncomingActivity.js +1 -1
- package/lib/types/AnyAnd.d.ts +2 -0
- package/lib/types/AnyAnd.d.ts.map +1 -0
- package/lib/types/AnyAnd.js +6 -0
- package/lib/types/OneOrMany.js +4 -0
- package/lib/types/WebChatActivity.d.ts +81 -0
- package/lib/types/WebChatActivity.d.ts.map +1 -0
- package/lib/types/WebChatActivity.js +6 -0
- package/lib/types/external/DirectLineActivity.d.ts +2 -2
- package/lib/types/external/DirectLineActivity.d.ts.map +1 -1
- package/lib/types/external/DirectLineActivity.js +4 -0
- package/lib/types/external/DirectLineAnimationCard.d.ts +5 -2
- package/lib/types/external/DirectLineAnimationCard.d.ts.map +1 -1
- package/lib/types/external/DirectLineAnimationCard.js +4 -0
- package/lib/types/external/DirectLineAttachment.d.ts +8 -2
- package/lib/types/external/DirectLineAttachment.d.ts.map +1 -1
- package/lib/types/external/DirectLineAttachment.js +4 -0
- package/lib/types/external/DirectLineAudioCard.d.ts +5 -2
- package/lib/types/external/DirectLineAudioCard.d.ts.map +1 -1
- package/lib/types/external/DirectLineAudioCard.js +4 -0
- package/lib/types/external/DirectLineBasicCardEssence.d.ts +12 -0
- package/lib/types/external/DirectLineBasicCardEssence.d.ts.map +1 -0
- package/lib/types/external/DirectLineBasicCardEssence.js +6 -0
- package/lib/types/external/DirectLineCardAction.d.ts +1 -1
- package/lib/types/external/DirectLineCardAction.d.ts.map +1 -1
- package/lib/types/external/DirectLineCardAction.js +4 -0
- package/lib/types/external/DirectLineCardImage.d.ts +8 -0
- package/lib/types/external/DirectLineCardImage.d.ts.map +1 -0
- package/lib/types/external/DirectLineCardImage.js +6 -0
- package/lib/types/external/DirectLineHeroCard.d.ts +5 -2
- package/lib/types/external/DirectLineHeroCard.d.ts.map +1 -1
- package/lib/types/external/DirectLineHeroCard.js +4 -0
- package/lib/types/external/DirectLineJSBotConnection.d.ts +1 -1
- package/lib/types/external/DirectLineJSBotConnection.d.ts.map +1 -1
- package/lib/types/external/DirectLineJSBotConnection.js +4 -0
- package/lib/types/external/DirectLineMediaCardEssence.d.ts +21 -0
- package/lib/types/external/DirectLineMediaCardEssence.d.ts.map +1 -0
- package/lib/types/external/DirectLineMediaCardEssence.js +6 -0
- package/lib/types/external/DirectLineOAuthCard.d.ts +7 -2
- package/lib/types/external/DirectLineOAuthCard.d.ts.map +1 -1
- package/lib/types/external/DirectLineOAuthCard.js +4 -0
- package/lib/types/external/DirectLineReceiptCard.d.ts +27 -2
- package/lib/types/external/DirectLineReceiptCard.d.ts.map +1 -1
- package/lib/types/external/DirectLineReceiptCard.js +4 -0
- package/lib/types/external/DirectLineSignInCard.d.ts +7 -2
- package/lib/types/external/DirectLineSignInCard.d.ts.map +1 -1
- package/lib/types/external/DirectLineSignInCard.js +4 -0
- package/lib/types/external/DirectLineSuggestedAction.d.ts +6 -2
- package/lib/types/external/DirectLineSuggestedAction.d.ts.map +1 -1
- package/lib/types/external/DirectLineSuggestedAction.js +4 -0
- package/lib/types/external/DirectLineThumbnailCard.d.ts +5 -2
- package/lib/types/external/DirectLineThumbnailCard.d.ts.map +1 -1
- package/lib/types/external/DirectLineThumbnailCard.js +4 -0
- package/lib/types/external/DirectLineVideoCard.d.ts +5 -2
- package/lib/types/external/DirectLineVideoCard.d.ts.map +1 -1
- package/lib/types/external/DirectLineVideoCard.js +4 -0
- package/lib/types/external/Observable.js +6 -0
- package/lib/types/internal/Notification.js +6 -0
- package/lib/types/internal/ReduxState.js +6 -0
- package/lib/utils/dateToLocaleISOString.js +5 -5
- package/lib/utils/deleteKey.js +2 -3
- package/lib/utils/isForbiddenPropertyName.d.ts +2 -0
- package/lib/utils/isForbiddenPropertyName.d.ts.map +1 -0
- package/lib/utils/isForbiddenPropertyName.js +21 -0
- package/lib/utils/sleep.js +1 -1
- package/lib/utils/uniqueID.js +1 -1
- package/package.json +20 -23
- package/src/__tests__/detectSlowConnectionSaga.spec.js +1 -1
- package/src/__tests__/observeOnce.spec.js +3 -3
- package/src/actions/deleteActivity.ts +19 -0
- package/src/actions/incomingActivity.ts +21 -0
- package/src/actions/markActivity.ts +23 -0
- package/src/actions/postActivity.ts +48 -0
- package/src/actions/setNotification.js +8 -2
- package/src/createStore.ts +4 -4
- package/src/definitions/speakingActivity.js +1 -1
- package/src/index.ts +19 -14
- package/src/reducer.ts +0 -2
- package/src/reducers/activities.ts +172 -0
- package/src/reducers/notifications.js +22 -16
- package/src/reducers/typing.js +6 -5
- package/src/sagas/connectionStatusToNotificationSaga.js +1 -1
- package/src/sagas/effects/{observeOnce.js → observeOnce.ts} +6 -4
- package/src/sagas/effects/{whileConnected.js → whileConnected.ts} +20 -1
- package/src/sagas/{observeActivitySaga.js → observeActivitySaga.ts} +25 -6
- package/src/sagas/{postActivitySaga.js → postActivitySaga.ts} +57 -48
- package/src/sagas/queueIncomingActivitySaga.js +40 -39
- package/src/sagas/sendFilesToPostActivitySaga.js +1 -1
- package/src/sagas/sendMessageBackToPostActivitySaga.js +0 -1
- package/src/sagas/sendTypingIndicatorOnSetSendBoxSaga.js +1 -1
- package/src/sagas/speakActivityAndStartDictateOnIncomingActivityFromOthersSaga.js +1 -1
- package/src/sagas/startSpeakActivityOnPostActivitySaga.js +1 -1
- package/src/sagas/stopSpeakingActivityOnInputSaga.js +1 -1
- package/src/sagas/submitSendBoxSaga.js +1 -1
- package/src/selectors/activities.ts +12 -0
- package/src/selectors/combineSelectors.ts +21 -0
- package/src/selectors/dictateState.ts +3 -0
- package/src/selectors/language.ts +3 -0
- package/src/selectors/notifications.ts +6 -0
- package/src/selectors/sendBoxValue.ts +3 -0
- package/src/selectors/sendTimeout.ts +3 -0
- package/src/selectors/sendTypingIndicator.ts +3 -0
- package/src/selectors/shouldSpeakIncomingActivity.ts +3 -0
- package/src/types/AnyAnd.ts +1 -0
- package/src/types/WebChatActivity.ts +154 -0
- package/src/types/external/DirectLineActivity.ts +4 -3
- package/src/types/external/DirectLineAnimationCard.ts +6 -4
- package/src/types/external/DirectLineAttachment.ts +10 -5
- package/src/types/external/DirectLineAudioCard.ts +6 -4
- package/src/types/external/DirectLineBasicCardEssence.ts +14 -0
- package/src/types/external/DirectLineCardAction.ts +1 -1
- package/src/types/external/DirectLineCardImage.ts +9 -0
- package/src/types/external/DirectLineHeroCard.ts +6 -4
- package/src/types/external/DirectLineJSBotConnection.ts +1 -2
- package/src/types/external/DirectLineMediaCardEssence.ts +19 -0
- package/src/types/external/DirectLineOAuthCard.ts +7 -4
- package/src/types/external/DirectLineReceiptCard.ts +30 -4
- package/src/types/external/DirectLineSignInCard.ts +8 -4
- package/src/types/external/DirectLineSuggestedAction.ts +6 -4
- package/src/types/external/DirectLineThumbnailCard.ts +6 -4
- package/src/types/external/DirectLineVideoCard.ts +6 -4
- package/src/types/external/Observable.ts +69 -0
- package/src/types/internal/Notification.ts +10 -0
- package/src/types/internal/ReduxState.ts +16 -0
- package/src/utils/dateToLocaleISOString.chatham.spec.js +1 -0
- package/src/utils/dateToLocaleISOString.japan.spec.js +1 -0
- package/src/utils/dateToLocaleISOString.pacific.spec.js +1 -0
- package/src/utils/{dateToLocaleISOString.js → dateToLocaleISOString.ts} +6 -6
- package/src/utils/dateToLocaleISOString.utc.spec.js +2 -0
- package/src/utils/deleteKey.ts +9 -0
- package/src/utils/isForbiddenPropertyName.spec.js +6 -0
- package/src/utils/isForbiddenPropertyName.ts +33 -0
- package/src/utils/{sleep.js → sleep.ts} +1 -1
- package/src/utils/uniqueID.ts +7 -0
- package/.eslintignore +0 -9
- package/lib/reducers/clockSkewAdjustment.js +0 -44
- package/lib/sagas/effects/callUntil.js +0 -48
- package/lib/selectors/clockSkewAdjustment.js +0 -14
- package/lib/utils/mime-wrapper.js +0 -49
- package/src/actions/deleteActivity.js +0 -8
- package/src/actions/incomingActivity.js +0 -10
- package/src/actions/markActivity.js +0 -14
- package/src/actions/postActivity.js +0 -14
- package/src/reducers/activities.js +0 -116
- package/src/reducers/clockSkewAdjustment.js +0 -29
- package/src/sagas/effects/callUntil.js +0 -13
- package/src/selectors/activities.js +0 -8
- package/src/selectors/clockSkewAdjustment.js +0 -1
- package/src/selectors/combineSelectors.js +0 -8
- package/src/selectors/dictateState.js +0 -1
- package/src/selectors/language.js +0 -1
- package/src/selectors/notifications.js +0 -3
- package/src/selectors/sendBoxValue.js +0 -1
- package/src/selectors/sendTimeout.js +0 -1
- package/src/selectors/sendTypingIndicator.js +0 -1
- package/src/selectors/shouldSpeakIncomingActivity.js +0 -1
- package/src/utils/deleteKey.js +0 -11
- package/src/utils/mime-wrapper.js +0 -40
- package/src/utils/uniqueID.js +0 -12
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/* eslint no-magic-numbers: ["error", { "ignore": [0, 1, -1] }] */
|
|
2
|
+
|
|
3
|
+
import updateIn from 'simple-update-in';
|
|
4
|
+
|
|
5
|
+
import { DELETE_ACTIVITY } from '../actions/deleteActivity';
|
|
6
|
+
import { INCOMING_ACTIVITY } from '../actions/incomingActivity';
|
|
7
|
+
import { MARK_ACTIVITY } from '../actions/markActivity';
|
|
8
|
+
import { POST_ACTIVITY_FULFILLED, POST_ACTIVITY_PENDING, POST_ACTIVITY_REJECTED } from '../actions/postActivity';
|
|
9
|
+
import { SEND_FAILED, SENDING, SENT } from '../constants/ActivityClientState';
|
|
10
|
+
import type { DeleteActivityAction } from '../actions/deleteActivity';
|
|
11
|
+
import type { IncomingActivityAction } from '../actions/incomingActivity';
|
|
12
|
+
import type { MarkActivityAction } from '../actions/markActivity';
|
|
13
|
+
import type {
|
|
14
|
+
PostActivityFulfilledAction,
|
|
15
|
+
PostActivityPendingAction,
|
|
16
|
+
PostActivityRejectedAction
|
|
17
|
+
} from '../actions/postActivity';
|
|
18
|
+
import type { WebChatActivity } from '../types/WebChatActivity';
|
|
19
|
+
|
|
20
|
+
type ActivitiesAction =
|
|
21
|
+
| DeleteActivityAction
|
|
22
|
+
| IncomingActivityAction
|
|
23
|
+
| MarkActivityAction
|
|
24
|
+
| PostActivityFulfilledAction
|
|
25
|
+
| PostActivityPendingAction
|
|
26
|
+
| PostActivityRejectedAction;
|
|
27
|
+
|
|
28
|
+
type ActivitiesStateType = WebChatActivity[];
|
|
29
|
+
|
|
30
|
+
const DEFAULT_STATE: ActivitiesStateType = [];
|
|
31
|
+
const DIRECT_LINE_PLACEHOLDER_URL =
|
|
32
|
+
'https://docs.botframework.com/static/devportal/client/images/bot-framework-default-placeholder.png';
|
|
33
|
+
|
|
34
|
+
function getClientActivityID(activity: WebChatActivity): string | undefined {
|
|
35
|
+
return activity.channelData?.clientActivityID;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function findByClientActivityID(clientActivityID: string): (activity: WebChatActivity) => boolean {
|
|
39
|
+
return (activity: WebChatActivity) => getClientActivityID(activity) === clientActivityID;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function patchActivity(activity: WebChatActivity, lastActivity: WebChatActivity): WebChatActivity {
|
|
43
|
+
// Direct Line channel will return a placeholder image for the user-uploaded image.
|
|
44
|
+
// As observed, the URL for the placeholder image is https://docs.botframework.com/static/devportal/client/images/bot-framework-default-placeholder.png.
|
|
45
|
+
// To make our code simpler, we are removing the value if "contentUrl" is pointing to a placeholder image.
|
|
46
|
+
|
|
47
|
+
// TODO: [P2] #2869 This "contentURL" removal code should be moved to DirectLineJS adapter.
|
|
48
|
+
|
|
49
|
+
// Also, if the "contentURL" starts with "blob:", this means the user is uploading a file (the URL is constructed by URL.createObjectURL)
|
|
50
|
+
// Although the copy/reference of the file is temporary in-memory, to make the UX consistent across page refresh, we do not allow the user to re-download the file either.
|
|
51
|
+
|
|
52
|
+
activity = updateIn(activity, ['attachments', () => true, 'contentUrl'], (contentUrl: string) => {
|
|
53
|
+
if (contentUrl !== DIRECT_LINE_PLACEHOLDER_URL && !/^blob:/iu.test(contentUrl)) {
|
|
54
|
+
return contentUrl;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// If the message does not have sequence ID, use these fallback values:
|
|
59
|
+
// 1. "timestamp" field
|
|
60
|
+
// - outgoing activity will not have "timestamp" field
|
|
61
|
+
// 2. last activity sequence ID (or 0) + 0.001
|
|
62
|
+
// - best effort to put this message the last one in the chat history
|
|
63
|
+
activity = updateIn(activity, ['channelData', 'webchat:sequence-id'], (sequenceId?: number) =>
|
|
64
|
+
typeof sequenceId === 'number'
|
|
65
|
+
? sequenceId
|
|
66
|
+
: typeof activity.timestamp !== 'undefined'
|
|
67
|
+
? +new Date(activity.timestamp)
|
|
68
|
+
: // We assume there will be no more than 1,000 messages sent before receiving server response.
|
|
69
|
+
// If there are more than 1,000 messages, some messages will get reordered and appear jumpy after receiving server response.
|
|
70
|
+
// eslint-disable-next-line no-magic-numbers
|
|
71
|
+
(lastActivity?.channelData?.['webchat:sequence-id'] || 0) + 0.001
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// TODO: [P1] #3953 We should move this patching logic to a DLJS wrapper for simplicity.
|
|
75
|
+
activity = updateIn(activity, ['channelData', 'webchat:sequence-id'], (sequenceId: number) =>
|
|
76
|
+
typeof sequenceId === 'number' ? sequenceId : +new Date(activity.timestamp || 0) || 0
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return activity;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function upsertActivityWithSort(activities: WebChatActivity[], nextActivity: WebChatActivity): WebChatActivity[] {
|
|
83
|
+
nextActivity = patchActivity(nextActivity, activities[activities.length - 1]);
|
|
84
|
+
|
|
85
|
+
const { channelData: { clientActivityID: nextClientActivityID, 'webchat:sequence-id': nextSequenceId } = {} } =
|
|
86
|
+
nextActivity;
|
|
87
|
+
|
|
88
|
+
const nextActivities = activities.filter(
|
|
89
|
+
({ channelData: { clientActivityID } = {}, id }) =>
|
|
90
|
+
// We will remove all "sending messages" activities and activities with same ID
|
|
91
|
+
// "clientActivityID" is unique and used to track if the message has been sent and echoed back from the server
|
|
92
|
+
!(nextClientActivityID && clientActivityID === nextClientActivityID) && !(id && id === nextActivity.id)
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Then, find the right (sorted) place to insert the new activity at, based on timestamp
|
|
96
|
+
// Since clockskew might happen, we will ignore timestamp on messages that are sending
|
|
97
|
+
|
|
98
|
+
const indexToInsert = nextActivities.findIndex(
|
|
99
|
+
({ channelData: { state, 'webchat:sequence-id': sequenceId } = {} }) =>
|
|
100
|
+
(sequenceId || 0) > (nextSequenceId || 0) && state !== SENDING && state !== SEND_FAILED
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// If no right place are found, append it
|
|
104
|
+
nextActivities.splice(~indexToInsert ? indexToInsert : nextActivities.length, 0, nextActivity);
|
|
105
|
+
|
|
106
|
+
return nextActivities;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default function activities(
|
|
110
|
+
state: ActivitiesStateType = DEFAULT_STATE,
|
|
111
|
+
action: ActivitiesAction
|
|
112
|
+
): ActivitiesStateType {
|
|
113
|
+
switch (action.type) {
|
|
114
|
+
case DELETE_ACTIVITY:
|
|
115
|
+
state = updateIn(state, [({ id }: WebChatActivity) => id === action.payload.activityID]);
|
|
116
|
+
break;
|
|
117
|
+
|
|
118
|
+
case MARK_ACTIVITY:
|
|
119
|
+
{
|
|
120
|
+
const { payload } = action;
|
|
121
|
+
|
|
122
|
+
state = updateIn(
|
|
123
|
+
state,
|
|
124
|
+
[({ id }: WebChatActivity) => id === payload.activityID, 'channelData', payload.name],
|
|
125
|
+
() => payload.value
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
break;
|
|
130
|
+
|
|
131
|
+
case POST_ACTIVITY_PENDING:
|
|
132
|
+
{
|
|
133
|
+
let {
|
|
134
|
+
payload: { activity }
|
|
135
|
+
} = action;
|
|
136
|
+
|
|
137
|
+
activity = updateIn(activity, ['channelData', 'state'], () => SENDING);
|
|
138
|
+
|
|
139
|
+
state = upsertActivityWithSort(state, activity);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
break;
|
|
143
|
+
|
|
144
|
+
case POST_ACTIVITY_REJECTED:
|
|
145
|
+
state = updateIn(
|
|
146
|
+
state,
|
|
147
|
+
[findByClientActivityID(action.meta.clientActivityID), 'channelData', 'state'],
|
|
148
|
+
() => SEND_FAILED
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
case POST_ACTIVITY_FULFILLED:
|
|
154
|
+
state = updateIn(state, [findByClientActivityID(action.meta.clientActivityID)], () =>
|
|
155
|
+
// We will replace the activity with the version from the server
|
|
156
|
+
updateIn(patchActivity(action.payload.activity, state[state.length - 1]), ['channelData', 'state'], () => SENT)
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
break;
|
|
160
|
+
|
|
161
|
+
case INCOMING_ACTIVITY:
|
|
162
|
+
// TODO: [P4] #2100 Move "typing" into Constants.ActivityType
|
|
163
|
+
state = upsertActivityWithSort(state, action.payload.activity);
|
|
164
|
+
|
|
165
|
+
break;
|
|
166
|
+
|
|
167
|
+
default:
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return state;
|
|
172
|
+
}
|
|
@@ -3,6 +3,7 @@ import updateIn from 'simple-update-in';
|
|
|
3
3
|
import { DISMISS_NOTIFICATION } from '../actions/dismissNotification';
|
|
4
4
|
import { SAGA_ERROR } from '../actions/sagaError';
|
|
5
5
|
import { SET_NOTIFICATION } from '../actions/setNotification';
|
|
6
|
+
import isForbiddenPropertyName from '../utils/isForbiddenPropertyName';
|
|
6
7
|
|
|
7
8
|
const DEFAULT_STATE = {};
|
|
8
9
|
|
|
@@ -15,23 +16,28 @@ export default function notifications(state = DEFAULT_STATE, { payload, type })
|
|
|
15
16
|
state = updateIn(state, ['connectivitystatus', 'message'], () => 'javascripterror');
|
|
16
17
|
} else if (type === SET_NOTIFICATION) {
|
|
17
18
|
const { alt, data, id, level, message } = payload;
|
|
18
|
-
const notification = state[id];
|
|
19
19
|
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
20
|
+
if (!isForbiddenPropertyName(id)) {
|
|
21
|
+
// Mitigated through denylisting.
|
|
22
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
23
|
+
const notification = state[id];
|
|
24
|
+
|
|
25
|
+
if (
|
|
26
|
+
!notification ||
|
|
27
|
+
alt !== notification.alt ||
|
|
28
|
+
!Object.is(data, notification.data) ||
|
|
29
|
+
level !== notification.level ||
|
|
30
|
+
message !== notification.message
|
|
31
|
+
) {
|
|
32
|
+
state = updateIn(state, [id], () => ({
|
|
33
|
+
alt,
|
|
34
|
+
data,
|
|
35
|
+
id,
|
|
36
|
+
level,
|
|
37
|
+
message,
|
|
38
|
+
timestamp: now
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
35
41
|
}
|
|
36
42
|
}
|
|
37
43
|
|
package/src/reducers/typing.js
CHANGED
|
@@ -18,11 +18,12 @@ export default function lastTyping(state = DEFAULT_STATE, { payload, type }) {
|
|
|
18
18
|
} = payload;
|
|
19
19
|
|
|
20
20
|
if (activityType === 'typing') {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
|
|
23
|
+
state = updateIn(state, [id, 'at'], at => at || now);
|
|
24
|
+
state = updateIn(state, [id, 'last'], () => now);
|
|
25
|
+
state = updateIn(state, [id, 'name'], () => name);
|
|
26
|
+
state = updateIn(state, [id, 'role'], () => role);
|
|
26
27
|
} else if (activityType === 'message') {
|
|
27
28
|
state = updateIn(state, [id]);
|
|
28
29
|
}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { call } from 'redux-saga/effects';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import { Observable, Observer, Subscription } from '../../types/external/Observable';
|
|
4
|
+
|
|
5
|
+
export default function observeOnceEffect<T>(observable: Observable<T>) {
|
|
4
6
|
return call(function* observeOnce() {
|
|
5
|
-
let subscription;
|
|
7
|
+
let subscription: Subscription;
|
|
6
8
|
|
|
7
9
|
try {
|
|
8
10
|
return yield call(
|
|
9
11
|
() =>
|
|
10
|
-
new Promise((resolve, reject) => {
|
|
12
|
+
new Promise<T>((resolve, reject) => {
|
|
11
13
|
subscription = observable.subscribe({
|
|
12
14
|
complete: resolve,
|
|
13
15
|
error: reject,
|
|
14
16
|
next: resolve
|
|
15
|
-
});
|
|
17
|
+
} as Observer<T>);
|
|
16
18
|
})
|
|
17
19
|
);
|
|
18
20
|
} finally {
|
|
@@ -4,13 +4,32 @@ import { CONNECT_FULFILLING } from '../../actions/connect';
|
|
|
4
4
|
import { DISCONNECT_PENDING } from '../../actions/disconnect';
|
|
5
5
|
import { RECONNECT_PENDING, RECONNECT_FULFILLING } from '../../actions/reconnect';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import type { DirectLineJSBotConnection } from '../../types/external/DirectLineJSBotConnection';
|
|
8
|
+
|
|
9
|
+
export default function whileConnectedEffect(
|
|
10
|
+
fn: ({
|
|
11
|
+
directLine,
|
|
12
|
+
userID,
|
|
13
|
+
username
|
|
14
|
+
}: {
|
|
15
|
+
directLine: DirectLineJSBotConnection;
|
|
16
|
+
userID: string;
|
|
17
|
+
username: string;
|
|
18
|
+
}) => void
|
|
19
|
+
) {
|
|
8
20
|
return call(function* whileConnected() {
|
|
9
21
|
for (;;) {
|
|
10
22
|
const {
|
|
11
23
|
meta: { userID, username },
|
|
12
24
|
payload: { directLine }
|
|
25
|
+
}: {
|
|
26
|
+
meta: {
|
|
27
|
+
userID: string;
|
|
28
|
+
username: string;
|
|
29
|
+
};
|
|
30
|
+
payload: { directLine: DirectLineJSBotConnection };
|
|
13
31
|
} = yield take([CONNECT_FULFILLING, RECONNECT_FULFILLING]);
|
|
32
|
+
|
|
14
33
|
const task = yield fork(fn, { directLine, userID, username });
|
|
15
34
|
|
|
16
35
|
// When we receive DISCONNECT_PENDING or RECONNECT_PENDING, the Direct Line connection is currently busy and should not be used.
|
|
@@ -4,10 +4,13 @@ import updateIn from 'simple-update-in';
|
|
|
4
4
|
import observeEach from './effects/observeEach';
|
|
5
5
|
import queueIncomingActivity from '../actions/queueIncomingActivity';
|
|
6
6
|
import whileConnected from './effects/whileConnected';
|
|
7
|
+
import type { DirectLineActivity } from '../types/external/DirectLineActivity';
|
|
8
|
+
import type { DirectLineJSBotConnection } from '../types/external/DirectLineJSBotConnection';
|
|
9
|
+
import type { WebChatActivity } from '../types/WebChatActivity';
|
|
7
10
|
|
|
8
|
-
const PASSTHRU_FN = value => value;
|
|
11
|
+
const PASSTHRU_FN = (value: unknown) => value;
|
|
9
12
|
|
|
10
|
-
function patchActivityWithFromRole(activity, userID) {
|
|
13
|
+
function patchActivityWithFromRole(activity: DirectLineActivity, userID?: string): DirectLineActivity {
|
|
11
14
|
// Some activities, such as "ConversationUpdate", does not have "from" defined.
|
|
12
15
|
// And although "role" is defined in Direct Line spec, it was not sent over the wire.
|
|
13
16
|
// We normalize the activity here to simplify null-check and logic later.
|
|
@@ -28,7 +31,7 @@ function patchActivityWithFromRole(activity, userID) {
|
|
|
28
31
|
return activity;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
function patchNullAsUndefined(activity) {
|
|
34
|
+
function patchNullAsUndefined(activity: DirectLineActivity): DirectLineActivity {
|
|
32
35
|
// These fields are known used in Web Chat and in any cases, they should not be null, but undefined.
|
|
33
36
|
// The only field omitted is "value", as it could be null purposefully.
|
|
34
37
|
|
|
@@ -56,12 +59,28 @@ function patchNullAsUndefined(activity) {
|
|
|
56
59
|
}, activity);
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
// Patching the `from.name` to be a human readable name.
|
|
63
|
+
// We use the `from.name` for typing indicator, such that it read "John is typing...".
|
|
64
|
+
function patchFromName(activity: DirectLineActivity) {
|
|
65
|
+
return updateIn(activity, ['from', 'name'], (name: string | undefined): string => {
|
|
66
|
+
const { channelId, from = {} } = activity;
|
|
67
|
+
|
|
68
|
+
if ((channelId === 'directline' || channelId === 'webchat') && from.id === from.name && from.role === 'bot') {
|
|
69
|
+
return 'Bot';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return name;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function* observeActivity({ directLine, userID }: { directLine: DirectLineJSBotConnection; userID?: string }) {
|
|
77
|
+
yield observeEach(directLine.activity$, function* observeActivity(activity: DirectLineActivity) {
|
|
78
|
+
// TODO: [P2] #3953 Move the patching logic to a DirectLineJS wrapper, instead of too close to inners of Web Chat.
|
|
61
79
|
activity = patchNullAsUndefined(activity);
|
|
62
80
|
activity = patchActivityWithFromRole(activity, userID);
|
|
81
|
+
activity = patchFromName(activity);
|
|
63
82
|
|
|
64
|
-
yield put(queueIncomingActivity(activity));
|
|
83
|
+
yield put(queueIncomingActivity(activity as WebChatActivity));
|
|
65
84
|
});
|
|
66
85
|
}
|
|
67
86
|
|
|
@@ -1,41 +1,44 @@
|
|
|
1
1
|
import { all, call, cancelled, put, race, select, take, takeEvery } from 'redux-saga/effects';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import whileConnected from './effects/whileConnected';
|
|
5
|
-
|
|
6
|
-
import clockSkewAdjustmentSelector from '../selectors/clockSkewAdjustment';
|
|
7
|
-
import combineSelectors from '../selectors/combineSelectors';
|
|
8
|
-
import dateToLocaleISOString from '../utils/dateToLocaleISOString';
|
|
9
|
-
import languageSelector from '../selectors/language';
|
|
10
|
-
import sendTimeoutSelector from '../selectors/sendTimeout';
|
|
11
|
-
|
|
12
|
-
import deleteKey from '../utils/deleteKey';
|
|
13
|
-
import sleep from '../utils/sleep';
|
|
14
|
-
import uniqueID from '../utils/uniqueID';
|
|
15
|
-
|
|
3
|
+
import { INCOMING_ACTIVITY } from '../actions/incomingActivity';
|
|
16
4
|
import {
|
|
17
5
|
POST_ACTIVITY,
|
|
18
6
|
POST_ACTIVITY_FULFILLED,
|
|
19
7
|
POST_ACTIVITY_PENDING,
|
|
20
8
|
POST_ACTIVITY_REJECTED
|
|
21
9
|
} from '../actions/postActivity';
|
|
22
|
-
|
|
23
|
-
import
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
10
|
+
import dateToLocaleISOString from '../utils/dateToLocaleISOString';
|
|
11
|
+
import deleteKey from '../utils/deleteKey';
|
|
12
|
+
import languageSelector from '../selectors/language';
|
|
13
|
+
import observeOnce from './effects/observeOnce';
|
|
14
|
+
import sendTimeoutSelector from '../selectors/sendTimeout';
|
|
15
|
+
import sleep from '../utils/sleep';
|
|
16
|
+
import uniqueID from '../utils/uniqueID';
|
|
17
|
+
import whileConnected from './effects/whileConnected';
|
|
18
|
+
import type { DirectLineActivity } from '../types/external/DirectLineActivity';
|
|
19
|
+
import type { DirectLineJSBotConnection } from '../types/external/DirectLineJSBotConnection';
|
|
20
|
+
import type { IncomingActivityAction } from '../actions/incomingActivity';
|
|
21
|
+
import type {
|
|
22
|
+
PostActivityAction,
|
|
23
|
+
PostActivityFulfilledAction,
|
|
24
|
+
PostActivityPendingAction,
|
|
25
|
+
PostActivityRejectedAction
|
|
26
|
+
} from '../actions/postActivity';
|
|
27
|
+
import type { WebChatActivity } from '../types/WebChatActivity';
|
|
28
|
+
|
|
29
|
+
function* postActivity(
|
|
30
|
+
directLine: DirectLineJSBotConnection,
|
|
31
|
+
userID: string,
|
|
32
|
+
username: string,
|
|
33
|
+
numActivitiesPosted: number,
|
|
34
|
+
{ meta: { method }, payload: { activity } }: PostActivityAction
|
|
35
|
+
) {
|
|
36
|
+
const attachments = (activity.type === 'message' && activity.attachments) || [];
|
|
35
37
|
const clientActivityID = uniqueID();
|
|
36
|
-
const
|
|
38
|
+
const locale = yield select(languageSelector);
|
|
37
39
|
const localTimeZone =
|
|
38
40
|
typeof window.Intl === 'undefined' ? undefined : new Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
41
|
+
const now = new Date();
|
|
39
42
|
|
|
40
43
|
activity = {
|
|
41
44
|
...deleteKey(activity, 'id'),
|
|
@@ -49,9 +52,7 @@ function* postActivity(directLine, userID, username, numActivitiesPosted, { meta
|
|
|
49
52
|
})),
|
|
50
53
|
channelData: {
|
|
51
54
|
...deleteKey(activity.channelData, 'state'),
|
|
52
|
-
clientActivityID
|
|
53
|
-
// This is unskewed local timestamp for estimating clock skew.
|
|
54
|
-
clientTimestamp: getTimestamp(now)
|
|
55
|
+
clientActivityID
|
|
55
56
|
},
|
|
56
57
|
channelId: 'webchat',
|
|
57
58
|
from: {
|
|
@@ -61,10 +62,7 @@ function* postActivity(directLine, userID, username, numActivitiesPosted, { meta
|
|
|
61
62
|
},
|
|
62
63
|
locale,
|
|
63
64
|
localTimestamp: dateToLocaleISOString(now),
|
|
64
|
-
localTimezone: localTimeZone
|
|
65
|
-
// This timestamp will be replaced by Direct Line Channel in echoback.
|
|
66
|
-
// We are temporarily adding this timestamp for sorting.
|
|
67
|
-
timestamp: getTimestamp(now, clockSkewAdjustment)
|
|
65
|
+
localTimezone: localTimeZone
|
|
68
66
|
};
|
|
69
67
|
|
|
70
68
|
if (!numActivitiesPosted) {
|
|
@@ -81,9 +79,9 @@ function* postActivity(directLine, userID, username, numActivitiesPosted, { meta
|
|
|
81
79
|
];
|
|
82
80
|
}
|
|
83
81
|
|
|
84
|
-
const meta = { clientActivityID, method };
|
|
82
|
+
const meta: { clientActivityID: string; method: string } = { clientActivityID, method };
|
|
85
83
|
|
|
86
|
-
yield put({ type: POST_ACTIVITY_PENDING, meta, payload: { activity } });
|
|
84
|
+
yield put({ type: POST_ACTIVITY_PENDING, meta, payload: { activity } } as PostActivityPendingAction);
|
|
87
85
|
|
|
88
86
|
try {
|
|
89
87
|
// Quirks: We might receive INCOMING_ACTIVITY before the postActivity call completed
|
|
@@ -93,10 +91,8 @@ function* postActivity(directLine, userID, username, numActivitiesPosted, { meta
|
|
|
93
91
|
for (;;) {
|
|
94
92
|
const {
|
|
95
93
|
payload: { activity }
|
|
96
|
-
} = yield take(INCOMING_ACTIVITY);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (channelData.clientActivityID === clientActivityID && id) {
|
|
94
|
+
}: IncomingActivityAction = yield take(INCOMING_ACTIVITY);
|
|
95
|
+
if (activity.channelData?.clientActivityID === clientActivityID && activity.id) {
|
|
100
96
|
return activity;
|
|
101
97
|
}
|
|
102
98
|
}
|
|
@@ -107,35 +103,48 @@ function* postActivity(directLine, userID, username, numActivitiesPosted, { meta
|
|
|
107
103
|
// - Direct Line service only respond on HTTP after bot respond to Direct Line
|
|
108
104
|
// - Activity may take too long time to echo back
|
|
109
105
|
|
|
110
|
-
const sendTimeout = yield select(sendTimeoutSelector);
|
|
106
|
+
const sendTimeout: number = yield select(sendTimeoutSelector);
|
|
111
107
|
|
|
112
108
|
const {
|
|
113
109
|
send: { echoBack }
|
|
114
|
-
} = yield race({
|
|
110
|
+
}: { send: { echoBack: WebChatActivity } } = yield race({
|
|
115
111
|
send: all({
|
|
116
112
|
echoBack: echoBackCall,
|
|
117
|
-
postActivity: observeOnce(directLine.postActivity(activity))
|
|
113
|
+
postActivity: observeOnce(directLine.postActivity(activity as DirectLineActivity))
|
|
118
114
|
}),
|
|
119
115
|
timeout: call(() => sleep(sendTimeout).then(() => Promise.reject(new Error('timeout'))))
|
|
120
116
|
});
|
|
121
117
|
|
|
122
|
-
yield put({ type: POST_ACTIVITY_FULFILLED, meta, payload: { activity: echoBack } });
|
|
118
|
+
yield put({ type: POST_ACTIVITY_FULFILLED, meta, payload: { activity: echoBack } } as PostActivityFulfilledAction);
|
|
123
119
|
} catch (err) {
|
|
124
120
|
console.error('botframework-webchat: Failed to post activity to chat adapter.', err);
|
|
125
121
|
|
|
126
|
-
yield put({ type: POST_ACTIVITY_REJECTED, error: true, meta, payload: err });
|
|
122
|
+
yield put({ type: POST_ACTIVITY_REJECTED, error: true, meta, payload: err } as PostActivityRejectedAction);
|
|
127
123
|
} finally {
|
|
128
124
|
if (yield cancelled()) {
|
|
129
|
-
yield put({
|
|
125
|
+
yield put({
|
|
126
|
+
type: POST_ACTIVITY_REJECTED,
|
|
127
|
+
error: true,
|
|
128
|
+
meta,
|
|
129
|
+
payload: new Error('cancelled')
|
|
130
|
+
} as PostActivityRejectedAction);
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
export default function* postActivitySaga() {
|
|
135
|
-
yield whileConnected(function* postActivityWhileConnected({
|
|
136
|
+
yield whileConnected(function* postActivityWhileConnected({
|
|
137
|
+
directLine,
|
|
138
|
+
userID,
|
|
139
|
+
username
|
|
140
|
+
}: {
|
|
141
|
+
directLine: DirectLineJSBotConnection;
|
|
142
|
+
userID: string;
|
|
143
|
+
username: string;
|
|
144
|
+
}) {
|
|
136
145
|
let numActivitiesPosted = 0;
|
|
137
146
|
|
|
138
|
-
yield takeEvery(POST_ACTIVITY, function* postActivityWrapper(action) {
|
|
147
|
+
yield takeEvery(POST_ACTIVITY, function* postActivityWrapper(action: PostActivityAction) {
|
|
139
148
|
yield* postActivity(directLine, userID, username, numActivitiesPosted++, action);
|
|
140
149
|
});
|
|
141
150
|
});
|
|
@@ -50,51 +50,52 @@ function* waitForActivityId(replyToId, initialActivities) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function* queueIncomingActivity({ userID }) {
|
|
53
|
-
yield takeEveryAndSelect(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
53
|
+
yield takeEveryAndSelect(
|
|
54
|
+
QUEUE_INCOMING_ACTIVITY,
|
|
55
|
+
activitiesSelector,
|
|
56
|
+
function* queueIncomingActivity({ payload: { activity } }, initialActivities) {
|
|
57
|
+
// This is for resolving an accessibility issue.
|
|
58
|
+
// If the incoming activity has "replyToId" field, hold on it until the activity replied to is in the transcript, then release this one.
|
|
59
|
+
const { replyToId } = activity;
|
|
60
|
+
const initialBotActivities = initialActivities.filter(({ from: { role } }) => role === 'bot');
|
|
61
|
+
|
|
62
|
+
// To speed up the first activity render time, we do not delay the first activity from the bot.
|
|
63
|
+
// Even if it is the first activity from the bot, the bot might be "replying" to the "conversationUpdate" event.
|
|
64
|
+
// Thus, the "replyToId" will always be there even it is the first activity in the conversation.
|
|
65
|
+
if (replyToId && initialBotActivities.length) {
|
|
66
|
+
// Either the activity replied to is in the transcript or after timeout.
|
|
67
|
+
const result = yield race({
|
|
68
|
+
_: waitForActivityId(replyToId, initialActivities),
|
|
69
|
+
timeout: call(sleep, REPLY_TIMEOUT)
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if ('timeout' in result) {
|
|
73
|
+
console.warn(
|
|
74
|
+
`botframework-webchat: Timed out while waiting for activity "${replyToId}" which activity "${activity.id}" is replying to.`,
|
|
75
|
+
{
|
|
76
|
+
activity,
|
|
77
|
+
replyToId
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
}
|
|
80
81
|
}
|
|
81
|
-
}
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
yield put(incomingActivity(activity));
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
// Update suggested actions
|
|
86
|
+
// TODO: [P3] We could put this logic inside reducer to minimize number of actions dispatched.
|
|
87
|
+
const messageActivities = yield select(activitiesOfType('message'));
|
|
88
|
+
const lastMessageActivity = messageActivities[messageActivities.length - 1];
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
if (activityFromBot(lastMessageActivity)) {
|
|
91
|
+
const { suggestedActions: { actions, to } = {} } = lastMessageActivity;
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
// If suggested actions is not destined to anyone, or is destined to the user, show it.
|
|
94
|
+
// In other words, if suggested actions is destined to someone else, don't show it.
|
|
95
|
+
yield put(setSuggestedActions(to && to.length && !to.includes(userID) ? null : actions));
|
|
96
|
+
}
|
|
96
97
|
}
|
|
97
|
-
|
|
98
|
+
);
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
export default function* queueIncomingActivitySaga() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { put, takeEvery } from 'redux-saga/effects';
|
|
2
|
+
import mime from 'mime';
|
|
2
3
|
|
|
3
4
|
import { SEND_FILES } from '../actions/sendFiles';
|
|
4
|
-
import mime from '../utils/mime-wrapper';
|
|
5
5
|
import postActivity from '../actions/postActivity';
|
|
6
6
|
import whileConnected from './effects/whileConnected';
|
|
7
7
|
|
|
@@ -3,7 +3,6 @@ import postActivity from '../actions/postActivity';
|
|
|
3
3
|
import { SEND_MESSAGE_BACK } from '../actions/sendMessageBack';
|
|
4
4
|
import whileConnected from './effects/whileConnected';
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
// https://github.com/microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#message-back
|
|
8
7
|
function* postActivityWithMessageBack({ payload: { displayText, text, value } }) {
|
|
9
8
|
yield put(
|