Skip to content

Commit

Permalink
Send welcome event and fixes around speech sagas (#1286)
Browse files Browse the repository at this point in the history
* Send join event and various fixes

* Cleanup

* Clean up coding style

* Clean up coding style

* Fix numActivitiesPosted

* Clean up

* Stop speak activity action will mark all as spoken

* Clean up

* Revisit all sagas related to dictate and speak

* Clean up

* Clean up

* Clean up

* Move send typing to another branch and clean up sample

* Rename whileSpeaking to whileSpeakIncomingActivity

* Rename via to method

* Refactoring

* Better verbiage

* Remove unnecessary null-ref check

* Add comment for patching

* Add selectors

* Prefix items with area

* Fix missing activities

* Sort line

* Send join event on connect

* Move user ID check out of speakableActivity

* Fix sendEvent

* Update sample number
  • Loading branch information
compulim authored Jan 13, 2019
1 parent a10e0e8 commit 849e563
Show file tree
Hide file tree
Showing 50 changed files with 607 additions and 335 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Fix [#1383](https://github.com/Microsoft/BotFramework-WebChat/issues/1383). Added options to hide upload button, by [@compulim](https://github.com/compulim) in PR [#1491](https://github.com/Microsoft/BotFramework-WebChat/pull/1491)
- Added support of avatar image, thru `styleOptions.botAvatarImage` and `styleOptions.userAvatarImage`, in PR [#1486](https://github.com/Microsoft/BotFramework-WebChat/pull/1486)
- Added ability to style sendbox background and text color, thru `styleOptions.sendBoxBackground` and `styleOptions.sendBoxTextColor`, in PR [#1575](https://github.com/Microsoft/BotFramework-WebChat/pull/1575)
- `core`: Added `sendEvent`, in PR [#1286](https://github.com/Microsoft/BotFramework-WebChat/pull/1286)
- `core`: Added `CONNECT_FULFILLING` action to workaround `redux-saga` [design decision](https://github.com/redux-saga/redux-saga/issues/1651), in PR [#1286](https://github.com/Microsoft/BotFramework-WebChat/pull/1286)

### Changed
- Moved `botAvatarImage` and `userAvatarImage` to `styleOptions.botAvatarImage` and `styleOptions.userAvatarImage` respectively, in PR [#1486](https://github.com/Microsoft/BotFramework-WebChat/pull/1486)
Expand All @@ -43,6 +45,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- `component`: Fix [#1560](https://github.com/Microsoft/BotFramework-WebChat/issues/1560). Fixed carousel layout did not show date and alignment issues, by [@compulim](https://github.com/compulim) in PR [#1561](https://github.com/Microsoft/BotFramework-WebChat/pull/1561)
- `playground`: Fix [#1562](https://github.com/Microsoft/BotFramework-WebChat/issues/1562). Fixed timestamp grouping "Don't group" and added "Don't show timestamp", by [@compulim](https://github.com/compulim) in PR [#1563](https://github.com/Microsoft/BotFramework-WebChat/pull/1563)
- `component`: Fix [#1576](https://github.com/Microsoft/BotFramework-WebChat/issues/1576). Rich card without `tap` should be rendered properly, by [@compulim](https://github.com/compulim) in PR [#1577](https://github.com/Microsoft/BotFramework-WebChat/pull/1577)
- `core`: Some sagas missed handling successive actions, in PR [#1286](https://github.com/Microsoft/BotFramework-WebChat/pull/1286)
- `core`: `incomingActivitySaga` may throw null-ref exception if the first activity is from user, in PR [#1286](https://github.com/Microsoft/BotFramework-WebChat/pull/1286)
- `component`: Fix [#1328](https://github.com/Microsoft/BotFramework-WebChat/issues/1328). Should not start microphone if input hint is set to `ignoringInput`, in PR [#1286](https://github.com/Microsoft/BotFramework-WebChat/pull/1286)
- `component`: Fix outgoing typing indicators are not sent and acknowledged properly, in PR [#1286](https://github.com/Microsoft/BotFramework-WebChat/pull/1286)

### Removed
- `botAvatarImage` and `userAvatarImage` props, as they are moved inside `styleOptions`, in PR [#1486](https://github.com/Microsoft/BotFramework-WebChat/pull/1486)
Expand All @@ -52,6 +58,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- `component`: [Hide upload button](https://microsoft.github.io/BotFramework-WebChat/05.d.hide-upload-button-styling/), in [#1491](https://github.com/Microsoft/BotFramework-WebChat/pull/1491)
- `component`: [Avatar image](https://microsoft.github.io/BotFramework-WebChat/04.b.display-user-bot-images-styling/), in [#1486](https://github.com/Microsoft/BotFramework-WebChat/pull/1486)
- `core`: [Incoming activity to JavaScript event](https://microsoft.github.io/BotFramework-WebChat/15.b.incoming-activity-event/), in [#1567](https://github.com/Microsoft/BotFramework-WebChat/pull/1567)
- `core`: [Send welcome event](https://microsoft.github.io/BotFramework-WebChat/15.b.backchannel-send-welcome-event/), in PR [#1286](https://github.com/Microsoft/BotFramework-WebChat/pull/1286)

## [4.2.0] - 2018-12-11
### Added
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ npm run prepublishOnly
| [`15.a.backchannel-piggyback-on-outgoing-activities`](https://github.com/Microsoft/BotFramework-WebChat/tree/master/samples/15.a.backchannel-piggyback-on-outgoing-activities) | Advanced tutorial: Demonstrates how to add custom data to every outgoing activities. | [Demo](https://microsoft.github.io/BotFramework-WebChat/15.a.backchannel-piggyback-on-outgoing-activities) |
| [`15.b.incoming-activity-event`](https://github.com/Microsoft/BotFramework-WebChat/tree/master/samples/15.a.incoming-activity-event) | Advanced tutorial: Demonstrates how to forward all incoming activities to a JavaScript event for further processing. | [Demo](https://microsoft.github.io/BotFramework-WebChat/15.a.incoming-activity-event) |
| [`15.c.programmatic-post-activity`](https://github.com/Microsoft/BotFramework-WebChat/tree/master/samples/15.c.programmatic-post-activity) | Advanced tutorial: Demonstrates how to send a message programmatically. | [Demo](https://microsoft.github.io/BotFramework-WebChat/15.c.programmatic-post-activity) |
| [`15.d.backchannel-send-welcome-event`](https://github.com/Microsoft/BotFramework-WebChat/tree/master/samples/15.d.backchannel-send-welcome-event) | Advanced tutorial: Demonstrates how to send welcome event with client capabilities such as browser language. | [Demo](https://microsoft.github.io/BotFramework-WebChat/15.d.backchannel-send-welcome-event) |

# Contributions

Expand Down
5 changes: 4 additions & 1 deletion packages/component/src/BasicTranscript.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,10 @@ const BasicTranscript = ({
role="listitem"
>
{ activityRenderer({ activity, showTimestamp })(({ attachment }) => attachmentRenderer({ activity, attachment })) }
{ activity.channelData && activity.channelData.speak && <SpeakActivity activity={ activity } /> }
{
// TODO: [P2] We should use core/definitions/speakingActivity for this predicate instead
activity.channelData && activity.channelData.speak && <SpeakActivity activity={ activity } />
}
</li>
);
})
Expand Down
2 changes: 2 additions & 0 deletions packages/component/src/Composer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
disconnect,
markActivity,
postActivity,
sendEvent,
sendFiles,
sendMessage,
sendPostBack,
Expand Down Expand Up @@ -44,6 +45,7 @@ const EMPTY_ARRAY = [];
const DISPATCHERS = {
markActivity,
postActivity,
sendEvent,
sendFiles,
sendMessage,
sendPostBack,
Expand Down
4 changes: 2 additions & 2 deletions packages/component/src/Dictation.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class Dictation extends React.Component {
props.stopDictate();

if (transcript) {
props.setSendBox(transcript, 'speech');
props.setSendBox(transcript);
props.submitSendBox('speech');
props.startSpeakingActivity();
}
Expand All @@ -45,7 +45,7 @@ class Dictation extends React.Component {
// This is for two purposes:
// 1. Set send box will also trigger send typing
// 2. If the user cancelled out, the interim result will be in the send box so the user can update it before send
props.setSendBox(interims.join(' '), 'speech');
props.setSendBox(interims.join(' '));
}

handleError(event) {
Expand Down
4 changes: 2 additions & 2 deletions packages/component/src/SendBox/TextBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const connectSendTextBox = (...selectors) => connectToWebChat(
disabled,
language,
onChange: ({ target: { value } }) => {
setSendBox(value, 'keyboard');
setSendBox(value);
},
onSubmit: event => {
event.preventDefault();
Expand All @@ -36,7 +36,7 @@ const connectSendTextBox = (...selectors) => connectToWebChat(

if (sendBoxValue) {
scrollToEnd();
submitSendBox('keyboard');
submitSendBox();
}
},
value: sendBoxValue
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/actions/connect.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const CONNECT = 'DIRECT_LINE/CONNECT';
const CONNECT_PENDING = `${ CONNECT }_PENDING`;
const CONNECT_REJECTED = `${ CONNECT }_REJECTED`;
const CONNECT_FULFILLING = `${ CONNECT }_FULFILLING`;
const CONNECT_FULFILLED = `${ CONNECT }_FULFILLED`;

export default function ({ directLine, userID }) {
Expand All @@ -14,5 +15,6 @@ export {
CONNECT,
CONNECT_PENDING,
CONNECT_REJECTED,
CONNECT_FULFILLING,
CONNECT_FULFILLED
}
3 changes: 2 additions & 1 deletion packages/core/src/actions/postActivity.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ export const POST_ACTIVITY_FULFILLED = `${ POST_ACTIVITY }_${ FULFILLED }`;
export const POST_ACTIVITY_PENDING = `${ POST_ACTIVITY }_${ PENDING }`;
export const POST_ACTIVITY_REJECTED = `${ POST_ACTIVITY }_${ REJECTED }`;

export default function (activity) {
export default function (activity, method = 'keyboard') {
return {
type: POST_ACTIVITY,
meta: { method },
payload: { activity }
};
}
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/actions/sendEvent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const SEND_EVENT = 'WEB_CHAT/SEND_EVENT';

export default function sendEvent(name, value) {
return {
type: SEND_EVENT,
payload: { name, value }
};
}

export { SEND_EVENT }
4 changes: 2 additions & 2 deletions packages/core/src/actions/sendMessage.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const SEND_MESSAGE = 'WEB_CHAT/SEND_MESSAGE';

export default function sendMessage(text, via) {
export default function sendMessage(text, method) {
return {
type: SEND_MESSAGE,
payload: { text, via }
payload: { method, text }
};
}

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/actions/setSendBox.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const SET_SEND_BOX = 'WEB_CHAT/SET_SEND_BOX';

export default function (text, via) {
export default function (text) {
return {
type: SET_SEND_BOX,
payload: { text, via }
payload: { text }
};
}

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/actions/submitSendBox.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const SUBMIT_SEND_BOX = 'WEB_CHAT/SUBMIT_SEND_BOX';

export default function submitSendBox(via) {
export default function submitSendBox(method = 'keyboard') {
return {
type: SUBMIT_SEND_BOX,
payload: { via }
payload: { method }
};
}

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/definitions/activityFromBot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default activity => activity && activity.from.role === 'bot'
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
export default function (activity, userID) {
export default function (activity) {
return (
activity
&& activity.from
&& activity.from.id !== userID
&& activity.type === 'message'
);
}
3 changes: 3 additions & 0 deletions packages/core/src/definitions/speakingActivity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function (activity) {
return activity.channelData && activity.channelData.speak;
}
2 changes: 2 additions & 0 deletions packages/core/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import createStore from './createStore';
import disconnect from './actions/disconnect';
import markActivity from './actions/markActivity';
import postActivity from './actions/postActivity';
import sendEvent from './actions/sendEvent';
import sendFiles from './actions/sendFiles';
import sendMessage from './actions/sendMessage';
import sendPostBack from './actions/sendPostBack';
Expand Down Expand Up @@ -31,6 +32,7 @@ export {
disconnect,
markActivity,
postActivity,
sendEvent,
sendFiles,
sendMessage,
sendPostBack,
Expand Down
22 changes: 14 additions & 8 deletions packages/core/src/sagas.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,38 @@ import clearSuggestedActionsOnPostActivitySaga from './sagas/clearSuggestedActio
import connectionStatusUpdateSaga from './sagas/connectionStatusUpdateSaga';
import connectSaga from './sagas/connectSaga';
import incomingActivitySaga from './sagas/incomingActivitySaga';
import incomingTypingSaga from './sagas/incomingTypingSaga';
import markActivityForSpeakSaga from './sagas/markActivityForSpeakSaga';
import markActivityForSpeakOnIncomingActivityFromOthersSaga from './sagas/markActivityForSpeakOnIncomingActivityFromOthersSaga';
import markAllAsSpokenOnStopSpeakActivitySaga from './sagas/markAllAsSpokenOnStopSpeakActivitySaga';
import postActivitySaga from './sagas/postActivitySaga';
import removeIncomingTypingAfterIntervalSaga from './sagas/removeIncomingTypingAfterIntervalSaga';
import sendEventToPostActivitySaga from './sagas/sendEventToPostActivitySaga';
import sendFilesToPostActivitySaga from './sagas/sendFilesToPostActivitySaga';
import sendMessageToPostActivitySaga from './sagas/sendMessageToPostActivitySaga';
import sendPostBackToPostActivitySaga from './sagas/sendPostBackToPostActivitySaga';
import sendTypingOnSetSendBoxSaga from './sagas/sendTypingOnSetSendBoxSaga';
import startDictateAfterSpeakActivitySaga from './sagas/startDictateAfterSpeakActivitySaga';
import stopDictateOnCardAction from './sagas/stopDictateOnCardAction';
import stopSpeakActivityOnInputSaga from './sagas/stopSpeakActivityOnInputSaga';
import startSpeakActivityOnPostActivitySaga from './sagas/startSpeakActivityOnPostActivitySaga';
import stopDictateOnCardActionSaga from './sagas/stopDictateOnCardActionSaga';
import stopSpeakingActivityOnInputSaga from './sagas/stopSpeakingActivityOnInputSaga';
import submitSendBoxSaga from './sagas/submitSendBoxSaga';

export default function* () {
yield fork(clearSuggestedActionsOnPostActivitySaga);
yield fork(connectionStatusUpdateSaga);
yield fork(connectSaga);
yield fork(incomingActivitySaga);
yield fork(incomingTypingSaga);
yield fork(markActivityForSpeakSaga);
yield fork(markActivityForSpeakOnIncomingActivityFromOthersSaga);
yield fork(markAllAsSpokenOnStopSpeakActivitySaga);
yield fork(postActivitySaga);
yield fork(removeIncomingTypingAfterIntervalSaga);
yield fork(sendEventToPostActivitySaga);
yield fork(sendFilesToPostActivitySaga);
yield fork(sendMessageToPostActivitySaga);
yield fork(sendPostBackToPostActivitySaga);
yield fork(sendTypingOnSetSendBoxSaga);
yield fork(startDictateAfterSpeakActivitySaga);
yield fork(stopDictateOnCardAction);
yield fork(stopSpeakActivityOnInputSaga);
yield fork(startSpeakActivityOnPostActivitySaga);
yield fork(stopDictateOnCardActionSaga);
yield fork(stopSpeakingActivityOnInputSaga);
yield fork(submitSendBoxSaga);
}
26 changes: 18 additions & 8 deletions packages/core/src/sagas/clearSuggestedActionsOnPostActivitySaga.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
import {
put,
take
takeEvery
} from 'redux-saga/effects';

import { POST_ACTIVITY_PENDING } from '../actions/postActivity';
import whileConnected from './effects/whileConnected';

import { POST_ACTIVITY_PENDING } from '../actions/postActivity';
import setSuggestedActions from '../actions/setSuggestedActions';

export default function* () {
yield whileConnected(function* () {
for (;;) {
yield take(({ payload, type }) => type === POST_ACTIVITY_PENDING && payload.activity.type === 'message');
yield put(setSuggestedActions());
}
});
yield whileConnected(clearSuggestedActionsOnPostActivity);
}

function* clearSuggestedActionsOnPostActivity() {
yield takeEvery(
({ payload, type }) => (
type === POST_ACTIVITY_PENDING
&& payload.activity.type === 'message'
),
clearSuggestedActions
);
}

function* clearSuggestedActions() {
yield put(setSuggestedActions());
}
2 changes: 2 additions & 0 deletions packages/core/src/sagas/connectSaga.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
CONNECT,
CONNECT_PENDING,
CONNECT_REJECTED,
CONNECT_FULFILLING,
CONNECT_FULFILLED
} from '../actions/connect';

Expand Down Expand Up @@ -83,6 +84,7 @@ function* connectSaga(directLine, userID) {
try {
try {
yield callUntil(connectionStatusQueue.shift, [], connectionStatus => connectionStatus === ONLINE);
yield put({ type: CONNECT_FULFILLING, meta, payload: { directLine } });
yield put({ type: CONNECT_FULFILLED, meta, payload: { directLine } });
} catch (err) {
yield put({ type: CONNECT_REJECTED, error: true, meta, payload: err });
Expand Down
12 changes: 7 additions & 5 deletions packages/core/src/sagas/connectionStatusUpdateSaga.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import connectionStatusUpdate from '../actions/connectionStatusUpdate';
import setReferenceGrammarID from '../actions/setReferenceGrammarID';

export default function* () {
yield whileConnected(function* (directLine) {
yield observeEach(directLine.connectionStatus$, function* (connectionStatus) {
yield put(connectionStatusUpdate(connectionStatus));
yield put(setReferenceGrammarID(directLine.referenceGrammarId));
});
yield whileConnected(observeConnectionStatus);
}

function* observeConnectionStatus(directLine) {
yield observeEach(directLine.connectionStatus$, function* (connectionStatus) {
yield put(connectionStatusUpdate(connectionStatus));
yield put(setReferenceGrammarID(directLine.referenceGrammarId));
});
}
4 changes: 2 additions & 2 deletions packages/core/src/sagas/effects/whileConnected.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import {
take
} from 'redux-saga/effects';

import { CONNECT_FULFILLED } from '../../actions/connect';
import { CONNECT_FULFILLING } from '../../actions/connect';
import { DISCONNECT_FULFILLED } from '../../actions/disconnect';

export default function (fn) {
return call(function* () {
for (;;) {
const { meta: { userID }, payload: { directLine } } = yield take(CONNECT_FULFILLED);
const { meta: { userID }, payload: { directLine } } = yield take(CONNECT_FULFILLING);
const task = yield fork(fn, directLine, userID);

yield take(DISCONNECT_FULFILLED);
Expand Down
22 changes: 22 additions & 0 deletions packages/core/src/sagas/effects/whileSpeakIncomingActivity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {
call,
cancel,
fork,
take
} from 'redux-saga/effects';

import { START_SPEAKING_ACTIVITY } from '../../actions/startSpeakingActivity';
import { STOP_SPEAKING_ACTIVITY } from '../../actions/stopSpeakingActivity';

export default function (fn) {
return call(function* () {
for (;;) {
yield take(START_SPEAKING_ACTIVITY);

const task = yield fork(fn);

yield take(STOP_SPEAKING_ACTIVITY);
yield cancel(task);
}
});
}
Loading

0 comments on commit 849e563

Please sign in to comment.