diff --git a/src/CONST.ts b/src/CONST.ts index 58fdea7654cb..b9141c9c0d4b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -904,6 +904,8 @@ const CONST = { HERE_TEXT: '@here', }, COMPOSER_MAX_HEIGHT: 125, + CHAT_FOOTER_SECONDARY_ROW_HEIGHT: 15, + CHAT_FOOTER_SECONDARY_ROW_PADDING: 5, CHAT_FOOTER_MIN_HEIGHT: 65, CHAT_SKELETON_VIEW: { AVERAGE_ROW_HEIGHT: 80, diff --git a/src/libs/SuggestionUtils.js b/src/libs/SuggestionUtils.js index aa2640d006c8..9c3e92799334 100644 --- a/src/libs/SuggestionUtils.js +++ b/src/libs/SuggestionUtils.js @@ -26,4 +26,22 @@ function trimLeadingSpace(str) { return str.slice(0, 1) === ' ' ? str.slice(1) : str; } -export {getMaxArrowIndex, trimLeadingSpace}; +/** + * Checks if space is available to render large suggestion menu + * @param {Number} listHeight + * @param {Number} composerHeight + * @param {Number} totalSuggestions + * @returns {Boolean} + */ +function hasEnoughSpaceForLargeSuggestionMenu(listHeight, composerHeight, totalSuggestions) { + const maxSuggestions = CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_VISIBLE_SUGGESTIONS_IN_CONTAINER; + const chatFooterHeight = CONST.CHAT_FOOTER_SECONDARY_ROW_HEIGHT + 2 * CONST.CHAT_FOOTER_SECONDARY_ROW_PADDING; + const availableHeight = listHeight - composerHeight - chatFooterHeight; + const menuHeight = + (!totalSuggestions || totalSuggestions > maxSuggestions ? maxSuggestions : totalSuggestions) * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT + + CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTER_INNER_PADDING * 2; + + return availableHeight > menuHeight; +} + +export {getMaxArrowIndex, trimLeadingSpace, hasEnoughSpaceForLargeSuggestionMenu}; diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 6dba940f0ecb..8a7da6a7930d 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -156,6 +156,7 @@ function ReportScreen({ const prevReport = usePrevious(report); const prevUserLeavingStatus = usePrevious(userLeavingStatus); const [isBannerVisible, setIsBannerVisible] = useState(true); + const [listHeight, setListHeight] = useState(0); const reportID = getReportID(route); const {addWorkspaceRoomOrChatPendingAction, addWorkspaceRoomOrChatErrors} = ReportUtils.getReportOfflinePendingActionAndErrors(report); @@ -351,7 +352,8 @@ function ReportScreen({ } }, [report, didSubscribeToReportLeavingEvents, reportID]); - const onListLayout = useCallback(() => { + const onListLayout = useCallback((e) => { + setListHeight((prev) => lodashGet(e, 'nativeEvent.layout.height', prev)); if (!markReadyForHydration) { return; } @@ -438,6 +440,7 @@ function ReportScreen({ isComposerFullSize={isComposerFullSize} onSubmitComment={onSubmitComment} policies={policies} + listHeight={listHeight} personalDetails={personalDetails} /> ) : ( diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index faa710d2cd6b..80b92423a1b1 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -22,6 +22,7 @@ import usePrevious from '../../../../hooks/usePrevious'; import * as EmojiUtils from '../../../../libs/EmojiUtils'; import * as User from '../../../../libs/actions/User'; import * as ReportUtils from '../../../../libs/ReportUtils'; +import * as SuggestionUtils from '../../../../libs/SuggestionUtils'; import * as ReportActionsUtils from '../../../../libs/ReportActionsUtils'; import canFocusInputOnScreenFocus from '../../../../libs/canFocusInputOnScreenFocus'; import SilentCommentUpdater from './SilentCommentUpdater'; @@ -93,6 +94,7 @@ function ComposerWithSuggestions({ handleSendMessage, shouldShowComposeInput, measureParentContainer, + listHeight, // Refs suggestionsRef, animatedRef, @@ -133,6 +135,11 @@ function ComposerWithSuggestions({ // A flag to indicate whether the onScroll callback is likely triggered by a layout change (caused by text change) or not const isScrollLikelyLayoutTriggered = useRef(false); + const suggestions = lodashGet(suggestionsRef, 'current.getSuggestions', () => [])(); + + const hasEnoughSpaceForLargeSuggestion = SuggestionUtils.hasEnoughSpaceForLargeSuggestionMenu(listHeight, composerHeight, suggestions.length); + + const isAutoSuggestionPickerLarge = !isSmallScreenWidth || (isSmallScreenWidth && hasEnoughSpaceForLargeSuggestion); /** * Update frequently used emojis list. We debounce this method in the constructor so that UpdateFrequentlyUsedEmojis @@ -552,6 +559,7 @@ function ComposerWithSuggestions({ updateComment={updateComment} composerHeight={composerHeight} measureParentContainer={measureParentContainer} + isAutoSuggestionPickerLarge={isAutoSuggestionPickerLarge} // Input value={value} setValue={setValue} diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index b43400287269..dd4d51653546 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -61,6 +61,9 @@ const propTypes = { /** Whether user interactions should be disabled */ disabled: PropTypes.bool, + /** Height of the list which the composer is part of */ + listHeight: PropTypes.number, + // The NVP describing a user's block status blockedFromConcierge: PropTypes.shape({ // The date that the user will be unblocked @@ -86,6 +89,7 @@ const defaultProps = { isComposerFullSize: false, pendingAction: null, shouldShowComposeInput: true, + listHeight: 0, isReportReadyForDisplay: true, ...withCurrentUserPersonalDetailsDefaultProps, }; @@ -108,6 +112,7 @@ function ReportActionCompose({ report, reportID, reportActions, + listHeight, shouldShowComposeInput, isReportReadyForDisplay, }) { @@ -408,6 +413,7 @@ function ReportActionCompose({ onFocus={onFocus} onBlur={onBlur} measureParentContainer={measureContainer} + listHeight={listHeight} /> { diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js index 8ffed01f1068..857b7d5e52c2 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js @@ -199,6 +199,8 @@ function SuggestionEmoji({ [shouldBlockCalc], ); + const getSuggestions = useCallback(() => suggestionValues.suggestedEmojis, [suggestionValues]); + useImperativeHandle( forwardedRef, () => ({ @@ -207,8 +209,9 @@ function SuggestionEmoji({ triggerHotkeyActions, setShouldBlockSuggestionCalc, updateShouldShowSuggestionMenuToFalse, + getSuggestions, }), - [onSelectionChange, resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse], + [onSelectionChange, resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, getSuggestions], ); if (!isEmojiSuggestionsMenuVisible) { diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.js index 6c08b68cdc78..84bee9c80c7f 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.js @@ -256,6 +256,8 @@ function SuggestionMention({ setSuggestionValues((prevState) => ({...prevState, suggestedMentions: []})); }, []); + const getSuggestions = useCallback(() => suggestionValues.suggestedMentions, [suggestionValues]); + useImperativeHandle( forwardedRef, () => ({ @@ -263,8 +265,9 @@ function SuggestionMention({ triggerHotkeyActions, setShouldBlockSuggestionCalc, updateShouldShowSuggestionMenuToFalse, + getSuggestions, }), - [resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse], + [resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, getSuggestions], ); if (!isMentionSuggestionsMenuVisible) { diff --git a/src/pages/home/report/ReportActionCompose/Suggestions.js b/src/pages/home/report/ReportActionCompose/Suggestions.js index 0e98e69d31d1..60c31efb1446 100644 --- a/src/pages/home/report/ReportActionCompose/Suggestions.js +++ b/src/pages/home/report/ReportActionCompose/Suggestions.js @@ -2,7 +2,6 @@ import React, {useRef, useCallback, useImperativeHandle} from 'react'; import PropTypes from 'prop-types'; import SuggestionMention from './SuggestionMention'; import SuggestionEmoji from './SuggestionEmoji'; -import useWindowDimensions from '../../../../hooks/useWindowDimensions'; import * as SuggestionProps from './suggestionProps'; const propTypes = { @@ -12,11 +11,15 @@ const propTypes = { /** Function to clear the input */ resetKeyboardInput: PropTypes.func.isRequired, + /** Is auto suggestion picker large */ + isAutoSuggestionPickerLarge: PropTypes.bool, + ...SuggestionProps.baseProps, }; const defaultProps = { forwardedRef: null, + isAutoSuggestionPickerLarge: true, }; /** @@ -25,10 +28,24 @@ const defaultProps = { * * @returns {React.Component} */ -function Suggestions({isComposerFullSize, value, setValue, selection, setSelection, updateComment, composerHeight, forwardedRef, resetKeyboardInput, measureParentContainer}) { +function Suggestions({ + isComposerFullSize, + value, + setValue, + selection, + setSelection, + updateComment, + composerHeight, + forwardedRef, + resetKeyboardInput, + measureParentContainer, + isAutoSuggestionPickerLarge, +}) { const suggestionEmojiRef = useRef(null); const suggestionMentionRef = useRef(null); + const getSuggestions = useCallback(() => suggestionEmojiRef.current.getSuggestions() || suggestionMentionRef.current.getSuggestions(), []); + /** * Clean data related to EmojiSuggestions */ @@ -71,16 +88,11 @@ function Suggestions({isComposerFullSize, value, setValue, selection, setSelecti triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, setShouldBlockSuggestionCalc, + getSuggestions, }), - [onSelectionChange, resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse], + [onSelectionChange, resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, getSuggestions], ); - const {windowHeight, isSmallScreenWidth} = useWindowDimensions(); - - // the larger composerHeight the less space for EmojiPicker, Pixel 2 has pretty small screen and this value equal 5.3 - const hasEnoughSpaceForLargeSuggestion = windowHeight / composerHeight >= 6.8; - const isAutoSuggestionPickerLarge = !isSmallScreenWidth || (isSmallScreenWidth && hasEnoughSpaceForLargeSuggestion); - const baseProps = { value, setValue, diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js index 0711cd181964..e8132c942854 100644 --- a/src/pages/home/report/ReportFooter.js +++ b/src/pages/home/report/ReportFooter.js @@ -42,6 +42,9 @@ const propTypes = { /** Whether user interactions should be disabled */ shouldDisableCompose: PropTypes.bool, + /** Height of the list which the composer is part of */ + listHeight: PropTypes.number, + /** Whetjer the report is ready for display */ isReportReadyForDisplay: PropTypes.bool, @@ -56,6 +59,7 @@ const defaultProps = { personalDetails: {}, shouldShowComposeInput: true, shouldDisableCompose: false, + listHeight: 0, isReportReadyForDisplay: true, }; @@ -96,6 +100,7 @@ function ReportFooter(props) { pendingAction={props.pendingAction} isComposerFullSize={props.isComposerFullSize} disabled={props.shouldDisableCompose} + listHeight={props.listHeight} isReportReadyForDisplay={props.isReportReadyForDisplay} /> diff --git a/src/styles/styles.js b/src/styles/styles.js index 8fa81cd98b21..c278ffbff335 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -821,9 +821,9 @@ const styles = (theme) => ({ }, chatItemComposeSecondaryRow: { - height: 15, - marginBottom: 5, - marginTop: 5, + height: CONST.CHAT_FOOTER_SECONDARY_ROW_HEIGHT, + marginBottom: CONST.CHAT_FOOTER_SECONDARY_ROW_PADDING, + marginTop: CONST.CHAT_FOOTER_SECONDARY_ROW_PADDING, }, chatItemComposeSecondaryRowSubText: {