diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b232faa7d..ea94a232cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,12 +47,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - PR [#2547](https://github.com/microsoft/BotFramework-WebChat/pull/2547): `useEmitTypingIndicator`, `usePeformCardAction`, `usePostActivity`, `useSendEvent`, `useSendFiles`, `useSendMessage`, `useSendMessageBack`, `useSendPostBack` - PR [#2548](https://github.com/microsoft/BotFramework-WebChat/pull/2548): `useDisabled` - PR [#2549](https://github.com/microsoft/BotFramework-WebChat/pull/2549): `useSuggestedActions` + - PR [#2550](https://github.com/microsoft/BotFramework-WebChat/pull/2550): `useConnectivityStatus`, `useGroupTimestamp`, `useTimeoutForSend`, `useUserID`, `useUsername` - PR [#2551](https://github.com/microsoft/BotFramework-WebChat/pull/2551): `useLastTypingAt`, `useSendTypingIndicator`, `useTypingIndicator` - PR [#2552](https://github.com/microsoft/BotFramework-WebChat/pull/2552): `useFocusSendBox`, `useScrollToEnd`, `useSendBoxValue`, `useSubmitSendBox`, `useTextBoxSubmit`, `useTextBoxValue` - Bring your own Adaptive Cards package by specifying `adaptiveCardsPackage` prop, by [@compulim](https://github.com/compulim) in PR [#2543](https://github.com/microsoft/BotFramework-WebChat/pull/2543) - Fixes [#2597](https://github.com/microsoft/BotFramework-WebChat/issues/2597). Modify `watch` script to `start` and add `tableflip` script for throwing `node_modules`, by [@corinagum](https://github.com/corinagum) in PR [#2598](https://github.com/microsoft/BotFramework-WebChat/pull/2598) - `component`: Fixes [#2331](https://github.com/microsoft/BotFramework-WebChat/issues/2331). Updated timer to use React Hooks, by [@spyip](https://github.com/spyip) in PR [#2546](https://github.com/microsoft/BotFramework-WebChat/pull/2546) -- Resolves [#2620](https://github.com/Microsoft/BotFramework-WebChat/issues/2620), update Chinese localization files, by [@spyip](https://github.com/spyip) in PR [#2631](https://github.com/microsoft/BotFramework-WebChat/pull/2631) +- Resolves [#2597](https://github.com/microsoft/BotFramework-WebChat/issues/2597). Modify `watch` script to `start` and add `tableflip` script for throwing `node_modules`, by [@corinagum](https://github.com/corinagum) in PR [#2598](https://github.com/microsoft/BotFramework-WebChat/pull/2598) +- Adds `suggestedActionLayout` to `defaultStyleOptions`, by [@spyip](https://github.com/spyip), in PR [#2596](https://github.com/microsoft/BotFramework-WebChat/pull/2596) +- Resolves [#2331](https://github.com/microsoft/BotFramework-WebChat/issues/2331). Updated timer to use React Hooks, by [@spyip](https://github.com/spyip) in PR [#2546](https://github.com/microsoft/BotFramework-WebChat/pull/2546) +- Resolves [#2620](https://github.com/microsoft/BotFramework-WebChat/issues/2620), update Chinese localization files, by [@spyip](https://github.com/spyip) in PR [#2631](https://github.com/microsoft/BotFramework-WebChat/pull/2631) ### Changed diff --git a/__tests__/__image_snapshots__/chrome-docker/suggested-actions-js-suggested-actions-command-should-show-correctly-formatted-buttons-when-suggested-actions-are-displayed-as-stacked-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/suggested-actions-js-suggested-actions-command-should-show-correctly-formatted-buttons-when-suggested-actions-are-displayed-as-stacked-1-snap.png new file mode 100644 index 0000000000..a8f7ed476f Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/suggested-actions-js-suggested-actions-command-should-show-correctly-formatted-buttons-when-suggested-actions-are-displayed-as-stacked-1-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/suggested-actions-js-suggested-actions-command-should-show-suggested-actions-with-larger-images-as-stacked-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/suggested-actions-js-suggested-actions-command-should-show-suggested-actions-with-larger-images-as-stacked-1-snap.png new file mode 100644 index 0000000000..fd91da858b Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/suggested-actions-js-suggested-actions-command-should-show-suggested-actions-with-larger-images-as-stacked-1-snap.png differ diff --git a/__tests__/hooks/useConnectivityStatus.js b/__tests__/hooks/useConnectivityStatus.js new file mode 100644 index 0000000000..11ec3f4c9e --- /dev/null +++ b/__tests__/hooks/useConnectivityStatus.js @@ -0,0 +1,24 @@ +import { timeouts } from '../constants.json'; +import uiConnected from '../setup/conditions/uiConnected'; + +// selenium-webdriver API doc: +// https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html + +jest.setTimeout(timeouts.test); + +test('getter should return online', async () => { + const { driver, pageObjects } = await setupWebDriver(); + + await driver.wait(uiConnected(), timeouts.directLine); + + const [connectivityStatus] = await pageObjects.runHook('useConnectivityStatus'); + + expect(connectivityStatus).toMatchInlineSnapshot(`"connected"`); +}); + +test('setter should be falsy', async () => { + const { pageObjects } = await setupWebDriver(); + const [_, setConnectivityStatus] = await pageObjects.runHook('useConnectivityStatus'); + + expect(setConnectivityStatus).toBeFalsy(); +}); diff --git a/__tests__/hooks/useGroupTimestamp.js b/__tests__/hooks/useGroupTimestamp.js new file mode 100644 index 0000000000..823e238d4c --- /dev/null +++ b/__tests__/hooks/useGroupTimestamp.js @@ -0,0 +1,43 @@ +import { timeouts } from '../constants.json'; + +// selenium-webdriver API doc: +// https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html + +jest.setTimeout(timeouts.test); + +test('getter should return group timestamp set in props', async () => { + const { pageObjects } = await setupWebDriver({ + props: { + groupTimestamp: 1000 + } + }); + + const [groupTimestamp] = await pageObjects.runHook('useGroupTimestamp'); + + expect(groupTimestamp).toMatchInlineSnapshot(`1000`); +}); + +test('getter should return default group timestamp if not set in props', async () => { + const { pageObjects } = await setupWebDriver(); + + const [groupTimestamp] = await pageObjects.runHook('useGroupTimestamp'); + + expect(groupTimestamp).toMatchInlineSnapshot(`true`); +}); + +test('getter should return false if group timestamp is disabled', async () => { + const { pageObjects } = await setupWebDriver({ + props: { groupTimestamp: false } + }); + + const [groupTimestamp] = await pageObjects.runHook('useGroupTimestamp'); + + expect(groupTimestamp).toMatchInlineSnapshot(`false`); +}); + +test('setter should be falsy', async () => { + const { pageObjects } = await setupWebDriver(); + const [_, setGroupTimestamp] = await pageObjects.runHook('useGroupTimestamp'); + + expect(setGroupTimestamp).toBeFalsy(); +}); diff --git a/__tests__/hooks/useTimeoutForSend.js b/__tests__/hooks/useTimeoutForSend.js new file mode 100644 index 0000000000..25b7a22d4c --- /dev/null +++ b/__tests__/hooks/useTimeoutForSend.js @@ -0,0 +1,36 @@ +import { timeouts } from '../constants.json'; + +// selenium-webdriver API doc: +// https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html + +jest.setTimeout(timeouts.test); + +test('getter should return timeout for sending activity', async () => { + const { pageObjects } = await setupWebDriver({ + props: { + sendTimeout: 1000 + } + }); + + const [timeoutForSend] = await pageObjects.runHook('useTimeoutForSend'); + + expect(timeoutForSend).toMatchInlineSnapshot(`1000`); +}); + +test('getter should return default timeout for sending activity if not set in props', async () => { + const { pageObjects } = await setupWebDriver(); + + const [timeoutForSend] = await pageObjects.runHook('useTimeoutForSend'); + + expect(timeoutForSend).toMatchInlineSnapshot(`20000`); +}); + +test('setter should set the timeout for sending activity', async () => { + const { pageObjects } = await setupWebDriver(); + + await pageObjects.runHook('useTimeoutForSend', [], result => result[1](1000)); + + const [timeoutForSend] = await pageObjects.runHook('useTimeoutForSend'); + + expect(timeoutForSend).toMatchInlineSnapshot(`1000`); +}); diff --git a/__tests__/hooks/useUserID.js b/__tests__/hooks/useUserID.js new file mode 100644 index 0000000000..ec0e521036 --- /dev/null +++ b/__tests__/hooks/useUserID.js @@ -0,0 +1,33 @@ +import { timeouts } from '../constants.json'; + +// selenium-webdriver API doc: +// https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html + +jest.setTimeout(timeouts.test); + +test('getter should return user ID set in props', async () => { + const { pageObjects } = await setupWebDriver({ + props: { + userID: 'u-12345' + } + }); + + const [userID] = await pageObjects.runHook('useUserID'); + + expect(userID).toMatchInlineSnapshot(`"u-12345"`); +}); + +test('getter should return empty string if not set in props', async () => { + const { pageObjects } = await setupWebDriver(); + + const [userID] = await pageObjects.runHook('useUserID'); + + expect(userID).toMatchInlineSnapshot(`""`); +}); + +test('setter should be falsy', async () => { + const { pageObjects } = await setupWebDriver(); + const [_, setUserID] = await pageObjects.runHook('useUserID'); + + expect(setUserID).toBeFalsy(); +}); diff --git a/__tests__/hooks/useUsername.js b/__tests__/hooks/useUsername.js new file mode 100644 index 0000000000..0cde8b289e --- /dev/null +++ b/__tests__/hooks/useUsername.js @@ -0,0 +1,33 @@ +import { timeouts } from '../constants.json'; + +// selenium-webdriver API doc: +// https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html + +jest.setTimeout(timeouts.test); + +test('getter should return username set in props', async () => { + const { pageObjects } = await setupWebDriver({ + props: { + username: 'u-12345' + } + }); + + const [username] = await pageObjects.runHook('useUsername'); + + expect(username).toMatchInlineSnapshot(`"u-12345"`); +}); + +test('getter should return undefined if not set in props', async () => { + const { pageObjects } = await setupWebDriver(); + + const [username] = await pageObjects.runHook('useUsername'); + + expect(username).toMatchInlineSnapshot(`"Happy Web Chat user"`); +}); + +test('setter should be falsy', async () => { + const { pageObjects } = await setupWebDriver(); + const [_, setUsername] = await pageObjects.runHook('useUsername'); + + expect(setUsername).toBeFalsy(); +}); diff --git a/__tests__/setup/conditions/suggestedActionsShown.js b/__tests__/setup/conditions/suggestedActionsShown.js index 7255e12d23..c3090ab740 100644 --- a/__tests__/setup/conditions/suggestedActionsShown.js +++ b/__tests__/setup/conditions/suggestedActionsShown.js @@ -1,5 +1,5 @@ import { By, until } from 'selenium-webdriver'; export default function suggestedActionsShown() { - return until.elementLocated(By.css('[role="form"] ul')); + return until.elementLocated(By.css('[role="form"] > :not(.main) button')); } diff --git a/__tests__/suggestedActions.js b/__tests__/suggestedActions.js index 14b1fd5a9e..3b67b9fb89 100644 --- a/__tests__/suggestedActions.js +++ b/__tests__/suggestedActions.js @@ -28,6 +28,22 @@ describe('suggested-actions command', () => { expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); }); + test('should show correctly formatted buttons when suggested actions are displayed as stacked', async () => { + const { driver, pageObjects } = await setupWebDriver({ + props: { styleOptions: { suggestedActionLayout: 'stacked' } } + }); + + await driver.wait(uiConnected(), timeouts.directLine); + await pageObjects.sendMessageViaSendBox('suggested-actions', { waitForSend: true }); + + await driver.wait(suggestedActionsShown(), timeouts.directLine); + await driver.wait(allImagesLoaded(), 2000); + + const base64PNG = await driver.takeScreenshot(); + + expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); + }); + test('should show response from bot and no text from user on imback', async () => { const { driver, pageObjects } = await setupWebDriver(); @@ -174,4 +190,23 @@ describe('suggested-actions command', () => { expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); }); + + test('should show suggested actions with larger images as stacked', async () => { + const styleOptions = { + suggestedActionHeight: 80, + suggestedActionImageHeight: 60, + suggestedActionLayout: 'stacked' + }; + const { driver, pageObjects } = await setupWebDriver({ props: { styleOptions } }); + + await driver.wait(uiConnected(), timeouts.directLine); + await pageObjects.sendMessageViaSendBox('emptycard', { waitForSend: true }); + + await driver.wait(suggestedActionsShown(), timeouts.directLine); + await driver.wait(allImagesLoaded(), 2000); + + const base64PNG = await driver.takeScreenshot(); + + expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); + }); }); diff --git a/packages/component/src/BasicTranscript.js b/packages/component/src/BasicTranscript.js index b2416204bb..8d6ed31acd 100644 --- a/packages/component/src/BasicTranscript.js +++ b/packages/component/src/BasicTranscript.js @@ -9,6 +9,7 @@ import connectToWebChat from './connectToWebChat'; import ScrollToEndButton from './Activity/ScrollToEndButton'; import SpeakActivity from './Activity/Speak'; import useActivities from './hooks/useActivities'; +import useGroupTimestamp from './hooks/useGroupTimestamp'; import useStyleOptions from './hooks/useStyleOptions'; import useStyleSet from './hooks/useStyleSet'; @@ -59,10 +60,11 @@ function sameTimestampGroup(activityX, activityY, groupTimestamp) { return false; } -const BasicTranscript = ({ activityRenderer, attachmentRenderer, className, groupTimestamp, webSpeechPonyfill }) => { - const [activities] = useActivities(); +const BasicTranscript = ({ activityRenderer, attachmentRenderer, className, webSpeechPonyfill }) => { const [{ activities: activitiesStyleSet, activity: activityStyleSet }] = useStyleSet(); const [{ hideScrollToEndButton }] = useStyleOptions(); + const [activities] = useActivities(); + const [groupTimestamp] = useGroupTimestamp(); const { speechSynthesis, SpeechSynthesisUtterance } = webSpeechPonyfill || {}; @@ -131,7 +133,6 @@ const BasicTranscript = ({ activityRenderer, attachmentRenderer, className, grou BasicTranscript.defaultProps = { className: '', - groupTimestamp: true, webSpeechPonyfill: undefined }; @@ -139,16 +140,14 @@ BasicTranscript.propTypes = { activityRenderer: PropTypes.func.isRequired, attachmentRenderer: PropTypes.func.isRequired, className: PropTypes.string, - groupTimestamp: PropTypes.oneOfType([PropTypes.bool.isRequired, PropTypes.number.isRequired]), webSpeechPonyfill: PropTypes.shape({ speechSynthesis: PropTypes.any, SpeechSynthesisUtterance: PropTypes.any }) }; -export default connectToWebChat(({ activityRenderer, attachmentRenderer, groupTimestamp, webSpeechPonyfill }) => ({ +export default connectToWebChat(({ activityRenderer, attachmentRenderer, webSpeechPonyfill }) => ({ activityRenderer, attachmentRenderer, - groupTimestamp, webSpeechPonyfill }))(BasicTranscript); diff --git a/packages/component/src/SendBox/ConnectivityStatus.js b/packages/component/src/SendBox/ConnectivityStatus.js index 1b95cddc15..9af3d6c882 100644 --- a/packages/component/src/SendBox/ConnectivityStatus.js +++ b/packages/component/src/SendBox/ConnectivityStatus.js @@ -5,6 +5,7 @@ import connectToWebChat from '../connectToWebChat'; import ErrorNotificationIcon from '../Attachment/Assets/ErrorNotificationIcon'; import ScreenReaderText from '../ScreenReaderText'; import SpinnerAnimation from '../Attachment/Assets/SpinnerAnimation'; +import useConnectivityStatus from '../hooks/useConnectivityStatus'; import useLocalize from '../hooks/useLocalize'; import useStyleSet from '../hooks/useStyleSet'; import WarningNotificationIcon from '../Attachment/Assets/WarningNotificationIcon'; @@ -51,7 +52,8 @@ DebouncedConnectivityStatus.propTypes = { const connectConnectivityStatus = (...selectors) => connectToWebChat(({ connectivityStatus, language }) => ({ connectivityStatus, language }), ...selectors); -const ConnectivityStatus = ({ connectivityStatus }) => { +const ConnectivityStatus = () => { + const [connectivityStatus] = useConnectivityStatus(); const [ { connectivityNotification: connectivityNotificationStyleSet, @@ -175,8 +177,6 @@ const ConnectivityStatus = ({ connectivityStatus }) => { ); }; -ConnectivityStatus.propTypes = { - connectivityStatus: PropTypes.string.isRequired -}; +export default ConnectivityStatus; -export default connectConnectivityStatus()(ConnectivityStatus); +export { connectConnectivityStatus }; diff --git a/packages/component/src/SendBox/SuggestedAction.js b/packages/component/src/SendBox/SuggestedAction.js index 4e0d42f700..259b705d54 100644 --- a/packages/component/src/SendBox/SuggestedAction.js +++ b/packages/component/src/SendBox/SuggestedAction.js @@ -11,11 +11,13 @@ import useStyleSet from '../hooks/useStyleSet'; import useSuggestedActions from '../hooks/useSuggestedActions'; const SUGGESTED_ACTION_CSS = css({ - display: 'inline-block', + display: 'flex', + flexDirection: 'column', whiteSpace: 'initial', '& > button': { - display: 'flex' + display: 'flex', + overflow: 'hidden' } }); diff --git a/packages/component/src/SendBox/SuggestedActions.js b/packages/component/src/SendBox/SuggestedActions.js index 5a2f82c5c4..3138fcc5b2 100644 --- a/packages/component/src/SendBox/SuggestedActions.js +++ b/packages/component/src/SendBox/SuggestedActions.js @@ -1,5 +1,6 @@ /* eslint react/no-array-index-key: "off" */ +import { css } from 'glamor'; import BasicFilm from 'react-film'; import classNames from 'classnames'; import PropTypes from 'prop-types'; @@ -12,6 +13,11 @@ import useLocalize from '../hooks/useLocalize'; import useStyleOptions from '../hooks/useStyleOptions'; import useStyleSet from '../hooks/useStyleSet'; +const SUGGESTED_ACTION_STACKED_CSS = css({ + display: 'flex', + flexDirection: 'column' +}); + function suggestedActionText({ displayText, title, type, value }) { if (type === 'messageBack') { return title || displayText; @@ -35,37 +41,52 @@ const connectSuggestedActions = (...selectors) => const SuggestedActions = ({ className, suggestedActions = [] }) => { const [{ suggestedActions: suggestedActionsStyleSet }] = useStyleSet(); - const [{ suggestedActionsStyleSet: suggestedActionsStyleSetForReactFilm }] = useStyleOptions(); + const [{ suggestedActionLayout, suggestedActionsStyleSet: suggestedActionsStyleSetForReactFilm }] = useStyleOptions(); const suggestedActionsContentText = useLocalize('SuggestedActionsContent'); const suggestedActionsEmptyText = useLocalize('SuggestedActionsEmpty'); const suggestedActionsContainerText = useLocalize('SuggestedActionsContainer') + (suggestedActions.length ? suggestedActionsContentText : suggestedActionsEmptyText); + if (!suggestedActions.length) { + return false; + } + + const children = suggestedActions.map(({ displayText, image, text, title, type, value }, index) => ( + + )); + + if (suggestedActionLayout === 'stacked') { + return ( +
+ +
+ {children} +
+
+ ); + } + return (
- {!!suggestedActions.length && ( - - {suggestedActions.map(({ displayText, image, text, title, type, value }, index) => ( - - ))} - - )} + + {children} +
); }; diff --git a/packages/component/src/Styles/StyleSet/SuggestedAction.js b/packages/component/src/Styles/StyleSet/SuggestedAction.js index 6dc4706381..ad75589c4b 100644 --- a/packages/component/src/Styles/StyleSet/SuggestedAction.js +++ b/packages/component/src/Styles/StyleSet/SuggestedAction.js @@ -21,10 +21,10 @@ export default function createSuggestedActionStyle({ subtle }) { return { - paddingBottom: paddingRegular, + paddingBottom: paddingRegular / 2, paddingLeft: paddingRegular / 2, paddingRight: paddingRegular / 2, - paddingTop: paddingRegular, + paddingTop: paddingRegular / 2, '& > button': { alignItems: 'center', @@ -32,6 +32,7 @@ export default function createSuggestedActionStyle({ fontFamily: primaryFont, fontSize: 'inherit', height: suggestedActionHeight, + justifyContent: 'center', paddingLeft: paddingWide, paddingRight: paddingWide, @@ -54,6 +55,11 @@ export default function createSuggestedActionStyle({ '& > img': { height: suggestedActionImageHeight, paddingRight: paddingRegular + }, + + '& > nobr': { + overflow: 'hidden', + textOverflow: 'ellipsis' } } }; diff --git a/packages/component/src/Styles/StyleSet/SuggestedActions.js b/packages/component/src/Styles/StyleSet/SuggestedActions.js index bb5ad7eef1..c963bbf33b 100644 --- a/packages/component/src/Styles/StyleSet/SuggestedActions.js +++ b/packages/component/src/Styles/StyleSet/SuggestedActions.js @@ -1,5 +1,18 @@ /* eslint no-empty-pattern: "off" */ +/* eslint no-magic-numbers: ["error", { "ignore": [2] }] */ -export default function createSuggestedActionsStyle({}) { - return {}; +export default function createSuggestedActionsStyle({ paddingRegular, suggestedActionLayout }) { + if (suggestedActionLayout === 'stacked') { + return { + paddingBottom: paddingRegular / 2, + paddingLeft: paddingRegular / 2, + paddingRight: paddingRegular / 2, + paddingTop: paddingRegular / 2 + }; + } + + return { + paddingBottom: paddingRegular / 2, + paddingTop: paddingRegular / 2 + }; } diff --git a/packages/component/src/Styles/defaultStyleOptions.js b/packages/component/src/Styles/defaultStyleOptions.js index 7cda667d48..1816a911d0 100644 --- a/packages/component/src/Styles/defaultStyleOptions.js +++ b/packages/component/src/Styles/defaultStyleOptions.js @@ -98,11 +98,9 @@ const DEFAULT_OPTIONS = { suggestedActionBackground: 'White', suggestedActionBorder: undefined, // split into 3, null suggestedActionBorderColor: undefined, // defaults to accent + suggestedActionBorderRadius: 0, suggestedActionBorderStyle: 'solid', suggestedActionBorderWidth: 2, - suggestedActionBorderRadius: 0, - suggestedActionImageHeight: 20, - suggestedActionTextColor: null, suggestedActionDisabledBackground: undefined, // defaults to suggestedActionBackground suggestedActionDisabledBorder: null, suggestedActionDisabledBorderColor: '#E6E6E6', @@ -110,6 +108,9 @@ const DEFAULT_OPTIONS = { suggestedActionDisabledBorderWidth: 2, suggestedActionDisabledTextColor: undefined, // defaults to subtle suggestedActionHeight: 40, + suggestedActionImageHeight: 20, + suggestedActionLayout: 'carousel', // either "carousel" or "stacked" + suggestedActionTextColor: null, // Timestamp timestampColor: undefined, // defaults to subtle diff --git a/packages/component/src/hooks/index.js b/packages/component/src/hooks/index.js index e5ef0d5ec8..c93e83001b 100644 --- a/packages/component/src/hooks/index.js +++ b/packages/component/src/hooks/index.js @@ -9,6 +9,8 @@ import useLastTypingAt from './useLastTypingAt'; import useLocalize from './useLocalize'; import useLocalizeDate from './useLocalizeDate'; import usePerformCardAction from './usePerformCardAction'; +import useConnectivityStatus from './useConnectivityStatus'; +import useGroupTimestamp from './useGroupTimestamp'; import usePostActivity from './usePostActivity'; import useReferenceGrammarID from './useReferenceGrammarID'; import useRenderMarkdownAsHTML from './useRenderMarkdownAsHTML'; @@ -24,6 +26,9 @@ import useStyleOptions from './useStyleOptions'; import useStyleSet from './useStyleSet'; import useSubmitSendBox from './useSubmitSendBox'; import useSuggestedActions from './useSuggestedActions'; +import useTimeoutForSend from './useTimeoutForSend'; +import useUserID from './useUserID'; +import useUsername from './useUsername'; import { useSendBoxDictationStarted } from '../BasicSendBox'; import { useTextBoxSubmit } from '../SendBox/TextBox'; @@ -36,6 +41,8 @@ export { useDisabled, useEmitTypingIndicator, useFocusSendBox, + useConnectivityStatus, + useGroupTimestamp, useLanguage, useLastTypingAt, useLocalize, @@ -58,5 +65,8 @@ export { useSubmitSendBox, useSuggestedActions, useTextBoxSubmit, - useTypingIndicatorVisible + useTimeoutForSend, + useTypingIndicatorVisible, + useUserID, + useUsername }; diff --git a/packages/component/src/hooks/useConnectivityStatus.js b/packages/component/src/hooks/useConnectivityStatus.js new file mode 100644 index 0000000000..4cbd5f19d2 --- /dev/null +++ b/packages/component/src/hooks/useConnectivityStatus.js @@ -0,0 +1,5 @@ +import { useSelector } from '../WebChatReduxContext'; + +export default function useConnectivityStatus() { + return [useSelector(({ connectivityStatus }) => connectivityStatus)]; +} diff --git a/packages/component/src/hooks/useGroupTimestamp.js b/packages/component/src/hooks/useGroupTimestamp.js new file mode 100644 index 0000000000..f2b8307ea1 --- /dev/null +++ b/packages/component/src/hooks/useGroupTimestamp.js @@ -0,0 +1,7 @@ +import { useContext } from 'react'; + +import WebChatUIContext from '../WebChatUIContext'; + +export default function useGroupTimestamp() { + return [useContext(WebChatUIContext).groupTimestamp]; +} diff --git a/packages/component/src/hooks/useTimeoutForSend.js b/packages/component/src/hooks/useTimeoutForSend.js new file mode 100644 index 0000000000..0b0c811452 --- /dev/null +++ b/packages/component/src/hooks/useTimeoutForSend.js @@ -0,0 +1,8 @@ +import { useContext } from 'react'; + +import { useSelector } from '../WebChatReduxContext'; +import WebChatUIContext from '../WebChatUIContext'; + +export default function useTimeoutForSend() { + return [useSelector(({ sendTimeout }) => sendTimeout), useContext(WebChatUIContext).setSendTimeout]; +} diff --git a/packages/component/src/hooks/useUserID.js b/packages/component/src/hooks/useUserID.js new file mode 100644 index 0000000000..ee2d84261c --- /dev/null +++ b/packages/component/src/hooks/useUserID.js @@ -0,0 +1,7 @@ +import { useContext } from 'react'; + +import WebChatUIContext from '../WebChatUIContext'; + +export default function useUserID() { + return [useContext(WebChatUIContext).userID]; +} diff --git a/packages/component/src/hooks/useUsername.js b/packages/component/src/hooks/useUsername.js new file mode 100644 index 0000000000..9033a5264b --- /dev/null +++ b/packages/component/src/hooks/useUsername.js @@ -0,0 +1,7 @@ +import { useContext } from 'react'; + +import WebChatUIContext from '../WebChatUIContext'; + +export default function useUsername() { + return [useContext(WebChatUIContext).username]; +}