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: 10731 Focus composer without showing keyboard when users go to chats #32711

Merged
merged 38 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
d8eb426
fix: 10731
christianwen Dec 8, 2023
07fdad0
Merge branch 'main' into fix/10731-chat-composer-behavior
christianwen Dec 11, 2023
a5f99b7
lint fix
christianwen Dec 11, 2023
f764d66
resolve conflict
christianwen Jan 2, 2024
f6e2e28
lint fix
christianwen Jan 2, 2024
4b19bdf
fix confict
christianwen Jan 25, 2024
7e930b8
lint fix
christianwen Jan 25, 2024
96e924d
fix conflicts
christianwen Feb 20, 2024
8edc49a
resolve conflict
christianwen Mar 5, 2024
5dc6922
fix focus input
christianwen Mar 5, 2024
b6647b4
focus input on safari
christianwen Mar 5, 2024
432e67e
Merge branch 'main' into fix/10731-chat-composer-behavior
christianwen Apr 8, 2024
e6bfa42
Merge branch 'main' into fix/10731-chat-composer-behavior
christianwen Apr 10, 2024
eb79042
lint fix
christianwen Apr 10, 2024
aec542a
resolve conflicts
christianwen Apr 16, 2024
802f37d
Merge branch 'main' into fix/10731-chat-composer-behavior
christianwen Apr 17, 2024
d036e24
conflicts
christianwen May 7, 2024
dd50857
resolve conflicts
christianwen Aug 13, 2024
b008b05
fix rn to remove autoFill when hide context menu
christianwen Aug 14, 2024
025fa15
type fix
christianwen Aug 14, 2024
c48bb63
resolve conflicts
christianwen Aug 27, 2024
726154d
lint fix
christianwen Aug 27, 2024
5e13e27
Merge branch 'main' into fix/10731-chat-composer-behavior
christianwen Sep 9, 2024
69bc468
remove outdated file
christianwen Sep 9, 2024
c037b05
resolve conflicts
christianwen Sep 13, 2024
3e60d1e
Merge branch 'main' into fix/10731-chat-composer-behavior
christianwen Sep 17, 2024
7ba9d60
clean isFocusedWhileChangingInputMode logic
christianwen Sep 17, 2024
958e9df
conflicts
christianwen Sep 20, 2024
ca23e24
bump rn livemarkdown version
christianwen Sep 20, 2024
c59d44f
use isKeyboardShown
christianwen Sep 24, 2024
fb798c0
Merge branch 'main' into fix/10731-chat-composer-behavior
christianwen Sep 24, 2024
fcac742
update rn-livemarkdown version
christianwen Sep 24, 2024
18055ae
remove CONST
christianwen Sep 24, 2024
381fdff
safari keyboard show
christianwen Sep 25, 2024
e08d442
lint fix
christianwen Sep 25, 2024
2282bf6
remove redundant fix
christianwen Sep 25, 2024
1245315
lint fix
christianwen Sep 25, 2024
33e093e
resolve conflicts
christianwen Sep 30, 2024
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
2 changes: 2 additions & 0 deletions src/components/Composer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ function Composer(
isReportActionCompose = false,
isComposerFullSize = false,
shouldContainScroll = false,
showSoftInputOnFocus = true,
...props
}: ComposerProps,
ref: ForwardedRef<TextInput | HTMLInputElement>,
Expand Down Expand Up @@ -381,6 +382,7 @@ function Composer(
value={value}
defaultValue={defaultValue}
autoFocus={autoFocus}
inputMode={!showSoftInputOnFocus ? 'none' : 'text'}
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...props}
onSelectionChange={addCursorPositionToSelectionChange}
Expand Down
2 changes: 2 additions & 0 deletions src/components/Composer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ type ComposerProps = TextInputProps & {

/** Should make the input only scroll inside the element avoid scroll out to parent */
shouldContainScroll?: boolean;

showSoftInputOnFocus?: boolean;
};

export type {TextSelection, ComposerProps};
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Browser from '@libs/Browser';
import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus';
import * as ComposerUtils from '@libs/ComposerUtils';
import convertToLTRForComposer from '@libs/convertToLTRForComposer';
import {getDraftComment} from '@libs/DraftCommentUtils';
Expand All @@ -36,7 +35,6 @@ import focusComposerWithDelay from '@libs/focusComposerWithDelay';
import getPlatform from '@libs/getPlatform';
import * as KeyDownListener from '@libs/KeyboardShortcut/KeyDownPressListener';
import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as SuggestionUtils from '@libs/SuggestionUtils';
import updateMultilineInputRange from '@libs/updateMultilineInputRange';
Expand Down Expand Up @@ -66,9 +64,6 @@ type ComposerWithSuggestionsOnyxProps = {
/** The number of lines the comment should take up */
numberOfLines: OnyxEntry<number>;

/** The parent report actions for the report */
parentReportActions: OnyxEntry<OnyxTypes.ReportActions>;

/** The modal state */
modal: OnyxEntry<OnyxTypes.Modal>;

Expand Down Expand Up @@ -156,22 +151,12 @@ type ComposerWithSuggestionsProps = ComposerWithSuggestionsOnyxProps &
/** Whether the edit is focused */
editFocused: boolean;

/** Wheater chat is empty */
isEmptyChat?: boolean;

/** The last report action */
lastReportAction?: OnyxEntry<OnyxTypes.ReportAction>;

/** Whether to include chronos */
includeChronos?: boolean;

/** The parent report action ID */
parentReportActionID?: string;

/** The parent report ID */
// eslint-disable-next-line react/no-unused-prop-types -- its used in the withOnyx HOC
parentReportID: string | undefined;

/** Whether report is from group policy */
isGroupPolicyReport: boolean;

Expand Down Expand Up @@ -199,10 +184,6 @@ const debouncedBroadcastUserIsTyping = lodashDebounce(

const willBlurTextInputOnTapOutside = willBlurTextInputOnTapOutsideFunc();

// We want consistent auto focus behavior on input between native and mWeb so we have some auto focus management code that will
// prevent auto focus on existing chat for mobile device
const shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus();

Comment on lines -214 to -217
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why this was deleted?

Copy link
Contributor

Choose a reason for hiding this comment

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

Same question.

After rechecking. This is added #18699 and now we change the expectation. So this should be removed to align with new expect

/**
* This component holds the value and selection state.
* If a component really needs access to these state values it should be put here.
Expand All @@ -214,15 +195,12 @@ function ComposerWithSuggestions(
// Onyx
modal,
preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE,
parentReportActions,
numberOfLines,

// Props: Report
reportID,
includeChronos,
isEmptyChat,
lastReportAction,
parentReportActionID,
isGroupPolicyReport,
policyID,

Expand Down Expand Up @@ -282,19 +260,21 @@ function ComposerWithSuggestions(
const {isSmallScreenWidth} = useWindowDimensions();
const maxComposerLines = isSmallScreenWidth ? CONST.COMPOSER.MAX_LINES_SMALL_SCREEN : CONST.COMPOSER.MAX_LINES;

const parentReportAction = parentReportActions?.[parentReportActionID ?? ''] ?? null;
const shouldAutoFocus =
!modal?.isVisible && isFocused && (shouldFocusInputOnScreenFocus || (isEmptyChat && !ReportActionsUtils.isTransactionThread(parentReportAction))) && shouldShowComposeInput;
const shouldAutoFocus = !modal?.isVisible && shouldShowComposeInput;

const valueRef = useRef(value);
valueRef.current = value;

const [showSoftInputOnFocus, setShowSoftInputOnFocus] = useState(false);

const [selection, setSelection] = useState(() => ({start: 0, end: 0}));

const [composerHeight, setComposerHeight] = useState(0);

const textInputRef = useRef<TextInput | null>(null);
const insertedEmojisRef = useRef<Emoji[]>([]);
const shouldInitFocus = useRef<boolean>(true);
const isFocusedWhileChangingInputMode = useRef<boolean>(false);

const syncSelectionWithOnChangeTextRef = useRef<SyncSelection | null>(null);

Expand Down Expand Up @@ -678,7 +658,15 @@ function ComposerWithSuggestions(
// We want to focus or refocus the input when a modal has been closed or the underlying screen is refocused.
// We avoid doing this on native platforms since the software keyboard popping
// open creates a jarring and broken UX.
if (!((willBlurTextInputOnTapOutside || shouldAutoFocus) && !isNextModalWillOpenRef.current && !modal?.isVisible && isFocused && (!!prevIsModalVisible || !prevIsFocused))) {
if (
!(
(willBlurTextInputOnTapOutside || shouldAutoFocus) &&
!isNextModalWillOpenRef.current &&
!modal?.isVisible &&
isFocused &&
(!!prevIsModalVisible || !prevIsFocused || shouldInitFocus.current)
Copy link
Collaborator

Choose a reason for hiding this comment

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

why would we need shouldInitFocus.current here?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

@christianwen Bump on this one. If possible, please rewrite this condition for more readable

)
) {
return;
}

Expand All @@ -687,6 +675,9 @@ function ComposerWithSuggestions(
return;
}
focus(true);
if (shouldInitFocus.current) {
shouldInitFocus.current = false;
}
}, [focus, prevIsFocused, editFocused, prevIsModalVisible, isFocused, modal?.isVisible, isNextModalWillOpenRef, shouldAutoFocus]);

useEffect(() => {
Expand Down Expand Up @@ -730,6 +721,16 @@ function ComposerWithSuggestions(
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
if (!showSoftInputOnFocus || !isFocusedWhileChangingInputMode.current) {
return;
}
// On Safari when changing inputMode from none to text, the keyboard will cover the view
// We need to re-focus to trigger the keyboard to open below the view
isFocusedWhileChangingInputMode.current = false;
textInputRef.current?.focus();
}, [showSoftInputOnFocus]);

return (
<>
<View style={[StyleUtils.getContainerComposeStyles(), styles.textInputComposeBorder]}>
Expand All @@ -746,7 +747,12 @@ function ComposerWithSuggestions(
style={[styles.textInputCompose, isComposerFullSize ? styles.textInputFullCompose : styles.textInputCollapseCompose]}
maxLines={maxComposerLines}
onFocus={onFocus}
onBlur={onBlur}
onBlur={(e) => {
if (isFocusedWhileChangingInputMode.current) {
return;
}
onBlur(e);
}}
onClick={setShouldBlockSuggestionCalcToFalse}
onPasteFile={displayFileInModal}
shouldClear={textInputShouldClear}
Expand All @@ -765,6 +771,18 @@ function ComposerWithSuggestions(
shouldCalculateCaretPosition
onLayout={onLayout}
onScroll={hideSuggestionMenu}
showSoftInputOnFocus={showSoftInputOnFocus}
onTouchStart={() => {
if (showSoftInputOnFocus) {
return;
}
if (Browser.isMobileSafari()) {
isFocusedWhileChangingInputMode.current = true;
textInputRef.current?.blur();
}

setShowSoftInputOnFocus(true);
}}
shouldContainScroll={Browser.isMobileSafari()}
/>
</View>
Expand Down Expand Up @@ -824,11 +842,6 @@ export default withOnyx<ComposerWithSuggestionsProps & RefAttributes<ComposerRef
editFocused: {
key: ONYXKEYS.INPUT_FOCUSED,
},
parentReportActions: {
key: ({parentReportID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`,
canEvict: false,
initWithStoredValues: false,
},
})(memo(ComposerWithSuggestionsWithRef));

export type {ComposerWithSuggestionsProps};
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import {getDraftComment} from '@libs/DraftCommentUtils';
import getModalState from '@libs/getModalState';
Expand Down Expand Up @@ -73,7 +72,7 @@ type ReportActionComposeOnyxProps = {

type ReportActionComposeProps = ReportActionComposeOnyxProps &
WithCurrentUserPersonalDetailsProps &
Pick<ComposerWithSuggestionsProps, 'reportID' | 'isEmptyChat' | 'isComposerFullSize' | 'disabled' | 'listHeight' | 'lastReportAction'> & {
Pick<ComposerWithSuggestionsProps, 'reportID' | 'isComposerFullSize' | 'disabled' | 'listHeight' | 'lastReportAction'> & {
/** A method to call when the form is submitted */
onSubmit: (newComment: string | undefined) => void;

Expand All @@ -93,10 +92,6 @@ type ReportActionComposeProps = ReportActionComposeOnyxProps &
onComposerBlur?: () => void;
};

// We want consistent auto focus behavior on input between native and mWeb so we have some auto focus management code that will
// prevent auto focus on existing chat for mobile device
const shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus();

const willBlurTextInputOnTapOutside = willBlurTextInputOnTapOutsideFunc();

function ReportActionCompose({
Expand All @@ -111,7 +106,6 @@ function ReportActionCompose({
listHeight = 0,
shouldShowComposeInput = true,
isReportReadyForDisplay = true,
isEmptyChat,
lastReportAction,
onComposerFocus,
onComposerBlur,
Expand All @@ -129,7 +123,7 @@ function ReportActionCompose({
*/
const [isFocused, setIsFocused] = useState(() => {
const initialModalState = getModalState();
return shouldFocusInputOnScreenFocus && shouldShowComposeInput && !initialModalState?.isVisible && !initialModalState?.willAlertModalBecomeVisible;
return shouldShowComposeInput && !initialModalState?.isVisible && !initialModalState?.willAlertModalBecomeVisible;
});
const [isFullComposerAvailable, setIsFullComposerAvailable] = useState(isComposerFullSize);

Expand Down Expand Up @@ -433,11 +427,8 @@ function ReportActionCompose({
raiseIsScrollLikelyLayoutTriggered={raiseIsScrollLikelyLayoutTriggered}
reportID={reportID}
policyID={report?.policyID ?? ''}
parentReportID={report?.parentReportID}
parentReportActionID={report?.parentReportActionID}
includeChronos={ReportUtils.chatIncludesChronos(report)}
isGroupPolicyReport={isGroupPolicyReport}
isEmptyChat={isEmptyChat}
lastReportAction={lastReportAction}
isMenuVisible={isMenuVisible}
inputPlaceholder={inputPlaceholder}
Expand Down
Loading