Skip to content

Commit

Permalink
Merge pull request #44720 from margelo/perunt/suggestion-box-edit-input
Browse files Browse the repository at this point in the history
Suggestion box for edit input
  • Loading branch information
puneetlath authored Jul 11, 2024
2 parents 9025880 + c213eec commit e7c027a
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 54 deletions.
2 changes: 1 addition & 1 deletion src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1245,7 +1245,7 @@ const CONST = {
MAX_AMOUNT_OF_SUGGESTIONS: 20,
MAX_AMOUNT_OF_VISIBLE_SUGGESTIONS_IN_CONTAINER: 5,
HERE_TEXT: '@here',
SUGGESTION_BOX_MAX_SAFE_DISTANCE: 38,
SUGGESTION_BOX_MAX_SAFE_DISTANCE: 10,
BIG_SCREEN_SUGGESTION_WIDTH: 300,
},
COMPOSER_MAX_HEIGHT: 125,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function getBottomSuggestionPadding(): number {
return 0;
return 6;
}

export default getBottomSuggestionPadding;
51 changes: 30 additions & 21 deletions src/components/AutoCompleteSuggestions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,16 @@ const measureHeightOfSuggestionRows = (numRows: number, canBeBig: boolean): numb
}
return numRows * CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT;
};
function isSuggestionRenderedAbove(isEnoughSpaceAboveForBig: boolean, isEnoughSpaceAboveForSmall: boolean): boolean {
return isEnoughSpaceAboveForBig || isEnoughSpaceAboveForSmall;
function isSuggestionMenuRenderedAbove(isEnoughSpaceAboveForBigMenu: boolean, isEnoughSpaceAboveForSmallMenu: boolean): boolean {
return isEnoughSpaceAboveForBigMenu || isEnoughSpaceAboveForSmallMenu;
}

type IsEnoughSpaceToRenderMenuAboveCursor = Pick<MeasureParentContainerAndCursor, 'y' | 'cursorCoordinates' | 'scrollValue'> & {
contentHeight: number;
topInset: number;
};
function isEnoughSpaceToRenderMenuAboveCursor({y, cursorCoordinates, scrollValue, contentHeight, topInset}: IsEnoughSpaceToRenderMenuAboveCursor): boolean {
return y + (cursorCoordinates.y - scrollValue) > contentHeight + topInset + CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_BOX_MAX_SAFE_DISTANCE;
}

/**
Expand All @@ -35,7 +43,7 @@ function isSuggestionRenderedAbove(isEnoughSpaceAboveForBig: boolean, isEnoughSp
function AutoCompleteSuggestions<TSuggestion>({measureParentContainerAndReportCursor = () => {}, ...props}: AutoCompleteSuggestionsProps<TSuggestion>) {
const containerRef = React.useRef<HTMLDivElement>(null);
const isInitialRender = React.useRef<boolean>(true);
const isSuggestionAboveRef = React.useRef<boolean>(false);
const isSuggestionMenuAboveRef = React.useRef<boolean>(false);
const leftValue = React.useRef<number>(0);
const prevLeftValue = React.useRef<number>(0);
const {windowHeight, windowWidth, isSmallScreenWidth} = useWindowDimensions();
Expand All @@ -44,11 +52,12 @@ function AutoCompleteSuggestions<TSuggestion>({measureParentContainerAndReportCu
width: 0,
left: 0,
bottom: 0,
cursorCoordinates: {x: 0, y: 0},
});
const StyleUtils = useStyleUtils();
const insets = useSafeAreaInsets();
const {keyboardHeight} = useKeyboardState();
const {paddingBottom: bottomInset} = StyleUtils.getSafeAreaPadding(insets ?? undefined);
const {paddingBottom: bottomInset, paddingTop: topInset} = StyleUtils.getSafeAreaPadding(insets ?? undefined);

useEffect(() => {
const container = containerRef.current;
Expand All @@ -73,51 +82,51 @@ function AutoCompleteSuggestions<TSuggestion>({measureParentContainerAndReportCu

measureParentContainerAndReportCursor(({x, y, width, scrollValue, cursorCoordinates}: MeasureParentContainerAndCursor) => {
const xCoordinatesOfCursor = x + cursorCoordinates.x;
const leftValueForBigScreen =
const bigScreenLeftOffset =
xCoordinatesOfCursor + CONST.AUTO_COMPLETE_SUGGESTER.BIG_SCREEN_SUGGESTION_WIDTH > windowWidth
? windowWidth - CONST.AUTO_COMPLETE_SUGGESTER.BIG_SCREEN_SUGGESTION_WIDTH
: xCoordinatesOfCursor;

let bottomValue = windowHeight - y - cursorCoordinates.y + scrollValue - (keyboardHeight || bottomInset);
const widthValue = isSmallScreenWidth ? width : CONST.AUTO_COMPLETE_SUGGESTER.BIG_SCREEN_SUGGESTION_WIDTH;

const contentMaxHeight = measureHeightOfSuggestionRows(suggestionsLength, true);
const contentMinHeight = measureHeightOfSuggestionRows(suggestionsLength, false);
const isEnoughSpaceAboveForBig = windowHeight - bottomValue - contentMaxHeight > CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_BOX_MAX_SAFE_DISTANCE;
const isEnoughSpaceAboveForSmall = windowHeight - bottomValue - contentMinHeight > CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_BOX_MAX_SAFE_DISTANCE;
let bottomValue = windowHeight - (cursorCoordinates.y - scrollValue + y) - keyboardHeight;
const widthValue = isSmallScreenWidth ? width : CONST.AUTO_COMPLETE_SUGGESTER.BIG_SCREEN_SUGGESTION_WIDTH;

const isEnoughSpaceToRenderMenuAboveForBig = isEnoughSpaceToRenderMenuAboveCursor({y, cursorCoordinates, scrollValue, contentHeight: contentMaxHeight, topInset});
const isEnoughSpaceToRenderMenuAboveForSmall = isEnoughSpaceToRenderMenuAboveCursor({y, cursorCoordinates, scrollValue, contentHeight: contentMinHeight, topInset});

const newLeftValue = isSmallScreenWidth ? x : leftValueForBigScreen;
const newLeftOffset = isSmallScreenWidth ? x : bigScreenLeftOffset;
// If the suggested word is longer than 150 (approximately half the width of the suggestion popup), then adjust a new position of popup
const isAdjustmentNeeded = Math.abs(prevLeftValue.current - leftValueForBigScreen) > 150;
const isAdjustmentNeeded = Math.abs(prevLeftValue.current - bigScreenLeftOffset) > 150;
if (isInitialRender.current || isAdjustmentNeeded) {
isSuggestionAboveRef.current = isSuggestionRenderedAbove(isEnoughSpaceAboveForBig, isEnoughSpaceAboveForSmall);
leftValue.current = newLeftValue;
isSuggestionMenuAboveRef.current = isSuggestionMenuRenderedAbove(isEnoughSpaceToRenderMenuAboveForBig, isEnoughSpaceToRenderMenuAboveForSmall);
leftValue.current = newLeftOffset;
isInitialRender.current = false;
prevLeftValue.current = newLeftValue;
prevLeftValue.current = newLeftOffset;
}

let measuredHeight = 0;
if (isSuggestionAboveRef.current && isEnoughSpaceAboveForBig) {
if (isSuggestionMenuAboveRef.current && isEnoughSpaceToRenderMenuAboveForBig) {
// calculation for big suggestion box above the cursor
measuredHeight = measureHeightOfSuggestionRows(suggestionsLength, true);
} else if (isSuggestionAboveRef.current && isEnoughSpaceAboveForSmall) {
} else if (isSuggestionMenuAboveRef.current && isEnoughSpaceToRenderMenuAboveForSmall) {
// calculation for small suggestion box above the cursor
measuredHeight = measureHeightOfSuggestionRows(suggestionsLength, false);
} else {
// calculation for big suggestion box below the cursor
measuredHeight = measureHeightOfSuggestionRows(suggestionsLength, true);
bottomValue = windowHeight - y - cursorCoordinates.y + scrollValue - measuredHeight - CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT;
bottomValue = windowHeight - y - cursorCoordinates.y + scrollValue - measuredHeight - CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT - keyboardHeight;
}
setSuggestionHeight(measuredHeight);
setContainerState({
left: leftValue.current,
bottom: bottomValue,
width: widthValue,
cursorCoordinates,
});
});
}, [measureParentContainerAndReportCursor, windowHeight, windowWidth, keyboardHeight, isSmallScreenWidth, suggestionsLength, bottomInset]);
}, [measureParentContainerAndReportCursor, windowHeight, windowWidth, keyboardHeight, isSmallScreenWidth, suggestionsLength, bottomInset, topInset]);

if (containerState.width === 0 && containerState.left === 0 && containerState.bottom === 0) {
if ((containerState.width === 0 && containerState.left === 0 && containerState.bottom === 0) || (containerState.cursorCoordinates.x === 0 && containerState.cursorCoordinates.y === 0)) {
return null;
}
return (
Expand Down
2 changes: 2 additions & 0 deletions src/libs/focusComposerWithDelay/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type {TextInput} from 'react-native';
type Selection = {
start: number;
end: number;
positionX?: number;
positionY?: number;
};

type FocusComposerWithDelay = (shouldDelay?: boolean, forcedSelectionRange?: Selection) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,8 @@ function ComposerWithSuggestions(
}, []);

useEffect(() => {
// We use the tag to store the native ID of the text input. Later, we use it in onSelectionChange to pick up the proper text input data.

tag.value = findNodeHandle(textInputRef.current) ?? -1;
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, []);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type SuggestionsRef = {
updateShouldShowSuggestionMenuToFalse: (shouldShowSuggestionMenu?: boolean) => void;
setShouldBlockSuggestionCalc: (shouldBlock: boolean) => void;
getSuggestions: () => Mention[] | Emoji[];
getIsSuggestionsMenuVisible: () => boolean;
};

type ReportActionComposeOnyxProps = {
Expand Down
8 changes: 6 additions & 2 deletions src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ function SuggestionEmoji(
*/
const calculateEmojiSuggestion = useCallback(
(selectionStart?: number, selectionEnd?: number) => {
if (selectionStart !== selectionEnd || !selectionEnd || shouldBlockCalc.current || !value) {
if (selectionStart !== selectionEnd || !selectionEnd || shouldBlockCalc.current || !value || (selectionStart === 0 && selectionEnd === 0)) {
shouldBlockCalc.current = false;
resetSuggestions();
return;
Expand Down Expand Up @@ -181,6 +181,7 @@ function SuggestionEmoji(
if (!isComposerFocused) {
return;
}

calculateEmojiSuggestion(selection.start, selection.end);
}, [selection, calculateEmojiSuggestion, isComposerFocused]);

Expand All @@ -193,6 +194,8 @@ function SuggestionEmoji(

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

const getIsSuggestionsMenuVisible = useCallback(() => isEmojiSuggestionsMenuVisible, [isEmojiSuggestionsMenuVisible]);

useImperativeHandle(
ref,
() => ({
Expand All @@ -201,8 +204,9 @@ function SuggestionEmoji(
setShouldBlockSuggestionCalc,
updateShouldShowSuggestionMenuToFalse,
getSuggestions,
getIsSuggestionsMenuVisible,
}),
[resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, getSuggestions],
[resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, getSuggestions, getIsSuggestionsMenuVisible],
);

if (!isEmojiSuggestionsMenuVisible) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ function SuggestionMention(
);

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

useImperativeHandle(
ref,
Expand All @@ -417,8 +418,9 @@ function SuggestionMention(
setShouldBlockSuggestionCalc,
updateShouldShowSuggestionMenuToFalse,
getSuggestions,
getIsSuggestionsMenuVisible,
}),
[resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, getSuggestions],
[resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, getSuggestions, getIsSuggestionsMenuVisible],
);

if (!isMentionSuggestionsMenuVisible) {
Expand Down
8 changes: 7 additions & 1 deletion src/pages/home/report/ReportActionCompose/Suggestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ function Suggestions(
suggestionEmojiRef.current?.setShouldBlockSuggestionCalc(shouldBlock);
suggestionMentionRef.current?.setShouldBlockSuggestionCalc(shouldBlock);
}, []);
const getIsSuggestionsMenuVisible = useCallback((): boolean => {
const isEmojiVisible = suggestionEmojiRef.current?.getIsSuggestionsMenuVisible() ?? false;
const isSuggestionVisible = suggestionMentionRef.current?.getIsSuggestionsMenuVisible() ?? false;
return isEmojiVisible || isSuggestionVisible;
}, []);

useImperativeHandle(
ref,
Expand All @@ -134,8 +139,9 @@ function Suggestions(
updateShouldShowSuggestionMenuToFalse,
setShouldBlockSuggestionCalc,
getSuggestions,
getIsSuggestionsMenuVisible,
}),
[onSelectionChange, resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, getSuggestions],
[onSelectionChange, resetSuggestions, setShouldBlockSuggestionCalc, triggerHotkeyActions, updateShouldShowSuggestionMenuToFalse, getSuggestions, getIsSuggestionsMenuVisible],
);

useEffect(() => {
Expand Down
Loading

0 comments on commit e7c027a

Please sign in to comment.