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 jumping composer when entering emojis or markdown text #40128

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -868,9 +868,8 @@ const CONST = {
MAX_LINES: 16,
MAX_LINES_SMALL_SCREEN: 6,
MAX_LINES_FULL: -1,

// The minimum number of typed lines needed to enable the full screen composer
FULL_COMPOSER_MIN_LINES: 3,
// The minimum height needed to enable the full screen composer
FULL_COMPOSER_MIN_HEIGHT: 60,
},
MODAL: {
MODAL_TYPE: {
Expand Down
2 changes: 0 additions & 2 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,6 @@ const ONYXKEYS = {
REPORT_ACTIONS_DRAFTS: 'reportActionsDrafts_',
REPORT_ACTIONS_REACTIONS: 'reportActionsReactions_',
REPORT_DRAFT_COMMENT: 'reportDraftComment_',
REPORT_DRAFT_COMMENT_NUMBER_OF_LINES: 'reportDraftCommentNumberOfLines_',
REPORT_IS_COMPOSER_FULL_SIZE: 'reportIsComposerFullSize_',
REPORT_USER_IS_TYPING: 'reportUserIsTyping_',
REPORT_USER_IS_LEAVING_ROOM: 'reportUserIsLeavingRoom_',
Expand Down Expand Up @@ -548,7 +547,6 @@ type OnyxCollectionValuesMapping = {
[ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS]: OnyxTypes.ReportActionsDrafts;
[ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS]: OnyxTypes.ReportActionReactions;
[ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT]: string;
[ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES]: number;
[ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE]: boolean;
[ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING]: OnyxTypes.ReportUserIsTyping;
[ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM]: boolean;
Expand Down
6 changes: 2 additions & 4 deletions src/components/Composer/index.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import useResetComposerFocus from '@hooks/useResetComposerFocus';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ComposerUtils from '@libs/ComposerUtils';
import updateIsFullComposerAvailable from '@libs/ComposerUtils/updateIsFullComposerAvailable';
import type {ComposerProps} from './types';

function Composer(
Expand All @@ -21,7 +21,6 @@ function Composer(
isComposerFullSize = false,
setIsFullComposerAvailable = () => {},
autoFocus = false,
isFullComposerAvailable = false,
style,
// On native layers we like to have the Text Input not focused so the
// user can read new chats without the keyboard in the way of the view.
Expand Down Expand Up @@ -75,14 +74,13 @@ function Composer(
placeholderTextColor={theme.placeholderText}
ref={setTextInputRef}
value={value}
onContentSizeChange={(e) => ComposerUtils.updateNumberOfLines({maxLines, isComposerFullSize, isDisabled, setIsFullComposerAvailable}, e, styles)}
onContentSizeChange={(e) => updateIsFullComposerAvailable({maxLines, isComposerFullSize, isDisabled, setIsFullComposerAvailable}, e, styles, true)}
rejectResponderTermination={false}
smartInsertDelete={false}
textAlignVertical="center"
style={[composerStyle, maxHeightStyle]}
markdownStyle={markdownStyle}
autoFocus={autoFocus}
isFullComposerAvailable={isFullComposerAvailable}
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...props}
readOnly={isDisabled}
Expand Down
92 changes: 19 additions & 73 deletions src/components/Composer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ import useMarkdownStyle from '@hooks/useMarkdownStyle';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Browser from '@libs/Browser';
import * as ComposerUtils from '@libs/ComposerUtils';
import updateIsFullComposerAvailable from '@libs/ComposerUtils/updateIsFullComposerAvailable';
import * as FileUtils from '@libs/fileDownload/FileUtils';
import isEnterWhileComposition from '@libs/KeyboardShortcut/isEnterWhileComposition';
Expand Down Expand Up @@ -58,14 +56,11 @@ function Composer(
style,
shouldClear = false,
autoFocus = false,
isFullComposerAvailable = false,
shouldCalculateCaretPosition = false,
numberOfLines: numberOfLinesProp = 0,
isDisabled = false,
onClear = () => {},
onPasteFile = () => {},
onSelectionChange = () => {},
onNumberOfLinesChange = () => {},
setIsFullComposerAvailable = () => {},
checkComposerVisibility = () => false,
selection: selectionProp = {
Expand All @@ -83,10 +78,8 @@ function Composer(
const styles = useThemeStyles();
const markdownStyle = useMarkdownStyle(value);
const StyleUtils = useStyleUtils();
const {windowWidth} = useWindowDimensions();
const textRef = useRef<HTMLElement & RNText>(null);
const textInput = useRef<AnimatedMarkdownTextInputRef | null>(null);
const [numberOfLines, setNumberOfLines] = useState(numberOfLinesProp);
const [selection, setSelection] = useState<
| {
start: number;
Expand All @@ -109,7 +102,6 @@ function Composer(
return;
}
textInput.current?.clear();
setNumberOfLines(1);
onClear();
}, [shouldClear, onClear]);

Expand All @@ -126,12 +118,8 @@ function Composer(
* Adds the cursor position to the selection change event.
*/
const addCursorPositionToSelectionChange = (event: NativeSyntheticEvent<TextInputSelectionChangeEventData>) => {
if (!isRendered) {
return;
}
const webEvent = event as BaseSyntheticEvent<TextInputSelectionChangeEventData>;

if (shouldCalculateCaretPosition) {
if (shouldCalculateCaretPosition && isRendered) {
// we do flushSync to make sure that the valueBeforeCaret is updated before we calculate the caret position to receive a proper position otherwise we will calculate position for the previous state
flushSync(() => {
setValueBeforeCaret(webEvent.target.value.slice(0, webEvent.nativeEvent.selection.start));
Expand Down Expand Up @@ -236,41 +224,6 @@ function Composer(
[onPasteFile, checkComposerVisibility],
);

/**
* Check the current scrollHeight of the textarea (minus any padding) and
* divide by line height to get the total number of rows for the textarea.
*/
const updateNumberOfLines = useCallback(() => {
if (!textInput.current) {
return;
}
// we reset the height to 0 to get the correct scrollHeight
textInput.current.style.height = '0';
const computedStyle = window.getComputedStyle(textInput.current);
const lineHeight = parseInt(computedStyle.lineHeight, 10) || 20;
const paddingTopAndBottom = parseInt(computedStyle.paddingBottom, 10) + parseInt(computedStyle.paddingTop, 10);
setTextInputWidth(computedStyle.width);

const computedNumberOfLines = ComposerUtils.getNumberOfLines(lineHeight, paddingTopAndBottom, textInput.current.scrollHeight, maxLines);
const generalNumberOfLines = computedNumberOfLines === 0 ? numberOfLinesProp : computedNumberOfLines;

onNumberOfLinesChange(generalNumberOfLines);
updateIsFullComposerAvailable({isFullComposerAvailable, setIsFullComposerAvailable}, generalNumberOfLines);
setNumberOfLines(generalNumberOfLines);
textInput.current.style.height = 'auto';
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value, maxLines, numberOfLinesProp, onNumberOfLinesChange, isFullComposerAvailable, setIsFullComposerAvailable, windowWidth]);

useEffect(() => {
updateNumberOfLines();
}, [updateNumberOfLines]);

const currentNumberOfLines = useMemo(
() => (isComposerFullSize ? undefined : numberOfLines),

[isComposerFullSize, numberOfLines],
);

useEffect(() => {
if (!textInput.current) {
return;
Expand Down Expand Up @@ -333,7 +286,7 @@ function Composer(
opacity: 0,
}}
>
<Text style={[StyleSheet.flatten([style, styles.noSelect]), numberOfLines < maxLines ? styles.overflowHidden : {}, {maxWidth: textInputWidth as DimensionValue}]}>
<Text style={[StyleSheet.flatten([style, styles.noSelect]), StyleUtils.getComposerMaxHeightStyle(maxLines, isComposerFullSize), {maxWidth: textInputWidth as DimensionValue}]}>
{`${valueBeforeCaret} `}
<Text
numberOfLines={1}
Expand All @@ -349,23 +302,20 @@ function Composer(
if (shouldContainScroll) {
return isScrollBarVisible ? [styles.overflowScroll, styles.overscrollBehaviorContain] : styles.overflowHidden;
}
return [
// We are hiding the scrollbar to prevent it from reducing the text input width,
// so we can get the correct scroll height while calculating the number of lines.
numberOfLines < maxLines ? styles.overflowHidden : {},
];
}, [shouldContainScroll, isScrollBarVisible, maxLines, numberOfLines, styles.overflowHidden, styles.overflowScroll, styles.overscrollBehaviorContain]);
return styles.overflowAuto;
}, [shouldContainScroll, styles.overflowAuto, styles.overflowScroll, styles.overscrollBehaviorContain, styles.overflowHidden, isScrollBarVisible]);

const inputStyleMemo = useMemo(
() => [
StyleSheet.flatten([style, {outline: 'none'}]),
StyleUtils.getComposeTextAreaPadding(numberOfLines, isComposerFullSize),
StyleUtils.getComposeTextAreaPadding(isComposerFullSize),
Browser.isMobileSafari() || Browser.isSafari() ? styles.rtlTextRenderForSafari : {},
scrollStyleMemo,
StyleUtils.getComposerMaxHeightStyle(maxLines, isComposerFullSize),
isComposerFullSize ? ({height: '100%', maxHeight: 'none' as DimensionValue} as TextStyle) : undefined,
],

[numberOfLines, scrollStyleMemo, styles.rtlTextRenderForSafari, style, StyleUtils, isComposerFullSize],
[style, styles.rtlTextRenderForSafari, scrollStyleMemo, StyleUtils, maxLines, isComposerFullSize],
);

return (
Expand All @@ -376,32 +326,28 @@ function Composer(
placeholderTextColor={theme.placeholderText}
ref={(el) => (textInput.current = el)}
selection={selection}
style={inputStyleMemo}
style={[inputStyleMemo]}
markdownStyle={markdownStyle}
value={value}
defaultValue={defaultValue}
autoFocus={autoFocus}
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...props}
onSelectionChange={addCursorPositionToSelectionChange}
numberOfLines={currentNumberOfLines}
onContentSizeChange={(e) => {
setTextInputWidth(`${e.nativeEvent.contentSize.width}px`);
updateIsFullComposerAvailable({maxLines, isComposerFullSize, isDisabled, setIsFullComposerAvailable}, e, styles);
}}
disabled={isDisabled}
onKeyPress={handleKeyPress}
onFocus={(e) => {
if (isReportActionCompose) {
ReportActionComposeFocusManager.onComposerFocus(null);
} else {
// While a user edits a comment, if they open the LHN menu, we want to ensure that
// the focus returns to the message edit composer after they click on a menu item (e.g. mark as read).
// To achieve this, we re-assign the focus callback here.
ReportActionComposeFocusManager.onComposerFocus(() => {
if (!textInput.current) {
return;
}

textInput.current.focus();
});
}
ReportActionComposeFocusManager.onComposerFocus(() => {
if (!textInput.current) {
return;
}

textInput.current.focus();
});

props.onFocus?.(e);
}}
Expand Down
6 changes: 0 additions & 6 deletions src/components/Composer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,9 @@ type ComposerProps = TextInputProps & {
/** The value of the comment box */
value?: string;

/** Number of lines for the comment */
numberOfLines?: number;

/** Callback method handle when the input is changed */
onChangeText?: (numberOfLines: string) => void;

/** Callback method to update number of lines for the comment */
onNumberOfLinesChange?: (numberOfLines: number) => void;

/** Callback method to handle pasting a file */
onPasteFile?: (file: File) => void;

Expand Down
8 changes: 0 additions & 8 deletions src/libs/ComposerUtils/getNumberOfLines/index.native.ts

This file was deleted.

12 changes: 0 additions & 12 deletions src/libs/ComposerUtils/getNumberOfLines/index.ts

This file was deleted.

3 changes: 0 additions & 3 deletions src/libs/ComposerUtils/getNumberOfLines/types.ts

This file was deleted.

4 changes: 1 addition & 3 deletions src/libs/ComposerUtils/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import getNumberOfLines from './getNumberOfLines';
import updateNumberOfLines from './updateNumberOfLines';

type Selection = {
start: number;
Expand Down Expand Up @@ -49,5 +47,5 @@ function findCommonSuffixLength(str1: string, str2: string, cursorPosition: numb
return commonSuffixLength;
}

export {getNumberOfLines, updateNumberOfLines, insertText, canSkipTriggerHotkeys, insertWhiteSpaceAtIndex, findCommonSuffixLength};
export {insertText, canSkipTriggerHotkeys, insertWhiteSpaceAtIndex, findCommonSuffixLength};
export type {Selection};
12 changes: 10 additions & 2 deletions src/libs/ComposerUtils/updateIsFullComposerAvailable.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import type {NativeSyntheticEvent, TextInputContentSizeChangeEventData} from 'react-native';
import type {ComposerProps} from '@components/Composer/types';
import type {ThemeStyles} from '@styles/index';
import CONST from '@src/CONST';

/**
* Update isFullComposerAvailable if needed
* @param numberOfLines The number of lines in the text input
*/
function updateIsFullComposerAvailable(props: ComposerProps, numberOfLines: number) {
const isFullComposerAvailable = numberOfLines >= CONST.COMPOSER.FULL_COMPOSER_MIN_LINES;
function updateIsFullComposerAvailable(props: ComposerProps, event: NativeSyntheticEvent<TextInputContentSizeChangeEventData>, styles: ThemeStyles, shouldIncludePadding = false) {
const paddingTopAndBottom = shouldIncludePadding ? styles.textInputComposeSpacing.paddingVertical * 2 : 0;
const inputHeight = event?.nativeEvent?.contentSize?.height ?? null;
if (!inputHeight) {
return;
}
const totalHeight = inputHeight + paddingTopAndBottom;
const isFullComposerAvailable = totalHeight >= CONST.COMPOSER.FULL_COMPOSER_MIN_HEIGHT;
if (isFullComposerAvailable !== props.isFullComposerAvailable) {
props.setIsFullComposerAvailable?.(isFullComposerAvailable);
}
Expand Down
20 changes: 0 additions & 20 deletions src/libs/ComposerUtils/updateNumberOfLines/index.native.ts

This file was deleted.

5 changes: 0 additions & 5 deletions src/libs/ComposerUtils/updateNumberOfLines/index.ts

This file was deleted.

7 changes: 0 additions & 7 deletions src/libs/ComposerUtils/updateNumberOfLines/types.ts

This file was deleted.

12 changes: 0 additions & 12 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1171,11 +1171,6 @@ function saveReportDraftComment(reportID: string, comment: string | null) {
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, prepareDraftComment(comment));
}

/** Saves the number of lines for the comment */
function saveReportCommentNumberOfLines(reportID: string, numberOfLines: number) {
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}`, numberOfLines);
}

/** Broadcasts whether or not a user is typing on a report over the report's private pusher channel. */
function broadcastUserIsTyping(reportID: string) {
const privateReportChannelName = getReportChannelName(reportID);
Expand Down Expand Up @@ -1508,11 +1503,6 @@ function saveReportActionDraft(reportID: string, reportAction: ReportAction, dra
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${originalReportID}`, {[reportAction.reportActionID]: {message: draftMessage}});
}

/** Saves the number of lines for the report action draft */
function saveReportActionDraftNumberOfLines(reportID: string, reportActionID: string, numberOfLines: number) {
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}_${reportActionID}`, numberOfLines);
}

function updateNotificationPreference(
reportID: string,
previousValue: NotificationPreference | undefined,
Expand Down Expand Up @@ -3726,15 +3716,13 @@ export {
unsubscribeFromReportChannel,
unsubscribeFromLeavingRoomReportChannel,
saveReportDraftComment,
saveReportCommentNumberOfLines,
broadcastUserIsTyping,
broadcastUserIsLeavingRoom,
togglePinnedState,
editReportComment,
handleUserDeletedLinksInHtml,
deleteReportActionDraft,
saveReportActionDraft,
saveReportActionDraftNumberOfLines,
deleteReportComment,
navigateToConciergeChat,
addPolicyReport,
Expand Down
Loading
Loading