diff --git a/src/CONST.ts b/src/CONST.ts index 66b51184852f..83690d5e9a85 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -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: { diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 68caf88c548c..1a27d691e2ef 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -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_', @@ -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; diff --git a/src/components/Composer/index.native.tsx b/src/components/Composer/index.native.tsx index 6cea253d5957..4d135cdd88e2 100644 --- a/src/components/Composer/index.native.tsx +++ b/src/components/Composer/index.native.tsx @@ -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( @@ -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. @@ -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} diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index 23d24a5ae5dd..4bc54d13b056 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -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'; @@ -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 = { @@ -83,10 +78,8 @@ function Composer( const styles = useThemeStyles(); const markdownStyle = useMarkdownStyle(value); const StyleUtils = useStyleUtils(); - const {windowWidth} = useWindowDimensions(); const textRef = useRef(null); const textInput = useRef(null); - const [numberOfLines, setNumberOfLines] = useState(numberOfLinesProp); const [selection, setSelection] = useState< | { start: number; @@ -109,7 +102,6 @@ function Composer( return; } textInput.current?.clear(); - setNumberOfLines(1); onClear(); }, [shouldClear, onClear]); @@ -126,12 +118,8 @@ function Composer( * Adds the cursor position to the selection change event. */ const addCursorPositionToSelectionChange = (event: NativeSyntheticEvent) => { - if (!isRendered) { - return; - } const webEvent = event as BaseSyntheticEvent; - - 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)); @@ -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; @@ -333,7 +286,7 @@ function Composer( opacity: 0, }} > - + {`${valueBeforeCaret} `} [ 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 ( @@ -376,7 +326,7 @@ function Composer( placeholderTextColor={theme.placeholderText} ref={(el) => (textInput.current = el)} selection={selection} - style={inputStyleMemo} + style={[inputStyleMemo]} markdownStyle={markdownStyle} value={value} defaultValue={defaultValue} @@ -384,24 +334,20 @@ function Composer( /* 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); }} diff --git a/src/components/Composer/types.ts b/src/components/Composer/types.ts index 6bc44aba69cd..531bcd03f8bf 100644 --- a/src/components/Composer/types.ts +++ b/src/components/Composer/types.ts @@ -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; diff --git a/src/libs/ComposerUtils/getNumberOfLines/index.native.ts b/src/libs/ComposerUtils/getNumberOfLines/index.native.ts deleted file mode 100644 index 0cbfb6c7f517..000000000000 --- a/src/libs/ComposerUtils/getNumberOfLines/index.native.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type GetNumberOfLines from './types'; - -/** - * Get the current number of lines in the composer - */ -const getNumberOfLines: GetNumberOfLines = (lineHeight, paddingTopAndBottom, scrollHeight) => Math.ceil((scrollHeight - paddingTopAndBottom) / lineHeight); - -export default getNumberOfLines; diff --git a/src/libs/ComposerUtils/getNumberOfLines/index.ts b/src/libs/ComposerUtils/getNumberOfLines/index.ts deleted file mode 100644 index e80744d41c69..000000000000 --- a/src/libs/ComposerUtils/getNumberOfLines/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type GetNumberOfLines from './types'; - -/** - * Get the current number of lines in the composer - */ -const getNumberOfLines: GetNumberOfLines = (lineHeight, paddingTopAndBottom, scrollHeight, maxLines = 0) => { - let newNumberOfLines = Math.ceil((scrollHeight - paddingTopAndBottom) / lineHeight); - newNumberOfLines = maxLines <= 0 ? newNumberOfLines : Math.min(newNumberOfLines, maxLines); - return newNumberOfLines; -}; - -export default getNumberOfLines; diff --git a/src/libs/ComposerUtils/getNumberOfLines/types.ts b/src/libs/ComposerUtils/getNumberOfLines/types.ts deleted file mode 100644 index 67bb790f726b..000000000000 --- a/src/libs/ComposerUtils/getNumberOfLines/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -type GetNumberOfLines = (lineHeight: number, paddingTopAndBottom: number, scrollHeight: number, maxLines?: number) => number; - -export default GetNumberOfLines; diff --git a/src/libs/ComposerUtils/index.ts b/src/libs/ComposerUtils/index.ts index f2e940abeb73..04d857a8faeb 100644 --- a/src/libs/ComposerUtils/index.ts +++ b/src/libs/ComposerUtils/index.ts @@ -1,6 +1,4 @@ import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import getNumberOfLines from './getNumberOfLines'; -import updateNumberOfLines from './updateNumberOfLines'; type Selection = { start: number; @@ -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}; diff --git a/src/libs/ComposerUtils/updateIsFullComposerAvailable.ts b/src/libs/ComposerUtils/updateIsFullComposerAvailable.ts index 580908fc3805..c10635f1c491 100644 --- a/src/libs/ComposerUtils/updateIsFullComposerAvailable.ts +++ b/src/libs/ComposerUtils/updateIsFullComposerAvailable.ts @@ -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, 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); } diff --git a/src/libs/ComposerUtils/updateNumberOfLines/index.native.ts b/src/libs/ComposerUtils/updateNumberOfLines/index.native.ts deleted file mode 100644 index 5a7676d8bfbd..000000000000 --- a/src/libs/ComposerUtils/updateNumberOfLines/index.native.ts +++ /dev/null @@ -1,20 +0,0 @@ -import getNumberOfLines from '@libs/ComposerUtils/getNumberOfLines'; -import updateIsFullComposerAvailable from '@libs/ComposerUtils/updateIsFullComposerAvailable'; -import type UpdateNumberOfLines from './types'; - -/** - * 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: UpdateNumberOfLines = (props, event, styles) => { - const lineHeight = styles.textInputCompose.lineHeight ?? 0; - const paddingTopAndBottom = styles.textInputComposeSpacing.paddingVertical * 2; - const inputHeight = event?.nativeEvent?.contentSize?.height ?? null; - if (!inputHeight) { - return; - } - const numberOfLines = getNumberOfLines(lineHeight, paddingTopAndBottom, inputHeight); - updateIsFullComposerAvailable(props, numberOfLines); -}; - -export default updateNumberOfLines; diff --git a/src/libs/ComposerUtils/updateNumberOfLines/index.ts b/src/libs/ComposerUtils/updateNumberOfLines/index.ts deleted file mode 100644 index 3037fb99c8b1..000000000000 --- a/src/libs/ComposerUtils/updateNumberOfLines/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type UpdateNumberOfLines from './types'; - -const updateNumberOfLines: UpdateNumberOfLines = () => {}; - -export default updateNumberOfLines; diff --git a/src/libs/ComposerUtils/updateNumberOfLines/types.ts b/src/libs/ComposerUtils/updateNumberOfLines/types.ts deleted file mode 100644 index 06daee56a707..000000000000 --- a/src/libs/ComposerUtils/updateNumberOfLines/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type {NativeSyntheticEvent, TextInputContentSizeChangeEventData} from 'react-native'; -import type {ComposerProps} from '@components/Composer/types'; -import type {ThemeStyles} from '@styles/index'; - -type UpdateNumberOfLines = (props: ComposerProps, event: NativeSyntheticEvent, styles: ThemeStyles) => void; - -export default UpdateNumberOfLines; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 6fc51cd6b066..98300b563e29 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -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); @@ -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, @@ -3726,7 +3716,6 @@ export { unsubscribeFromReportChannel, unsubscribeFromLeavingRoomReportChannel, saveReportDraftComment, - saveReportCommentNumberOfLines, broadcastUserIsTyping, broadcastUserIsLeavingRoom, togglePinnedState, @@ -3734,7 +3723,6 @@ export { handleUserDeletedLinksInHtml, deleteReportActionDraft, saveReportActionDraft, - saveReportActionDraftNumberOfLines, deleteReportComment, navigateToConciergeChat, addPolicyReport, diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 8f42da5a1575..a96375a93582 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -63,9 +63,6 @@ type AnimatedRef = ReturnType; type NewlyAddedChars = {startIndex: number; endIndex: number; diff: string}; type ComposerWithSuggestionsOnyxProps = { - /** The number of lines the comment should take up */ - numberOfLines: OnyxEntry; - /** The parent report actions for the report */ parentReportActions: OnyxEntry; @@ -215,7 +212,6 @@ function ComposerWithSuggestions( modal, preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE, parentReportActions, - numberOfLines, // Props: Report reportID, @@ -459,19 +455,6 @@ function ComposerWithSuggestions( ], ); - /** - * Update the number of lines for a comment in Onyx - */ - const updateNumberOfLines = useCallback( - (newNumberOfLines: number) => { - if (newNumberOfLines === numberOfLines) { - return; - } - Report.saveReportCommentNumberOfLines(reportID, newNumberOfLines); - }, - [reportID, numberOfLines], - ); - const prepareCommentAndResetComposer = useCallback((): string => { const trimmedComment = commentRef.current.trim(); const commentLength = ReportUtils.getCommentLength(trimmedComment); @@ -760,8 +743,6 @@ function ComposerWithSuggestions( isComposerFullSize={isComposerFullSize} value={value} testID="composer" - numberOfLines={numberOfLines ?? undefined} - onNumberOfLinesChange={updateNumberOfLines} shouldCalculateCaretPosition onLayout={onLayout} onScroll={hideSuggestionMenu} @@ -808,12 +789,6 @@ ComposerWithSuggestions.displayName = 'ComposerWithSuggestions'; const ComposerWithSuggestionsWithRef = forwardRef(ComposerWithSuggestions); export default withOnyx, ComposerWithSuggestionsOnyxProps>({ - numberOfLines: { - key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}`, - // We might not have number of lines in onyx yet, for which the composer would be rendered as null - // during the first render, which we want to avoid: - initWithStoredValues: false, - }, modal: { key: ONYXKEYS.MODAL, }, diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 38f9c9b875d1..cc82a80a4909 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -940,17 +940,11 @@ function getEmojiPickerListHeight(isRenderingShortcutRow: boolean, windowHeight: /** * Returns padding vertical based on number of lines */ -function getComposeTextAreaPadding(numberOfLines: number, isComposerFullSize: boolean): TextStyle { +function getComposeTextAreaPadding(isComposerFullSize: boolean): TextStyle { let paddingValue = 5; // Issue #26222: If isComposerFullSize paddingValue will always be 5 to prevent padding jumps when adding multiple lines. if (!isComposerFullSize) { - if (numberOfLines === 1) { - paddingValue = 9; - } - // In case numberOfLines = 3, there will be a Expand Icon appearing at the top left, so it has to be recalculated so that the textArea can be full height - else if (numberOfLines === 3) { - paddingValue = 8; - } + paddingValue = 8; } return { paddingTop: paddingValue,