From 97f8f7da93ddfa4209bc472420aae08e341cf4fb Mon Sep 17 00:00:00 2001 From: QichenZhu <57348009+QichenZhu@users.noreply.github.com> Date: Sat, 9 Nov 2024 18:08:06 +1300 Subject: [PATCH 1/6] Fix expand button interrupting composer shrinking --- src/components/Composer/implementation/index.native.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/Composer/implementation/index.native.tsx b/src/components/Composer/implementation/index.native.tsx index 9f237dd02424..c1fbf972380e 100644 --- a/src/components/Composer/implementation/index.native.tsx +++ b/src/components/Composer/implementation/index.native.tsx @@ -84,6 +84,9 @@ function Composer( const onClear = useCallback( ({nativeEvent}: NativeSyntheticEvent) => { + // Remove the expand button first to ensure the Composer can shrink smoothly. + setIsFullComposerAvailable(false); + onClearProp(nativeEvent.text); }, [onClearProp], From 196be4f874bf233d99cc7c7460588385f7ee35c2 Mon Sep 17 00:00:00 2001 From: QichenZhu <57348009+QichenZhu@users.noreply.github.com> Date: Sat, 9 Nov 2024 20:46:44 +1300 Subject: [PATCH 2/6] Update dependency --- src/components/Composer/implementation/index.native.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Composer/implementation/index.native.tsx b/src/components/Composer/implementation/index.native.tsx index c1fbf972380e..58005827f3c6 100644 --- a/src/components/Composer/implementation/index.native.tsx +++ b/src/components/Composer/implementation/index.native.tsx @@ -89,7 +89,7 @@ function Composer( onClearProp(nativeEvent.text); }, - [onClearProp], + [onClearProp, setIsFullComposerAvailable], ); const pasteFile = useCallback( From 4f63749311861f2713e1d93ce835de862452835f Mon Sep 17 00:00:00 2001 From: QichenZhu <57348009+QichenZhu@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:09:40 +1300 Subject: [PATCH 3/6] Switch to pure CSS method --- .../Composer/implementation/index.native.tsx | 8 +- .../Composer/implementation/index.tsx | 5 - src/components/Composer/types.ts | 6 - .../updateIsFullComposerAvailable.ts | 23 --- .../AttachmentPickerWithMenuItems.tsx | 165 ++++++++++-------- .../ComposerWithSuggestions.tsx | 10 -- .../ReportActionCompose.tsx | 4 - .../report/ReportActionItemMessageEdit.tsx | 2 +- src/styles/index.ts | 2 +- 9 files changed, 93 insertions(+), 132 deletions(-) delete mode 100644 src/libs/ComposerUtils/updateIsFullComposerAvailable.ts diff --git a/src/components/Composer/implementation/index.native.tsx b/src/components/Composer/implementation/index.native.tsx index 58005827f3c6..d5f72f7088b2 100644 --- a/src/components/Composer/implementation/index.native.tsx +++ b/src/components/Composer/implementation/index.native.tsx @@ -14,7 +14,6 @@ import useResetComposerFocus from '@hooks/useResetComposerFocus'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import updateIsFullComposerAvailable from '@libs/ComposerUtils/updateIsFullComposerAvailable'; import * as EmojiUtils from '@libs/EmojiUtils'; import * as FileUtils from '@libs/fileDownload/FileUtils'; import CONST from '@src/CONST'; @@ -29,7 +28,6 @@ function Composer( isDisabled = false, maxLines, isComposerFullSize = false, - setIsFullComposerAvailable = () => {}, autoFocus = false, style, // On native layers we like to have the Text Input not focused so the @@ -84,12 +82,9 @@ function Composer( const onClear = useCallback( ({nativeEvent}: NativeSyntheticEvent) => { - // Remove the expand button first to ensure the Composer can shrink smoothly. - setIsFullComposerAvailable(false); - onClearProp(nativeEvent.text); }, - [onClearProp, setIsFullComposerAvailable], + [onClearProp], ); const pasteFile = useCallback( @@ -121,7 +116,6 @@ function Composer( placeholderTextColor={theme.placeholderText} ref={setTextInputRef} value={value} - onContentSizeChange={(e) => updateIsFullComposerAvailable({maxLines, isComposerFullSize, isDisabled, setIsFullComposerAvailable}, e, styles, true)} rejectResponderTermination={false} smartInsertDelete={false} textAlignVertical="center" diff --git a/src/components/Composer/implementation/index.tsx b/src/components/Composer/implementation/index.tsx index 4431007793cb..be875790d75e 100755 --- a/src/components/Composer/implementation/index.tsx +++ b/src/components/Composer/implementation/index.tsx @@ -16,7 +16,6 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; import * as Browser from '@libs/Browser'; -import updateIsFullComposerAvailable from '@libs/ComposerUtils/updateIsFullComposerAvailable'; import * as EmojiUtils from '@libs/EmojiUtils'; import * as FileUtils from '@libs/fileDownload/FileUtils'; import isEnterWhileComposition from '@libs/KeyboardShortcut/isEnterWhileComposition'; @@ -41,7 +40,6 @@ function Composer( onClear = () => {}, onPasteFile = () => {}, onSelectionChange = () => {}, - setIsFullComposerAvailable = () => {}, checkComposerVisibility = () => false, selection: selectionProp = { start: 0, @@ -352,9 +350,6 @@ function Composer( /* eslint-disable-next-line react/jsx-props-no-spreading */ {...props} onSelectionChange={addCursorPositionToSelectionChange} - onContentSizeChange={(e) => { - updateIsFullComposerAvailable({maxLines, isComposerFullSize, isDisabled, setIsFullComposerAvailable}, e, styles); - }} disabled={isDisabled} onKeyPress={handleKeyPress} addAuthTokenToImageURLCallback={addEncryptedAuthTokenToURL} diff --git a/src/components/Composer/types.ts b/src/components/Composer/types.ts index ef497dd52e47..3df5508f1dd7 100644 --- a/src/components/Composer/types.ts +++ b/src/components/Composer/types.ts @@ -54,12 +54,6 @@ type ComposerProps = Omit & { /** Selection Object */ selection?: TextSelection; - /** Whether the full composer can be opened */ - isFullComposerAvailable?: boolean; - - /** Allow the full composer to be opened */ - setIsFullComposerAvailable?: (value: boolean) => void; - /** Should we calculate the caret position */ shouldCalculateCaretPosition?: boolean; diff --git a/src/libs/ComposerUtils/updateIsFullComposerAvailable.ts b/src/libs/ComposerUtils/updateIsFullComposerAvailable.ts deleted file mode 100644 index c10635f1c491..000000000000 --- a/src/libs/ComposerUtils/updateIsFullComposerAvailable.ts +++ /dev/null @@ -1,23 +0,0 @@ -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, 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); - } -} - -export default updateIsFullComposerAvailable; diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 27ab62a0f02e..4a5c8a6f6112 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -1,5 +1,5 @@ import {useIsFocused} from '@react-navigation/native'; -import React, {useCallback, useEffect, useMemo, useState} from 'react'; +import React, {ForwardedRef, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; @@ -44,9 +44,6 @@ type AttachmentPickerWithMenuItemsProps = { /** Callback to open the file in the modal */ displayFileInModal: (url: FileObject) => void; - /** Whether or not the full size composer is available */ - isFullComposerAvailable: boolean; - /** Whether or not the composer is full size */ isComposerFullSize: boolean; @@ -100,7 +97,6 @@ function AttachmentPickerWithMenuItems({ report, reportParticipantIDs, displayFileInModal, - isFullComposerAvailable, isComposerFullSize, reportID, isBlockedFromConcierge, @@ -245,76 +241,95 @@ function AttachmentPickerWithMenuItems({ ]; return ( <> - - {isComposerFullSize && ( - - { - e?.preventDefault(); - raiseIsScrollLikelyLayoutTriggered(); - Report.setIsComposerFullSize(reportID, false); - }} - // Keep focus on the composer when Collapse button is clicked. - onMouseDown={(e) => e.preventDefault()} - style={styles.composerSizeButton} - disabled={isBlockedFromConcierge || disabled} - role={CONST.ROLE.BUTTON} - accessibilityLabel={translate('reportActionCompose.collapse')} - > - - - - )} - {!isComposerFullSize && isFullComposerAvailable && ( - - { - e?.preventDefault(); - raiseIsScrollLikelyLayoutTriggered(); - Report.setIsComposerFullSize(reportID, true); - }} - // Keep focus on the composer when Expand button is clicked. - onMouseDown={(e) => e.preventDefault()} - style={styles.composerSizeButton} - disabled={isBlockedFromConcierge || disabled} - role={CONST.ROLE.BUTTON} - accessibilityLabel={translate('reportActionCompose.expand')} - > - - - - )} - - { - e?.preventDefault(); - if (!isFocused) { - return; - } - onAddActionPressed(); - - // Drop focus to avoid blue focus ring. - actionButtonRef.current?.blur(); - setMenuVisibility(!isMenuVisible); - }} - style={styles.composerSizeButton} - disabled={isBlockedFromConcierge || disabled} - role={CONST.ROLE.BUTTON} - accessibilityLabel={translate('common.create')} - > - - - + + + + + { + e?.preventDefault(); + if (!isFocused) { + return; + } + onAddActionPressed(); + + // Drop focus to avoid blue focus ring. + actionButtonRef.current?.blur(); + setMenuVisibility(!isMenuVisible); + }} + style={styles.composerSizeButton} + disabled={isBlockedFromConcierge || disabled} + role={CONST.ROLE.BUTTON} + accessibilityLabel={translate('common.create')} + > + + + + + + {isComposerFullSize ? ( + + { + e?.preventDefault(); + raiseIsScrollLikelyLayoutTriggered(); + Report.setIsComposerFullSize(reportID, false); + }} + // Keep focus on the composer when Collapse button is clicked. + onMouseDown={(e) => e.preventDefault()} + style={styles.composerSizeButton} + disabled={isBlockedFromConcierge || disabled} + role={CONST.ROLE.BUTTON} + accessibilityLabel={translate('reportActionCompose.collapse')} + > + + + + ) : ( + + { + e?.preventDefault(); + raiseIsScrollLikelyLayoutTriggered(); + Report.setIsComposerFullSize(reportID, true); + }} + // Keep focus on the composer when Expand button is clicked. + onMouseDown={(e) => e.preventDefault()} + style={styles.composerSizeButton} + disabled={isBlockedFromConcierge || disabled} + role={CONST.ROLE.BUTTON} + accessibilityLabel={translate('reportActionCompose.expand')} + > + + + + )} + + & { /** Whether the input is disabled */ disabled: boolean; - /** Whether the full composer is available */ - isFullComposerAvailable: boolean; - - /** Function to set whether the full composer is available */ - setIsFullComposerAvailable: (isFullComposerAvailable: boolean) => void; - /** Function to set whether the comment is empty */ setIsCommentEmpty: (isCommentEmpty: boolean) => void; @@ -227,8 +221,6 @@ function ComposerWithSuggestions( displayFileInModal, isBlockedFromConcierge, disabled, - isFullComposerAvailable, - setIsFullComposerAvailable, setIsCommentEmpty, handleSendMessage, shouldShowComposeInput, @@ -776,8 +768,6 @@ function ComposerWithSuggestions( isDisabled={isBlockedFromConcierge || disabled} selection={selection} onSelectionChange={onSelectionChange} - isFullComposerAvailable={isFullComposerAvailable} - setIsFullComposerAvailable={setIsFullComposerAvailable} isComposerFullSize={isComposerFullSize} value={value} testID="composer" diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 23b059f2fda2..3a4735520b09 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -133,7 +133,6 @@ function ReportActionCompose({ const initialModalState = getModalState(); return shouldFocusInputOnScreenFocus && shouldShowComposeInput && !initialModalState?.isVisible && !initialModalState?.willAlertModalBecomeVisible; }); - const [isFullComposerAvailable, setIsFullComposerAvailable] = useState(isComposerFullSize); const [shouldHideEducationalTooltip, setShouldHideEducationalTooltip] = useState(false); // A flag to indicate whether the onScroll callback is likely triggered by a layout change (caused by text change) or not @@ -452,7 +451,6 @@ function ReportActionCompose({ reportID={reportID} report={report} reportParticipantIDs={reportParticipantIDs} - isFullComposerAvailable={isFullComposerAvailable} isComposerFullSize={isComposerFullSize} isBlockedFromConcierge={isBlockedFromConcierge} disabled={!!disabled} @@ -492,8 +490,6 @@ function ReportActionCompose({ onCleared={submitForm} isBlockedFromConcierge={isBlockedFromConcierge} disabled={!!disabled} - isFullComposerAvailable={isFullComposerAvailable} - setIsFullComposerAvailable={setIsFullComposerAvailable} setIsCommentEmpty={setIsCommentEmpty} handleSendMessage={handleSendMessage} shouldShowComposeInput={shouldShowComposeInput} diff --git a/src/pages/home/report/ReportActionItemMessageEdit.tsx b/src/pages/home/report/ReportActionItemMessageEdit.tsx index fd2dc2d44d4b..3ecdcdef234b 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.tsx +++ b/src/pages/home/report/ReportActionItemMessageEdit.tsx @@ -486,7 +486,7 @@ function ReportActionItemMessageEdit( height: 32, width: 32, padding: 6, - margin: 3, + marginHorizontal: 3, borderRadius: variables.componentBorderRadiusRounded, backgroundColor: theme.transparent, justifyContent: 'center', From 990b6cc8a4f3aae79601e4cef7cfa400f10f90fc Mon Sep 17 00:00:00 2001 From: QichenZhu <57348009+QichenZhu@users.noreply.github.com> Date: Mon, 11 Nov 2024 18:40:57 +1300 Subject: [PATCH 4/6] Add comments --- .../AttachmentPickerWithMenuItems.tsx | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 4a5c8a6f6112..7fd55f0aadca 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -1,5 +1,5 @@ import {useIsFocused} from '@react-navigation/native'; -import React, {ForwardedRef, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; @@ -241,22 +241,27 @@ function AttachmentPickerWithMenuItems({ ]; return ( <> - - + {/* 1. Limit the container width to a single column. */} + + {/* + 2. If there isn't enough height for two buttons, + the Expand/Collapse button wraps to the next column so that it's intentionally hidden, + and the Create button is centered vertically. + */} + + {/* 3. If there is enough height for two buttons, the Create button is at the bottom. */} + {/* 4. And the Expand/Collapse button is at the top. */} {isComposerFullSize ? ( From 3f83594cb53ccf1e71f522185ecfc39d80307001 Mon Sep 17 00:00:00 2001 From: QichenZhu <57348009+QichenZhu@users.noreply.github.com> Date: Mon, 11 Nov 2024 19:01:18 +1300 Subject: [PATCH 5/6] Extract inline styles --- .../AttachmentPickerWithMenuItems.tsx | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 7fd55f0aadca..6aab07e18ee0 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -220,6 +220,29 @@ function AttachmentPickerWithMenuItems({ setMenuVisibility(false); }, [didScreenBecomeInactive, isMenuVisible, setMenuVisibility]); + // 1. Limit the container width to a single column. + const outerContainerStyles = [{flexBasis: styles.composerSizeButton.width + styles.composerSizeButton.marginHorizontal * 2}, styles.flexGrow0, styles.flexShrink0]; + + // 2. If there isn't enough height for two buttons, the Expand/Collapse button wraps to the next column so that it's intentionally hidden, + // and the Create button is centered vertically. + const innerContainerStyles = [ + styles.dFlex, + styles.flexColumnReverse, + styles.flexWrap, + styles.justifyContentCenter, + styles.pAbsolute, + styles.h100, + styles.w100, + styles.overflowHidden, + {paddingVertical: styles.composerSizeButton.marginHorizontal}, + ]; + + // 3. If there is enough height for two buttons, the Expand/Collapse button is at the top. + const expandCollapseButtonContainerStyles = [styles.flexGrow1, styles.flexShrink0]; + + // 4. And the Create button is at the bottom. + const createButtonContainerStyles = [styles.flexGrow0, styles.flexShrink0]; + return ( {({openPicker}) => { @@ -241,28 +264,9 @@ function AttachmentPickerWithMenuItems({ ]; return ( <> - {/* 1. Limit the container width to a single column. */} - - {/* - 2. If there isn't enough height for two buttons, - the Expand/Collapse button wraps to the next column so that it's intentionally hidden, - and the Create button is centered vertically. - */} - - {/* 3. If there is enough height for two buttons, the Create button is at the bottom. */} - + + + - {/* 4. And the Expand/Collapse button is at the top. */} - + {isComposerFullSize ? ( Date: Mon, 11 Nov 2024 19:04:59 +1300 Subject: [PATCH 6/6] Extract inline styles --- src/pages/home/report/ReportActionItemMessageEdit.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemMessageEdit.tsx b/src/pages/home/report/ReportActionItemMessageEdit.tsx index 3ecdcdef234b..fbe51fa27acf 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.tsx +++ b/src/pages/home/report/ReportActionItemMessageEdit.tsx @@ -467,6 +467,8 @@ function ReportActionItemMessageEdit( } }, [isFocused, hideSuggestionMenu]); + const closeButtonStyles = [styles.composerSizeButton, {marginVertical: styles.composerSizeButton.marginHorizontal}]; + return ( <>