diff --git a/Mobile-Expensify b/Mobile-Expensify
index 768d69540612..7ffe8a7f1b47 160000
--- a/Mobile-Expensify
+++ b/Mobile-Expensify
@@ -1 +1 @@
-Subproject commit 768d695406126652ce222a46c95e643ba2e51e45
+Subproject commit 7ffe8a7f1b471c697f9823b8cd4a2c19b200fa6f
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 09bf1473e841..7fd4fd9dd59d 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -110,8 +110,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1009007801
- versionName "9.0.78-1"
+ versionCode 1009007802
+ versionName "9.0.78-2"
// Supported language variants must be declared here to avoid from being removed during the compilation.
// This also helps us to not include unnecessary language variants in the APK.
resConfigs "en", "es"
diff --git a/docs/assets/images/ExpensifyHelp-CreateExpenseUpdate-1.png b/docs/assets/images/ExpensifyHelp-CreateExpenseUpdate-1.png
new file mode 100644
index 000000000000..18318f782466
Binary files /dev/null and b/docs/assets/images/ExpensifyHelp-CreateExpenseUpdate-1.png differ
diff --git a/docs/assets/images/ExpensifyHelp-CreateExpenseUpdate-2.png b/docs/assets/images/ExpensifyHelp-CreateExpenseUpdate-2.png
new file mode 100644
index 000000000000..641c32a6a6b6
Binary files /dev/null and b/docs/assets/images/ExpensifyHelp-CreateExpenseUpdate-2.png differ
diff --git a/docs/assets/images/ExpensifyHelp-CreateExpenseUpdate-3.png b/docs/assets/images/ExpensifyHelp-CreateExpenseUpdate-3.png
new file mode 100644
index 000000000000..48c6f12fb75c
Binary files /dev/null and b/docs/assets/images/ExpensifyHelp-CreateExpenseUpdate-3.png differ
diff --git a/docs/assets/images/ExpensifyHelp-CreateExpenseUpdate-4.png b/docs/assets/images/ExpensifyHelp-CreateExpenseUpdate-4.png
new file mode 100644
index 000000000000..5f8af1e46ac4
Binary files /dev/null and b/docs/assets/images/ExpensifyHelp-CreateExpenseUpdate-4.png differ
diff --git a/docs/assets/images/OldDot - Create & Pay Bills 1.png b/docs/assets/images/OldDot - Create & Pay Bills 1.png
new file mode 100644
index 000000000000..a880e012408a
Binary files /dev/null and b/docs/assets/images/OldDot - Create & Pay Bills 1.png differ
diff --git a/docs/assets/images/OldDot - Create & Pay Bills 2.png b/docs/assets/images/OldDot - Create & Pay Bills 2.png
new file mode 100644
index 000000000000..ce022a95c6a1
Binary files /dev/null and b/docs/assets/images/OldDot - Create & Pay Bills 2.png differ
diff --git a/docs/assets/images/OldDot - Create & Pay Bills 3.png b/docs/assets/images/OldDot - Create & Pay Bills 3.png
new file mode 100644
index 000000000000..071bcc997934
Binary files /dev/null and b/docs/assets/images/OldDot - Create & Pay Bills 3.png differ
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 6a6d5bef5d7d..3374f9c36b3f 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -40,7 +40,7 @@
CFBundleVersion
- 9.0.78.1
+ 9.0.78.2
FullStory
OrgId
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index c996883405b2..6f72c68b009d 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -19,6 +19,6 @@
CFBundleSignature
????
CFBundleVersion
- 9.0.78.1
+ 9.0.78.2
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index 4e4305e1cbab..328278e16cf3 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -13,7 +13,7 @@
CFBundleShortVersionString
9.0.78
CFBundleVersion
- 9.0.78.1
+ 9.0.78.2
NSExtension
NSExtensionPointIdentifier
diff --git a/package-lock.json b/package-lock.json
index a1e8c86e2a78..ae031453f883 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "9.0.78-1",
+ "version": "9.0.78-2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "9.0.78-1",
+ "version": "9.0.78-2",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index b2248ab75cc2..3b5a25abb224 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "9.0.78-1",
+ "version": "9.0.78-2",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
diff --git a/src/components/Composer/implementation/index.native.tsx b/src/components/Composer/implementation/index.native.tsx
index 0cddb32f5aeb..a02767d24c87 100644
--- a/src/components/Composer/implementation/index.native.tsx
+++ b/src/components/Composer/implementation/index.native.tsx
@@ -1,7 +1,7 @@
import type {MarkdownStyle} from '@expensify/react-native-live-markdown';
import mimeDb from 'mime-db';
import type {ForwardedRef} from 'react';
-import React, {useCallback, useEffect, useMemo, useRef} from 'react';
+import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import type {NativeSyntheticEvent, TextInput, TextInputChangeEventData, TextInputPasteEventData} from 'react-native';
import {StyleSheet} from 'react-native';
import type {FileObject} from '@components/AttachmentModal';
@@ -9,6 +9,7 @@ import type {ComposerProps} from '@components/Composer/types';
import type {AnimatedMarkdownTextInputRef} from '@components/RNMarkdownTextInput';
import RNMarkdownTextInput from '@components/RNMarkdownTextInput';
import useAutoFocusInput from '@hooks/useAutoFocusInput';
+import useKeyboardState from '@hooks/useKeyboardState';
import useMarkdownStyle from '@hooks/useMarkdownStyle';
import useResetComposerFocus from '@hooks/useResetComposerFocus';
import useStyleUtils from '@hooks/useStyleUtils';
@@ -37,6 +38,7 @@ function Composer(
selection,
value,
isGroupPolicyReport = false,
+ showSoftInputOnFocus = true,
...props
}: ComposerProps,
ref: ForwardedRef,
@@ -49,7 +51,11 @@ function Composer(
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
+ const [contextMenuHidden, setContextMenuHidden] = useState(true);
+
const {inputCallbackRef, inputRef: autoFocusInputRef} = useAutoFocusInput();
+ const keyboardState = useKeyboardState();
+ const isKeyboardShown = keyboardState?.isKeyboardShown ?? false;
useEffect(() => {
if (autoFocus === !!autoFocusInputRef.current) {
@@ -58,6 +64,13 @@ function Composer(
inputCallbackRef(autoFocus ? textInput.current : null);
}, [autoFocus, inputCallbackRef, autoFocusInputRef]);
+ useEffect(() => {
+ if (!showSoftInputOnFocus || !isKeyboardShown) {
+ return;
+ }
+ setContextMenuHidden(false);
+ }, [showSoftInputOnFocus, isKeyboardShown]);
+
useEffect(() => {
if (!textInput.current || !textInput.current.setSelection || !selection || isComposerFullSize) {
return;
@@ -158,6 +171,8 @@ function Composer(
props?.onBlur?.(e);
}}
onClear={onClear}
+ showSoftInputOnFocus={showSoftInputOnFocus}
+ contextMenuHidden={contextMenuHidden}
/>
);
}
diff --git a/src/components/Composer/implementation/index.tsx b/src/components/Composer/implementation/index.tsx
index 5af76a2406b5..9171132964f6 100755
--- a/src/components/Composer/implementation/index.tsx
+++ b/src/components/Composer/implementation/index.tsx
@@ -5,7 +5,7 @@ import type {BaseSyntheticEvent, ForwardedRef} from 'react';
import React, {useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
// eslint-disable-next-line no-restricted-imports
import type {NativeSyntheticEvent, TextInput, TextInputKeyPressEventData, TextInputSelectionChangeEventData} from 'react-native';
-import {DeviceEventEmitter, StyleSheet} from 'react-native';
+import {DeviceEventEmitter, InteractionManager, StyleSheet} from 'react-native';
import type {ComposerProps} from '@components/Composer/types';
import type {AnimatedMarkdownTextInputRef} from '@components/RNMarkdownTextInput';
import RNMarkdownTextInput from '@components/RNMarkdownTextInput';
@@ -50,6 +50,7 @@ function Composer(
isComposerFullSize = false,
shouldContainScroll = true,
isGroupPolicyReport = false,
+ showSoftInputOnFocus = true,
...props
}: ComposerProps,
ref: ForwardedRef,
@@ -74,6 +75,11 @@ function Composer(
});
const [hasMultipleLines, setHasMultipleLines] = useState(false);
const [isRendered, setIsRendered] = useState(false);
+
+ // On mobile safari, the cursor will move from right to left with inputMode set to none during report transition
+ // To avoid that we should hide the cursor util the transition is finished
+ const [shouldTransparentCursor, setShouldTransparentCursor] = useState(!showSoftInputOnFocus && Browser.isMobileSafari());
+
const isScrollBarVisible = useIsScrollBarVisible(textInput, value ?? '');
const [prevScroll, setPrevScroll] = useState();
const [prevHeight, setPrevHeight] = useState();
@@ -260,6 +266,15 @@ function Composer(
setIsRendered(true);
}, []);
+ useEffect(() => {
+ if (!shouldTransparentCursor) {
+ return;
+ }
+ InteractionManager.runAfterInteractions(() => {
+ setShouldTransparentCursor(false);
+ });
+ }, [shouldTransparentCursor]);
+
const clear = useCallback(() => {
if (!textInput.current) {
return;
@@ -347,11 +362,12 @@ function Composer(
placeholderTextColor={theme.placeholderText}
ref={(el) => (textInput.current = el)}
selection={selection}
- style={[inputStyleMemo]}
+ style={[inputStyleMemo, shouldTransparentCursor ? {caretColor: 'transparent'} : undefined]}
markdownStyle={markdownStyle}
value={value}
defaultValue={defaultValue}
autoFocus={autoFocus}
+ inputMode={showSoftInputOnFocus ? 'text' : 'none'}
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...props}
onSelectionChange={addCursorPositionToSelectionChange}
diff --git a/src/components/Composer/types.ts b/src/components/Composer/types.ts
index 3df5508f1dd7..6ea3bdb2f824 100644
--- a/src/components/Composer/types.ts
+++ b/src/components/Composer/types.ts
@@ -68,6 +68,9 @@ type ComposerProps = Omit & {
/** Indicates whether the composer is in a group policy report. Used for disabling report mentioning style in markdown input */
isGroupPolicyReport?: boolean;
+
+ /** Whether to show the keyboard on focus */
+ showSoftInputOnFocus?: boolean;
};
export type {TextSelection, ComposerProps, CustomSelectionChangeEvent};
diff --git a/src/components/ConnectToNetSuiteFlow/index.tsx b/src/components/ConnectToNetSuiteFlow/index.tsx
index 1d33eb07df4f..7957896d4006 100644
--- a/src/components/ConnectToNetSuiteFlow/index.tsx
+++ b/src/components/ConnectToNetSuiteFlow/index.tsx
@@ -59,10 +59,11 @@ function ConnectToNetSuiteFlow({policyID}: ConnectToNetSuiteFlowProps) {
if (threeDotsMenuContainerRef) {
if (!shouldUseNarrowLayout) {
threeDotsMenuContainerRef.current?.measureInWindow((x, y, width, height) => {
- setReuseConnectionPopoverPosition({
- horizontal: x + width,
- vertical: y + height,
- });
+ const horizontal = x + width;
+ const vertical = y + height;
+ if (reuseConnectionPopoverPosition.horizontal !== horizontal || reuseConnectionPopoverPosition.vertical !== vertical) {
+ setReuseConnectionPopoverPosition({horizontal, vertical});
+ }
});
}
diff --git a/src/components/ConnectToSageIntacctFlow/index.tsx b/src/components/ConnectToSageIntacctFlow/index.tsx
index f93fce9c668a..807082365042 100644
--- a/src/components/ConnectToSageIntacctFlow/index.tsx
+++ b/src/components/ConnectToSageIntacctFlow/index.tsx
@@ -64,10 +64,11 @@ function ConnectToSageIntacctFlow({policyID}: ConnectToSageIntacctFlowProps) {
if (threeDotsMenuContainerRef) {
if (!shouldUseNarrowLayout) {
threeDotsMenuContainerRef.current?.measureInWindow((x, y, width, height) => {
- setReuseConnectionPopoverPosition({
- horizontal: x + width,
- vertical: y + height,
- });
+ const horizontal = x + width;
+ const vertical = y + height;
+ if (reuseConnectionPopoverPosition.horizontal !== horizontal || reuseConnectionPopoverPosition.vertical !== vertical) {
+ setReuseConnectionPopoverPosition({horizontal, vertical});
+ }
});
}
diff --git a/src/languages/es.ts b/src/languages/es.ts
index ed16213be0d0..cb7f53424958 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -4324,11 +4324,11 @@ const translations = {
planTypePage: {
planTypes: {
team: {
- label: 'Collect',
+ label: 'Recopilar',
description: 'Para equipos que buscan automatizar sus procesos.',
},
corporate: {
- label: 'Recolectar',
+ label: 'Controlar',
description: 'Para organizaciones con requisitos avanzados.',
},
},
diff --git a/src/libs/actions/EmojiPickerAction.ts b/src/libs/actions/EmojiPickerAction.ts
index e6123733b0e8..134364ddbad6 100644
--- a/src/libs/actions/EmojiPickerAction.ts
+++ b/src/libs/actions/EmojiPickerAction.ts
@@ -79,8 +79,8 @@ function hideEmojiPicker(isNavigating?: boolean) {
/**
* Whether Emoji Picker is active for the given id.
*/
-function isActive(id: string): boolean {
- if (!emojiPickerRef.current) {
+function isActive(id?: string): boolean {
+ if (!emojiPickerRef.current || !id) {
return false;
}
diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index c5f3bb442ab9..9de6b2632660 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -253,7 +253,6 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro
const {reportPendingAction, reportErrors} = ReportUtils.getReportOfflinePendingActionAndErrors(report);
const screenWrapperStyle: ViewStyle[] = [styles.appContent, styles.flex1, {marginTop: viewportOffsetTop}];
- const isEmptyChat = useMemo(() => ReportUtils.isEmptyReport(report), [report]);
const isOptimisticDelete = report?.statusNum === CONST.REPORT.STATUS_NUM.CLOSED;
const indexOfLinkedMessage = useMemo(
(): number => reportActions.findIndex((obj) => String(obj.reportActionID) === String(reportActionIDFromRoute)),
@@ -281,6 +280,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro
const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID ?? '-1'}`];
const isTopMostReportId = currentReportID === reportIDFromRoute;
const didSubscribeToReportLeavingEvents = useRef(false);
+ const [showSoftInputOnFocus, setShowSoftInputOnFocus] = useState(false);
useEffect(() => {
if (!report?.reportID || shouldHideReport) {
@@ -295,7 +295,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro
Navigation.dismissModal();
return;
}
- Navigation.goBack(ROUTES.HOME, false, true);
+ Navigation.goBack(undefined, false, true);
}, [isInNarrowPaneModal]);
let headerView = (
@@ -758,7 +758,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro
) : null}
diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx
index 7b0d6663facf..b56109b64c40 100644
--- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx
+++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx
@@ -40,7 +40,6 @@ import getPlatform from '@libs/getPlatform';
import * as KeyDownListener from '@libs/KeyboardShortcut/KeyDownPressListener';
import Parser from '@libs/Parser';
import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
-import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import updateMultilineInputRange from '@libs/updateMultilineInputRange';
import willBlurTextInputOnTapOutsideFunc from '@libs/willBlurTextInputOnTapOutside';
@@ -126,27 +125,26 @@ type ComposerWithSuggestionsProps = Partial & {
/** The ref to the next modal will open */
isNextModalWillOpenRef: MutableRefObject;
- /** Wheater chat is empty */
- isEmptyChat?: boolean;
-
/** The last report action */
lastReportAction?: OnyxEntry;
/** 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;
/** policy ID of the report */
- policyID: string;
+ policyID?: string;
+
+ /** Whether to show the keyboard on focus */
+ showSoftInputOnFocus: boolean;
+
+ /** A method to update showSoftInputOnFocus */
+ setShowSoftInputOnFocus: (value: boolean) => void;
+
+ /** Whether the main composer was hidden */
+ didHideComposerInput?: boolean;
};
type SwitchToCurrentReportProps = {
@@ -187,10 +185,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();
-
/**
* This component holds the value and selection state.
* If a component really needs access to these state values it should be put here.
@@ -201,11 +195,8 @@ function ComposerWithSuggestions(
{
// Props: Report
reportID,
- parentReportID,
includeChronos,
- isEmptyChat,
lastReportAction,
- parentReportActionID,
isGroupPolicyReport,
policyID,
@@ -236,6 +227,9 @@ function ComposerWithSuggestions(
// For testing
children,
+ showSoftInputOnFocus,
+ setShowSoftInputOnFocus,
+ didHideComposerInput,
}: ComposerWithSuggestionsProps,
ref: ForwardedRef,
) {
@@ -257,14 +251,12 @@ function ComposerWithSuggestions(
}
return draftComment;
});
+
const commentRef = useRef(value);
- const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
const [modal] = useOnyx(ONYXKEYS.MODAL);
const [preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE] = useOnyx(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, {selector: EmojiUtils.getPreferredSkinToneIndex});
const [editFocused] = useOnyx(ONYXKEYS.INPUT_FOCUSED);
- // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
- const [parentReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID || '-1'}`, {canEvict: false, initWithStoredValues: false});
const lastTextRef = useRef(value);
useEffect(() => {
@@ -274,13 +266,7 @@ function ComposerWithSuggestions(
const {shouldUseNarrowLayout} = useResponsiveLayout();
const maxComposerLines = shouldUseNarrowLayout ? CONST.COMPOSER.MAX_LINES_SMALL_SCREEN : CONST.COMPOSER.MAX_LINES;
- const parentReportAction = useMemo(() => parentReportActions?.[parentReportActionID ?? '-1'], [parentReportActionID, parentReportActions]);
- const shouldAutoFocus =
- !modal?.isVisible &&
- Modal.areAllModalsHidden() &&
- isFocused &&
- (shouldFocusInputOnScreenFocus || (isEmptyChat && !ReportActionsUtils.isTransactionThread(parentReportAction) && !ReportUtils.isTaskReport(report))) &&
- shouldShowComposeInput;
+ const shouldAutoFocus = !modal?.isVisible && shouldShowComposeInput && Modal.areAllModalsHidden() && isFocused && !didHideComposerInput;
const valueRef = useRef(value);
valueRef.current = value;
@@ -643,7 +629,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 && canFocusInputOnScreenFocus())) &&
+ !isNextModalWillOpenRef.current &&
+ !modal?.isVisible &&
+ isFocused &&
+ (!!prevIsModalVisible || !prevIsFocused)
+ )
+ ) {
return;
}
@@ -775,6 +769,19 @@ function ComposerWithSuggestions(
onScroll={hideSuggestionMenu}
shouldContainScroll={Browser.isMobileSafari()}
isGroupPolicyReport={isGroupPolicyReport}
+ showSoftInputOnFocus={showSoftInputOnFocus}
+ onTouchStart={() => {
+ if (showSoftInputOnFocus) {
+ return;
+ }
+ if (Browser.isMobileSafari()) {
+ setTimeout(() => {
+ setShowSoftInputOnFocus(true);
+ }, CONST.ANIMATED_TRANSITION);
+ return;
+ }
+ setShowSoftInputOnFocus(true);
+ }}
/>
diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx
index 78d3288d05f0..c3caaf16a3ef 100644
--- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx
+++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx
@@ -64,7 +64,7 @@ type SuggestionsRef = {
getIsSuggestionsMenuVisible: () => boolean;
};
-type ReportActionComposeProps = Pick & {
+type ReportActionComposeProps = Pick & {
/** A method to call when the form is submitted */
onSubmit: (newComment: string) => void;
@@ -88,6 +88,15 @@ type ReportActionComposeProps = Pick void;
+
+ /** Whether the main composer was hidden */
+ didHideComposerInput?: boolean;
};
// We want consistent auto focus behavior on input between native and mWeb so we have some auto focus management code that will
@@ -107,11 +116,13 @@ function ReportActionCompose({
report,
reportID,
isReportReadyForDisplay = true,
- isEmptyChat,
lastReportAction,
shouldShowEducationalTooltip,
+ showSoftInputOnFocus,
onComposerFocus,
onComposerBlur,
+ setShowSoftInputOnFocus,
+ didHideComposerInput,
}: ReportActionComposeProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
@@ -324,7 +335,7 @@ function ReportActionCompose({
// We are returning a callback here as we want to incoke the method on unmount only
useEffect(
() => () => {
- if (!EmojiPickerActions.isActive(report?.reportID ?? '-1')) {
+ if (!EmojiPickerActions.isActive(report?.reportID)) {
return;
}
EmojiPickerActions.hideEmojiPicker();
@@ -487,12 +498,9 @@ function ReportActionCompose({
isScrollLikelyLayoutTriggered={isScrollLikelyLayoutTriggered}
raiseIsScrollLikelyLayoutTriggered={raiseIsScrollLikelyLayoutTriggered}
reportID={reportID}
- policyID={report?.policyID ?? '-1'}
- parentReportID={report?.parentReportID}
- parentReportActionID={report?.parentReportActionID}
+ policyID={report?.policyID}
includeChronos={ReportUtils.chatIncludesChronos(report)}
isGroupPolicyReport={isGroupPolicyReport}
- isEmptyChat={isEmptyChat}
lastReportAction={lastReportAction}
isMenuVisible={isMenuVisible}
inputPlaceholder={inputPlaceholder}
@@ -507,7 +515,10 @@ function ReportActionCompose({
onFocus={onFocus}
onBlur={onBlur}
measureParentContainer={measureContainer}
+ showSoftInputOnFocus={showSoftInputOnFocus}
+ setShowSoftInputOnFocus={setShowSoftInputOnFocus}
onValueChange={onValueChange}
+ didHideComposerInput={didHideComposerInput}
/>
{
diff --git a/src/pages/home/report/ReportFooter.tsx b/src/pages/home/report/ReportFooter.tsx
index f46e2a9476b3..174549806db9 100644
--- a/src/pages/home/report/ReportFooter.tsx
+++ b/src/pages/home/report/ReportFooter.tsx
@@ -1,6 +1,6 @@
import {Str} from 'expensify-common';
import lodashIsEqual from 'lodash/isEqual';
-import React, {memo, useCallback} from 'react';
+import React, {memo, useCallback, useEffect, useState} from 'react';
import {Keyboard, View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
@@ -45,9 +45,6 @@ type ReportFooterProps = {
/** The last report action */
lastReportAction?: OnyxEntry;
- /** Whether the chat is empty */
- isEmptyChat?: boolean;
-
/** The pending action when we are adding a chat */
pendingAction?: PendingAction;
@@ -62,6 +59,12 @@ type ReportFooterProps = {
/** A method to call when the input is blur */
onComposerBlur: () => void;
+
+ /** Whether to show the keyboard on focus */
+ showSoftInputOnFocus: boolean;
+
+ /** A method to update showSoftInputOnFocus */
+ setShowSoftInputOnFocus: (value: boolean) => void;
};
function ReportFooter({
@@ -70,11 +73,12 @@ function ReportFooter({
report = {reportID: '-1'},
reportMetadata,
policy,
- isEmptyChat = true,
isReportReadyForDisplay = true,
isComposerFullSize = false,
+ showSoftInputOnFocus,
onComposerBlur,
onComposerFocus,
+ setShowSoftInputOnFocus,
}: ReportFooterProps) {
const styles = useThemeStyles();
const {isOffline} = useNetwork();
@@ -99,7 +103,7 @@ function ReportFooter({
}
},
});
- const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID ?? -1}`);
+ const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`);
const chatFooterStyles = {...styles.chatFooter, minHeight: !isOffline ? CONST.CHAT_FOOTER_MIN_HEIGHT : 0};
const isArchivedRoom = ReportUtils.isArchivedRoom(report, reportNameValuePairs);
@@ -168,6 +172,15 @@ function ReportFooter({
[report.reportID, handleCreateTask],
);
+ const [didHideComposerInput, setDidHideComposerInput] = useState(!shouldShowComposeInput);
+
+ useEffect(() => {
+ if (didHideComposerInput || shouldShowComposeInput) {
+ return;
+ }
+ setDidHideComposerInput(true);
+ }, [shouldShowComposeInput, didHideComposerInput]);
+
return (
<>
{!!shouldHideComposer && (
@@ -209,12 +222,14 @@ function ReportFooter({
onComposerBlur={onComposerBlur}
reportID={report.reportID}
report={report}
- isEmptyChat={isEmptyChat}
lastReportAction={lastReportAction}
pendingAction={pendingAction}
isComposerFullSize={isComposerFullSize}
isReportReadyForDisplay={isReportReadyForDisplay}
shouldShowEducationalTooltip={didScreenTransitionEnd && shouldShowEducationalTooltip}
+ showSoftInputOnFocus={showSoftInputOnFocus}
+ setShowSoftInputOnFocus={setShowSoftInputOnFocus}
+ didHideComposerInput={didHideComposerInput}
/>
@@ -231,9 +246,9 @@ export default memo(
lodashIsEqual(prevProps.report, nextProps.report) &&
prevProps.pendingAction === nextProps.pendingAction &&
prevProps.isComposerFullSize === nextProps.isComposerFullSize &&
- prevProps.isEmptyChat === nextProps.isEmptyChat &&
prevProps.lastReportAction === nextProps.lastReportAction &&
prevProps.isReportReadyForDisplay === nextProps.isReportReadyForDisplay &&
+ prevProps.showSoftInputOnFocus === nextProps.showSoftInputOnFocus &&
lodashIsEqual(prevProps.reportMetadata, nextProps.reportMetadata) &&
lodashIsEqual(prevProps.policy?.employeeList, nextProps.policy?.employeeList) &&
lodashIsEqual(prevProps.policy?.role, nextProps.policy?.role),
diff --git a/src/pages/workspace/accounting/intacct/EnterSageIntacctCredentialsPage.tsx b/src/pages/workspace/accounting/intacct/EnterSageIntacctCredentialsPage.tsx
index 29474431cd72..7ac4329c3a50 100644
--- a/src/pages/workspace/accounting/intacct/EnterSageIntacctCredentialsPage.tsx
+++ b/src/pages/workspace/accounting/intacct/EnterSageIntacctCredentialsPage.tsx
@@ -7,6 +7,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import TextInput from '@components/TextInput';
+import useAutoFocusInput from '@hooks/useAutoFocusInput';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import {connectToSageIntacct} from '@libs/actions/connections/SageIntacct';
@@ -24,6 +25,7 @@ type SageIntacctPrerequisitesPageProps = PlatformStackScreenProps
{translate('workspace.intacct.enterCredentials')}
- {formItems.map((formItem) => (
+ {formItems.map((formItem, index) => (
{}}
/>
);