Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: show large suggestion menu only if there is space #28927

Merged
merged 9 commits into from
Oct 12, 2023
2 changes: 2 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,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,
Expand Down
20 changes: 19 additions & 1 deletion src/libs/SuggestionUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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};
5 changes: 4 additions & 1 deletion src/pages/home/ReportScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -351,7 +352,8 @@ function ReportScreen({
}
}, [report, didSubscribeToReportLeavingEvents, reportID]);

const onListLayout = useCallback(() => {
const onListLayout = useCallback((e) => {
setListHeight((prev) => lodashGet(e, 'nativeEvent.layout.height', prev));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is the most interesting part that wasn't present in the proposal

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. There are some places where we also show a Pay With Expensify button in the header. This causes the height of the header to be different from what it is normally. To cater for this edge case, we need to get header height accurately.

if (!markReadyForHydration) {
return;
}
Expand Down Expand Up @@ -438,6 +440,7 @@ function ReportScreen({
isComposerFullSize={isComposerFullSize}
onSubmitComment={onSubmitComment}
policies={policies}
listHeight={listHeight}
/>
) : (
<ReportFooter isReportReadyForDisplay={false} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -93,6 +94,7 @@ function ComposerWithSuggestions({
handleSendMessage,
shouldShowComposeInput,
measureParentContainer,
listHeight,
// Refs
suggestionsRef,
animatedRef,
Expand Down Expand Up @@ -127,6 +129,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
Expand Down Expand Up @@ -552,6 +559,7 @@ function ComposerWithSuggestions({
composerHeight={composerHeight}
onInsertedEmoji={onInsertedEmoji}
measureParentContainer={measureParentContainer}
isAutoSuggestionPickerLarge={isAutoSuggestionPickerLarge}
// Input
value={value}
setValue={setValue}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -86,6 +89,7 @@ const defaultProps = {
isComposerFullSize: false,
pendingAction: null,
shouldShowComposeInput: true,
listHeight: 0,
isReportReadyForDisplay: true,
...withCurrentUserPersonalDetailsDefaultProps,
};
Expand All @@ -108,6 +112,7 @@ function ReportActionCompose({
report,
reportID,
reportActions,
listHeight,
shouldShowComposeInput,
isReportReadyForDisplay,
}) {
Expand Down Expand Up @@ -408,6 +413,7 @@ function ReportActionCompose({
onFocus={onFocus}
onBlur={onBlur}
measureParentContainer={measureContainer}
listHeight={listHeight}
/>
<ReportDropUI
onDrop={(e) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ function SuggestionEmoji({
[shouldBlockCalc],
);

const getSuggestions = useCallback(() => suggestionValues.suggestedEmojis, [suggestionValues]);

useImperativeHandle(
forwardedRef,
() => ({
Expand All @@ -213,8 +215,9 @@ function SuggestionEmoji({
triggerHotkeyActions,
setShouldBlockSuggestionCalc,
updateShouldShowSuggestionMenuToFalse,
getSuggestions,
}),
[onSelectionChange, resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse],
[onSelectionChange, resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, getSuggestions],
);

if (!isEmojiSuggestionsMenuVisible) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,15 +256,18 @@ function SuggestionMention({
setSuggestionValues((prevState) => ({...prevState, suggestedMentions: []}));
}, []);

const getSuggestions = useCallback(() => suggestionValues.suggestedMentions, [suggestionValues]);

useImperativeHandle(
forwardedRef,
() => ({
resetSuggestions,
triggerHotkeyActions,
setShouldBlockSuggestionCalc,
updateShouldShowSuggestionMenuToFalse,
getSuggestions,
}),
[resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse],
[resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, getSuggestions],
);

if (!isMentionSuggestionsMenuVisible) {
Expand Down
17 changes: 9 additions & 8 deletions src/pages/home/report/ReportActionCompose/Suggestions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -15,11 +14,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,
};

/**
Expand All @@ -40,10 +43,13 @@ function Suggestions({
onInsertedEmoji,
resetKeyboardInput,
measureParentContainer,
isAutoSuggestionPickerLarge,
}) {
const suggestionEmojiRef = useRef(null);
const suggestionMentionRef = useRef(null);

const getSuggestions = useCallback(() => suggestionEmojiRef.current.getSuggestions() || suggestionMentionRef.current.getSuggestions(), []);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This caused a regression as null handling is missing:

#31027 (comment)


/**
* Clean data related to EmojiSuggestions
*/
Expand Down Expand Up @@ -86,16 +92,11 @@ function Suggestions({
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,
Expand Down
5 changes: 5 additions & 0 deletions src/pages/home/report/ReportFooter.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,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,

Expand All @@ -51,6 +54,7 @@ const defaultProps = {
pendingAction: null,
shouldShowComposeInput: true,
shouldDisableCompose: false,
listHeight: 0,
isReportReadyForDisplay: true,
};

Expand Down Expand Up @@ -90,6 +94,7 @@ function ReportFooter(props) {
pendingAction={props.pendingAction}
isComposerFullSize={props.isComposerFullSize}
disabled={props.shouldDisableCompose}
listHeight={props.listHeight}
isReportReadyForDisplay={props.isReportReadyForDisplay}
/>
</SwipeableView>
Expand Down
6 changes: 3 additions & 3 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -817,9 +817,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: {
Expand Down
Loading