diff --git a/packages/core/src/definitions/activityFromBot.js b/packages/core/src/definitions/activityFromBot.js new file mode 100644 index 0000000000..8570b01d2d --- /dev/null +++ b/packages/core/src/definitions/activityFromBot.js @@ -0,0 +1 @@ +export default activity => activity && activity.from.role === 'bot' diff --git a/packages/core/src/sagas/incomingActivitySaga.js b/packages/core/src/sagas/incomingActivitySaga.js index 1f81e12fe3..6a4fd759a6 100644 --- a/packages/core/src/sagas/incomingActivitySaga.js +++ b/packages/core/src/sagas/incomingActivitySaga.js @@ -5,21 +5,15 @@ import { import updateIn from 'simple-update-in'; -import observeEach from './effects/observeEach'; -import whileConnected from './effects/whileConnected'; - import incomingActivity from '../actions/incomingActivity'; import setSuggestedActions from '../actions/setSuggestedActions'; -function last(array, predicate) { - for (let i = array.length - 1; i >= 0; i--) { - const item = array[i]; +import activityFromBot from '../definitions/activityFromBot'; - if (predicate.call(array, item)) { - return item; - } - } -} +import observeEach from './effects/observeEach'; +import whileConnected from './effects/whileConnected'; + +import { ofType as activitiesOfType } from '../selectors/activities'; export default function* () { yield whileConnected(observeActivity); @@ -32,11 +26,10 @@ function* observeActivity(directLine, userID) { yield put(incomingActivity(activity)); // Update suggested actions - const { activities } = yield select(); - const lastMessageActivity = last(activities, ({ type }) => type === 'message'); + const messageActivities = yield select(activitiesOfType('message')); + const lastMessageActivity = messageActivities[messageActivities.length - 1]; - // TODO: [P2] Consider using "definitions/activityFromBot" - if (lastMessageActivity && lastMessageActivity.from.role === 'bot') { + if (activityFromBot(lastMessageActivity)) { const { suggestedActions: { actions } = {} } = lastMessageActivity; yield put(setSuggestedActions(actions)); diff --git a/packages/core/src/sagas/markAllAsSpokenOnStopSpeakActivitySaga.js b/packages/core/src/sagas/markAllAsSpokenOnStopSpeakActivitySaga.js index 79b1ca10ef..84f9617830 100644 --- a/packages/core/src/sagas/markAllAsSpokenOnStopSpeakActivitySaga.js +++ b/packages/core/src/sagas/markAllAsSpokenOnStopSpeakActivitySaga.js @@ -4,21 +4,21 @@ import { takeEvery } from 'redux-saga/effects'; -import speakingActivity from '../definitions/speakingActivity'; - import { STOP_SPEAKING_ACTIVITY } from '../actions/stopSpeakingActivity'; import markActivity from '../actions/markActivity'; +import speakingActivity from '../definitions/speakingActivity'; + +import { of as activitiesOf } from '../selectors/activities'; + export default function* () { yield takeEvery(STOP_SPEAKING_ACTIVITY, markAllAsSpoken); } function* markAllAsSpoken() { - const { activities } = yield select(); + const speakingActivities = yield select(activitiesOf(speakingActivity)); - for (let activity of activities) { - if (speakingActivity(activity)) { - yield put(markActivity(activity, 'speak', false)); - } + for (let activity of speakingActivities) { + yield put(markActivity(activity, 'speak', false)); } } diff --git a/packages/core/src/sagas/postActivitySaga.js b/packages/core/src/sagas/postActivitySaga.js index 64ee2675e9..8218c93a3c 100644 --- a/packages/core/src/sagas/postActivitySaga.js +++ b/packages/core/src/sagas/postActivitySaga.js @@ -9,13 +9,15 @@ import { takeEvery } from 'redux-saga/effects'; -import sleep from '../utils/sleep'; - import observeOnce from './effects/observeOnce'; import whileConnected from './effects/whileConnected'; +import languageSelector from '../selectors/language'; +import sendTimeoutSelector from '../selectors/sendTimeout'; + import deleteKey from '../utils/deleteKey'; import getTimestamp from '../utils/getTimestamp'; +import sleep from '../utils/sleep'; import uniqueID from '../utils/uniqueID'; import { @@ -38,7 +40,7 @@ export default function* () { } function* postActivity(directLine, userID, numActivitiesPosted, { meta: { method }, payload: { activity } }) { - const locale = yield select(({ language }) => language); + const locale = yield select(languageSelector); const { attachments, channelData: { clientActivityID = uniqueID() } = {} } = activity; activity = { @@ -96,7 +98,7 @@ function* postActivity(directLine, userID, numActivitiesPosted, { meta: { method // - Direct Line service only respond on HTTP after bot respond to Direct Line // - Activity may take too long time to echo back - const sendTimeout = yield select(({ sendTimeout }) => sendTimeout); + const sendTimeout = yield select(sendTimeoutSelector); const { send: { echoBack } } = yield race({ send: all({ diff --git a/packages/core/src/sagas/sendTypingOnSetSendBoxSaga.js b/packages/core/src/sagas/sendTypingOnSetSendBoxSaga.js index 10801cdea6..e22015a74b 100644 --- a/packages/core/src/sagas/sendTypingOnSetSendBoxSaga.js +++ b/packages/core/src/sagas/sendTypingOnSetSendBoxSaga.js @@ -7,11 +7,14 @@ import { takeLatest } from 'redux-saga/effects'; -import whileConnected from './effects/whileConnected'; - import { SET_SEND_BOX } from '../actions/setSendBox'; import { SET_SEND_TYPING } from '../actions/setSendTyping'; import postActivity from '../actions/postActivity'; + +import whileConnected from './effects/whileConnected'; + +import sendTypingSelector from '../selectors/sendTyping'; + import sleep from '../utils/sleep'; const SEND_INTERVAL = 3000; @@ -25,7 +28,7 @@ export default function* () { } function* sendTypingOnSetSendBox() { - const { sendTyping } = yield select(); + const sendTyping = yield select(sendTypingSelector); if (!sendTyping) { yield takeSendTyping(true); diff --git a/packages/core/src/sagas/startDictateAfterSpeakActivitySaga.js b/packages/core/src/sagas/startDictateAfterSpeakActivitySaga.js index 029271ca89..852f1a09f2 100644 --- a/packages/core/src/sagas/startDictateAfterSpeakActivitySaga.js +++ b/packages/core/src/sagas/startDictateAfterSpeakActivitySaga.js @@ -4,13 +4,15 @@ import { takeEvery } from 'redux-saga/effects'; +import { MARK_ACTIVITY } from '../actions/markActivity'; +import startDictate from '../actions/startDictate'; + import speakingActivity from '../definitions/speakingActivity'; import whileConnected from './effects/whileConnected'; import whileSpeakIncomingActivity from './effects/whileSpeakIncomingActivity'; -import { MARK_ACTIVITY } from '../actions/markActivity'; -import startDictate from '../actions/startDictate'; +import { ofID as activitiesOfID } from '../selectors/activities'; export default function* () { yield whileConnected(function* () { @@ -30,11 +32,11 @@ function* startDictateAfterSpeakActivity() { } function* startDictateAfterAllActivitiesSpoken({ payload: { activityID } }) { - const { activities } = yield select(); - const spokenActivity = activities.find(({ id }) => id === activityID); + const [spokenActivity] = yield select(activitiesOfID(activityID)); if ( - spokenActivity.inputHint !== 'ignoringInput' + spokenActivity + && spokenActivity.inputHint !== 'ignoringInput' // Checks if there are no more activities that will be synthesis && !activities.some( activity => activity.id !== activityID && speakingActivity(activity) diff --git a/packages/core/src/sagas/submitSendBoxSaga.js b/packages/core/src/sagas/submitSendBoxSaga.js index 6b29865c37..cf08e5df4f 100644 --- a/packages/core/src/sagas/submitSendBoxSaga.js +++ b/packages/core/src/sagas/submitSendBoxSaga.js @@ -4,19 +4,21 @@ import { takeEvery } from 'redux-saga/effects'; -import whileConnected from './effects/whileConnected'; - import { SUBMIT_SEND_BOX } from '../actions/submitSendBox'; import sendMessage from '../actions/sendMessage'; import setSendBox from '../actions/setSendBox'; +import whileConnected from './effects/whileConnected'; + +import sendBoxValueSelector from '../selectors/sendBoxValue'; + export default function* () { yield whileConnected(submitSendBox); } function* submitSendBox() { yield takeEvery(SUBMIT_SEND_BOX, function* ({ payload: { method } }) { - const { sendBoxValue } = yield select(); + const sendBoxValue = yield select(sendBoxValueSelector); if (sendBoxValue) { yield put(sendMessage(sendBoxValue, method)); diff --git a/packages/core/src/selectors/activities.js b/packages/core/src/selectors/activities.js new file mode 100644 index 0000000000..033507d02b --- /dev/null +++ b/packages/core/src/selectors/activities.js @@ -0,0 +1,8 @@ +const activities = ({ activities }) => activities + +const of = predicate => state => activities(state).filter(predicate) +const ofID = targetID => of(({ id }) => id === targetID); +const ofType = targetType => of(({ type }) => type === targetType); + +export default activities +export { of, ofID, ofType } diff --git a/packages/core/src/selectors/language.js b/packages/core/src/selectors/language.js new file mode 100644 index 0000000000..a0805cacab --- /dev/null +++ b/packages/core/src/selectors/language.js @@ -0,0 +1 @@ +export default ({ language }) => language diff --git a/packages/core/src/selectors/sendBoxValue.js b/packages/core/src/selectors/sendBoxValue.js new file mode 100644 index 0000000000..1856b80846 --- /dev/null +++ b/packages/core/src/selectors/sendBoxValue.js @@ -0,0 +1 @@ +export default ({ sendBoxValue }) => sendBoxValue diff --git a/packages/core/src/selectors/sendTimeout.js b/packages/core/src/selectors/sendTimeout.js new file mode 100644 index 0000000000..12b4383623 --- /dev/null +++ b/packages/core/src/selectors/sendTimeout.js @@ -0,0 +1 @@ +export default ({ sendTimeout }) => sendTimeout diff --git a/packages/core/src/selectors/sendTyping.js b/packages/core/src/selectors/sendTyping.js new file mode 100644 index 0000000000..c90c9813cb --- /dev/null +++ b/packages/core/src/selectors/sendTyping.js @@ -0,0 +1 @@ +export default ({ sendTyping }) => sendTyping