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

Simplify Global Create menu #25564

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
65edf96
English and spanish translations
MaciejSWM Aug 21, 2023
d0ef97a
Assign icons to new tabs
MaciejSWM Aug 21, 2023
3e61212
Checkbox can become a button
MaciejSWM Aug 21, 2023
31ba218
Routes and consts
MaciejSWM Aug 21, 2023
8e26741
Simplified FAB
MaciejSWM Aug 21, 2023
d0362c8
Linking config for nested tab navigation
MaciejSWM Aug 21, 2023
0e46476
Adjust stack navigators
MaciejSWM Aug 21, 2023
725ae24
Shortcut for new chat modal
MaciejSWM Aug 21, 2023
cb8d751
New Chat modal with tabs
MaciejSWM Aug 21, 2023
6bcc194
Money Request split and request unified
MaciejSWM Aug 21, 2023
cd2780a
Run prettier
MaciejSWM Aug 21, 2023
2e5f42e
Remove unused translations
MaciejSWM Aug 21, 2023
b3fa521
Remove unused linking config relating new group
MaciejSWM Aug 21, 2023
70a7032
Add missing Tab constants
MaciejSWM Aug 21, 2023
45d606a
Always navigate to NEW route - tab will be auto-selected
MaciejSWM Aug 21, 2023
79a02d5
Add space to the left of the button
MaciejSWM Aug 21, 2023
cdcea69
Merge branch 'main' into simplify-global-create-menu
MaciejSWM Aug 24, 2023
94b8c0d
Merge branch 'main' into simplify-global-create-menu
MaciejSWM Aug 28, 2023
eb01b1f
Merge branch 'main' into simplify-global-create-menu
MaciejSWM Aug 28, 2023
a221a57
Fix rerender not triggered when translation changes
MaciejSWM Aug 29, 2023
fe065c1
Add missing default props
MaciejSWM Aug 29, 2023
166a27a
Prefer empty function as default prop instead of undefined
MaciejSWM Aug 29, 2023
58cd065
Destructure props
MaciejSWM Aug 29, 2023
3fb9ec0
Run prettier
MaciejSWM Aug 29, 2023
bad65d4
Merge branch 'main' into simplify-global-create-menu
MaciejSWM Aug 29, 2023
90afb07
Fix freezing due to autofocus issues
MaciejSWM Aug 29, 2023
e4348e1
Delete unused import
MaciejSWM Aug 29, 2023
9b3c9af
Add title translations
MaciejSWM Aug 29, 2023
afb89eb
Fix tab switching freezing
MaciejSWM Aug 30, 2023
ad56566
Merge branch 'main' into simplify-global-create-menu
MaciejSWM Aug 30, 2023
0f1648d
Enable autofocus for New Room tab
MaciejSWM Aug 30, 2023
b7907a5
Don't call onBlur when tab is switched
MaciejSWM Aug 31, 2023
8ad5472
Precalculate prefixCharacter width instead of calculating on the fly
MaciejSWM Aug 31, 2023
d2a2e50
Add isFocused to prop types
MaciejSWM Aug 31, 2023
a2e3350
Merge branch 'main' into simplify-global-create-menu
MaciejSWM Sep 6, 2023
d63e67c
Pass position prop to TabSelector
MaciejSWM Sep 6, 2023
fb2e415
Remove nesting
MaciejSWM Sep 6, 2023
3826fcb
Fix button being behing keyboard for NewChatScreen
MaciejSWM Sep 6, 2023
85519f7
WIP Fix button being behind keyboard for NewRoom
MaciejSWM Sep 6, 2023
45085a5
Fix NewRoom and improve NewChat keyboard avoiding views
MaciejSWM Sep 6, 2023
df48bd8
Merge branch 'main' into simplify-global-create-menu
MaciejSWM Sep 6, 2023
8a1cf12
Fix height calculations when beta flags are off
MaciejSWM Sep 6, 2023
ccd86bd
Lint + Prettier
MaciejSWM Sep 6, 2023
8ebe2a6
Merge branch 'main' into simplify-global-create-menu
MaciejSWM Sep 12, 2023
02df4e3
Fix infinite navigation back problem
MaciejSWM Sep 12, 2023
602ba38
Merge branch 'main' into simplify-global-create-menu
MaciejSWM Sep 13, 2023
22234f4
Merge branch 'main' into simplify-global-create-menu
MaciejSWM Sep 14, 2023
2cd6be3
Post-merge fixes
MaciejSWM Sep 14, 2023
058ecfb
Add blank lines
MaciejSWM Sep 14, 2023
cc67090
Drop invalid param
MaciejSWM Sep 14, 2023
5b8e6a8
Combine two switch statements into one
MaciejSWM Sep 14, 2023
2ee0a42
Merge branch 'main' into simplify-global-create-menu
MaciejSWM Sep 15, 2023
5788291
Fix autofocus being lost when screen mounted
MaciejSWM Sep 15, 2023
5d28fdb
Fix autofocus being lost when screen mounted
MaciejSWM Sep 15, 2023
70beac8
Autofocus fix
MaciejSWM Sep 15, 2023
0d2d560
Autofocus after animation ends
MaciejSWM Sep 15, 2023
7ff6e00
Add missing semicolons
MaciejSWM Sep 15, 2023
f45b2ca
Further autofocus improvements
MaciejSWM Sep 15, 2023
90b96de
Hacky blur not needed anymore
MaciejSWM Sep 15, 2023
4c478d3
Check if ref not null
MaciejSWM Sep 15, 2023
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
7 changes: 5 additions & 2 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,8 @@ const CONST = {
},
type: KEYBOARD_SHORTCUT_NAVIGATION_TYPE,
},
NEW_GROUP: {
descriptionKey: 'newGroup',
NEW_CHAT: {
descriptionKey: 'newChat',
shortcutKey: 'K',
modifiers: ['CTRL', 'SHIFT'],
trigger: {
Expand Down Expand Up @@ -2624,6 +2624,9 @@ const CONST = {
DISABLED: 'DISABLED',
},
TAB: {
NEW_CHAT_TAB_ID: 'NewChatTab',
NEW_CHAT: 'chat',
NEW_ROOM: 'room',
RECEIPT_TAB_ID: 'ReceiptTab',
MANUAL: 'manual',
SCAN: 'scan',
Expand Down
5 changes: 2 additions & 3 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ type ParseReportRouteParams = {

const REPORT = 'r';
const IOU_REQUEST = 'request/new';
const IOU_BILL = 'split/new';
const IOU_SEND = 'send/new';
const NEW_TASK = 'new/task';
const SETTINGS_PERSONAL_DETAILS = 'settings/profile/personal-details';
Expand Down Expand Up @@ -67,8 +66,9 @@ export default {
SETTINGS_2FA: 'settings/security/two-factor-auth',
SETTINGS_STATUS,
SETTINGS_STATUS_SET,
NEW_GROUP: 'new/group',
NEW: 'new',
NEW_CHAT: 'new/chat',
NEW_ROOM: 'new/room',
NEW_TASK,
REPORT,
REPORT_WITH_ID: 'r/:reportID?/:reportActionID?',
Expand All @@ -86,7 +86,6 @@ export default {
CONCIERGE: 'concierge',

IOU_REQUEST,
IOU_BILL,
IOU_SEND,

// To see the available iouType, please refer to CONST.IOU.MONEY_REQUEST_TYPE
Expand Down
35 changes: 34 additions & 1 deletion src/components/OptionRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as StyleUtils from '../styles/StyleUtils';
import optionPropTypes from './optionPropTypes';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
import Button from './Button';
import MultipleAvatars from './MultipleAvatars';
import Hoverable from './Hoverable';
import DisplayNames from './DisplayNames';
Expand Down Expand Up @@ -39,6 +40,15 @@ const propTypes = {
/** Whether we should show the selected state */
showSelectedState: PropTypes.bool,

/** Whether to show a button pill instead of a tickbox */
shouldShowSelectedStateAsButton: PropTypes.bool,

/** Text for button pill */
selectedStateButtonText: PropTypes.string,

/** Callback to fire when the multiple selector (tickbox or button) is clicked */
onSelectedStatePressed: PropTypes.func,

/** Whether we highlight selected option */
highlightSelected: PropTypes.bool,

Expand Down Expand Up @@ -71,6 +81,9 @@ const propTypes = {
const defaultProps = {
hoverStyle: styles.sidebarLinkHover,
showSelectedState: false,
shouldShowSelectedStateAsButton: false,
selectedStateButtonText: 'Select',
onSelectedStatePressed: () => {},
highlightSelected: false,
isSelected: false,
boldStyle: false,
Expand Down Expand Up @@ -100,6 +113,7 @@ class OptionRow extends Component {
this.props.isMultilineSupported !== nextProps.isMultilineSupported ||
this.props.isSelected !== nextProps.isSelected ||
this.props.shouldHaveOptionSeparator !== nextProps.shouldHaveOptionSeparator ||
this.props.selectedStateButtonText !== nextProps.selectedStateButtonText ||
this.props.showSelectedState !== nextProps.showSelectedState ||
this.props.highlightSelected !== nextProps.highlightSelected ||
this.props.showTitleTooltip !== nextProps.showTitleTooltip ||
Expand Down Expand Up @@ -259,7 +273,26 @@ class OptionRow extends Component {
/>
</View>
)}
{this.props.showSelectedState && <SelectCircle isChecked={this.props.isSelected} />}
{this.props.showSelectedState && (
<>
{this.props.shouldShowSelectedStateAsButton && !this.props.isSelected ? (
<Button
style={[styles.pl2]}
text={this.props.selectedStateButtonText}
onPress={() => this.props.onSelectedStatePressed(this.props.option)}
small
/>
) : (
<PressableWithFeedback
onPress={() => this.props.onSelectedStatePressed(this.props.option)}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.CHECKBOX}
accessibilityLabel={CONST.ACCESSIBILITY_ROLE.CHECKBOX}
>
<SelectCircle isChecked={this.props.isSelected} />
</PressableWithFeedback>
Comment on lines +286 to +292
Copy link
Contributor

Choose a reason for hiding this comment

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

Wrapping with PressableWithFeedback here caused super minor inconsistency - Cursor Remains as Hand Icon on Checkbox in "Paid By" Section During Split Bill
We should have passed disabled={isDisabled}.

)}
</>
)}
{this.props.isSelected && this.props.highlightSelected && (
<View style={styles.defaultCheckmarkWrapper}>
<Icon
Expand Down
6 changes: 6 additions & 0 deletions src/components/OptionsList/BaseOptionsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ function BaseOptionsList({
shouldDisableRowInnerPadding,
disableFocusOptions,
canSelectMultipleOptions,
shouldShowMultipleOptionSelectorAsButton,
multipleOptionSelectorButtonText,
onAddToSelection,
highlightSelectedOptions,
onSelectRow,
boldStyle,
Expand Down Expand Up @@ -191,6 +194,9 @@ function BaseOptionsList({
onSelectRow={onSelectRow}
isSelected={isSelected}
showSelectedState={canSelectMultipleOptions}
shouldShowSelectedStateAsButton={shouldShowMultipleOptionSelectorAsButton}
selectedStateButtonText={multipleOptionSelectorButtonText}
onSelectedStatePressed={onAddToSelection}
highlightSelected={highlightSelectedOptions}
boldStyle={boldStyle}
isDisabled={isItemDisabled}
Expand Down
28 changes: 17 additions & 11 deletions src/components/OptionsSelector/BaseOptionsSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import _ from 'underscore';
import lodashGet from 'lodash/get';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {View, InteractionManager} from 'react-native';
import {View} from 'react-native';
import Button from '../Button';
import FixedFooter from '../FixedFooter';
import OptionsList from '../OptionsList';
Expand All @@ -16,11 +16,9 @@ import KeyboardShortcut from '../../libs/KeyboardShortcut';
import {propTypes as optionsSelectorPropTypes, defaultProps as optionsSelectorDefaultProps} from './optionsSelectorPropTypes';
import setSelection from '../../libs/setSelection';
import compose from '../../libs/compose';
import getPlatform from '../../libs/getPlatform';

const propTypes = {
/** Whether we should wait before focusing the TextInput, useful when using transitions on Android */
shouldDelayFocus: PropTypes.bool,

/** padding bottom style of safe area */
safeAreaPaddingBottomStyle: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),

Expand Down Expand Up @@ -70,6 +68,12 @@ class BaseOptionsSelector extends Component {
componentDidMount() {
this.subscribeToKeyboardShortcut();

if (this.props.isFocused && this.props.autoFocus && this.textInput) {
setTimeout(() => {
this.textInput.focus();
}, CONST.ANIMATED_TRANSITION);
}

this.scrollToIndex(this.props.selectedOptions.length ? 0 : this.state.focusedIndex, false);
}

Expand All @@ -82,12 +86,13 @@ class BaseOptionsSelector extends Component {
}
}

if (this.textInput && this.props.autoFocus && !prevProps.isFocused && this.props.isFocused) {
InteractionManager.runAfterInteractions(() => {
// If we automatically focus on a text input when mounting a component,
// let's automatically focus on it when the component updates as well (eg, when navigating back from a page)
// Screen coming back into focus, for example
// when doing Cmd+Shift+K, then Cmd+K, then Cmd+Shift+K.
// Only applies to platforms that support keyboard shortcuts
if ([CONST.PLATFORM.DESKTOP, CONST.PLATFORM.WEB].includes(getPlatform()) && !prevProps.isFocused && this.props.isFocused && this.props.autoFocus && this.textInput) {
setTimeout(() => {
this.textInput.focus();
});
}, CONST.ANIMATED_TRANSITION);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

We cannot simply remove this code. This breaks auto focus in android.

Copy link
Contributor

Choose a reason for hiding this comment

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

We might consider using focusWithDelay.js. Didn't investigate it deeply, just a quick thought

Copy link
Contributor

Choose a reason for hiding this comment

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

focusWithDelay.js is for composer only.

Let's follow this pattern Rory suggested

const focusTimeoutRef = useRef(null);
useFocusEffect(useCallback(() => {
    focusTimeoutRef.current = setTimeout(textInputRef.current.focus, CONST.ANIMATED_TRANSITION);
    return () => {
        if (!focusTimeoutRef.current) {
            return;
        }
        clearTimeout(focusTimeoutRef.current);
    };
}, []));

Copy link
Contributor

Choose a reason for hiding this comment

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

That pattern was not followed correctly. clearTimeout is missing in useEffect cleanup function.
So it introduced this bug - #29114

if (_.isEqual(this.props.sections, prevProps.sections)) {
Expand Down Expand Up @@ -359,8 +364,6 @@ class BaseOptionsSelector extends Component {
selectTextOnFocus
blurOnSubmit={Boolean(this.state.allOptions.length)}
spellCheck={false}
autoFocus={this.props.autoFocus}
shouldDelayFocus={this.props.shouldDelayFocus}
/>
);
const optionsList = (
Expand All @@ -372,6 +375,9 @@ class BaseOptionsSelector extends Component {
focusedIndex={this.state.focusedIndex}
selectedOptions={this.props.selectedOptions}
canSelectMultipleOptions={this.props.canSelectMultipleOptions}
shouldShowMultipleOptionSelectorAsButton={this.props.shouldShowMultipleOptionSelectorAsButton}
multipleOptionSelectorButtonText={this.props.multipleOptionSelectorButtonText}
onAddToSelection={this.props.onAddToSelection}
hideSectionHeaders={this.props.hideSectionHeaders}
headerMessage={this.props.headerMessage}
boldStyle={this.props.boldStyle}
Expand Down
12 changes: 12 additions & 0 deletions src/components/OptionsSelector/optionsSelectorPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ const propTypes = {
/** Whether we can select multiple options */
canSelectMultipleOptions: PropTypes.bool,

/** Whether to show a button pill instead of a standard tickbox */
shouldShowMultipleOptionSelectorAsButton: PropTypes.bool,

/** Text for button pill */
multipleOptionSelectorButtonText: PropTypes.string,

/** Callback to fire when the multiple selector (tickbox or button) is clicked */
onAddToSelection: PropTypes.func,
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here 😄

Copy link
Contributor

Choose a reason for hiding this comment

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

I was just about pointing this out :D


Comment on lines +56 to +64
Copy link
Contributor

Choose a reason for hiding this comment

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

These are missing in defaultProps

/** Whether we highlight selected options */
highlightSelectedOptions: PropTypes.bool,

Expand Down Expand Up @@ -125,6 +134,9 @@ const defaultProps = {
selectedOptions: [],
headerMessage: '',
canSelectMultipleOptions: false,
shouldShowMultipleOptionSelectorAsButton: false,
multipleOptionSelectorButtonText: '',
onAddToSelection: () => {},
highlightSelectedOptions: false,
hideSectionHeaders: false,
boldStyle: false,
Expand Down
7 changes: 4 additions & 3 deletions src/components/RoomNameInput/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import useLocalize from '../../hooks/useLocalize';
import * as roomNameInputPropTypes from './roomNameInputPropTypes';
import * as RoomNameInputUtils from '../../libs/RoomNameInputUtils';

function RoomNameInput({autoFocus, disabled, errorText, forwardedRef, value, onBlur, onChangeText, onInputChange}) {
function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, value, onBlur, onChangeText, onInputChange, shouldDelayFocus}) {
const {translate} = useLocalize();

const [selection, setSelection] = useState();
Expand Down Expand Up @@ -57,8 +57,9 @@ function RoomNameInput({autoFocus, disabled, errorText, forwardedRef, value, onB
onSelectionChange={(event) => setSelection(event.nativeEvent.selection)}
errorText={errorText}
autoCapitalize="none"
onBlur={onBlur}
autoFocus={autoFocus}
onBlur={() => isFocused && onBlur()}
shouldDelayFocus={shouldDelayFocus}
autoFocus={isFocused && autoFocus}
maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH}
spellCheck={false}
/>
Expand Down
6 changes: 3 additions & 3 deletions src/components/RoomNameInput/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as roomNameInputPropTypes from './roomNameInputPropTypes';
import * as RoomNameInputUtils from '../../libs/RoomNameInputUtils';
import getOperatingSystem from '../../libs/getOperatingSystem';

function RoomNameInput({autoFocus, disabled, errorText, forwardedRef, value, onBlur, onChangeText, onInputChange, shouldDelayFocus}) {
function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, value, onBlur, onChangeText, onInputChange, shouldDelayFocus}) {
const {translate} = useLocalize();

/**
Expand Down Expand Up @@ -41,8 +41,8 @@ function RoomNameInput({autoFocus, disabled, errorText, forwardedRef, value, onB
errorText={errorText}
maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH}
keyboardType={keyboardType} // this is a bit hacky solution to a RN issue https://github.com/facebook/react-native/issues/27449
onBlur={onBlur}
autoFocus={autoFocus}
onBlur={() => isFocused && onBlur()}
autoFocus={isFocused && autoFocus}
autoCapitalize="none"
shouldDelayFocus={shouldDelayFocus}
/>
Expand Down
3 changes: 3 additions & 0 deletions src/components/RoomNameInput/roomNameInputPropTypes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import PropTypes from 'prop-types';
import {withNavigationFocusPropTypes} from '../withNavigationFocus';

const propTypes = {
/** Callback to execute when the text input is modified correctly */
Expand Down Expand Up @@ -27,6 +28,8 @@ const propTypes = {

/** Whether we should wait before focusing the TextInput, useful when using transitions on Android */
shouldDelayFocus: PropTypes.bool,

...withNavigationFocusPropTypes,
};

const defaultProps = {
Expand Down
31 changes: 13 additions & 18 deletions src/components/TabSelector/TabSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,20 @@ const defaultProps = {
},
};

const getIcon = (route) => {
const getIconAndTitle = (route, translate) => {
switch (route) {
case CONST.TAB.MANUAL:
return {icon: Expensicons.Pencil, title: translate('tabSelector.manual')};
case CONST.TAB.SCAN:
return Expensicons.Receipt;
return {icon: Expensicons.Receipt, title: translate('tabSelector.scan')};
case CONST.TAB.NEW_CHAT:
return {icon: Expensicons.User, title: translate('tabSelector.chat')};
case CONST.TAB.NEW_ROOM:
return {icon: Expensicons.Hashtag, title: translate('tabSelector.room')};
case CONST.TAB.DISTANCE:
return Expensicons.Car;
return {icon: Expensicons.Car, title: translate('common.distance')};
default:
return Expensicons.Pencil;
}
};

const getTitle = (route, translate) => {
switch (route) {
case CONST.TAB.SCAN:
return translate('tabSelector.scan');
case CONST.TAB.DISTANCE:
return translate('common.distance');
default:
return translate('tabSelector.manual');
throw new Error(`Route ${route} has no icon nor title set.`);
}
};

Expand Down Expand Up @@ -94,8 +89,8 @@ function TabSelector({state, navigation, onTabPress, position}) {
const activeOpacity = getOpacity(position, state.routes.length, index, true);
const inactiveOpacity = getOpacity(position, state.routes.length, index, false);
const backgroundColor = getBackgroundColor(position, state.routes.length, index);

const isFocused = index === state.index;
const {icon, title} = getIconAndTitle(route.name, translate);

const onPress = () => {
if (isFocused) {
Expand All @@ -119,8 +114,8 @@ function TabSelector({state, navigation, onTabPress, position}) {
return (
<TabSelectorItem
key={route.name}
title={getTitle(route.name, translate)}
icon={getIcon(route.name)}
icon={icon}
title={title}
onPress={onPress}
activeOpacity={activeOpacity}
inactiveOpacity={inactiveOpacity}
Expand Down
21 changes: 15 additions & 6 deletions src/components/TextInput/BaseTextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ function BaseTextInput(props) {
const [passwordHidden, setPasswordHidden] = useState(props.secureTextEntry);
const [textInputWidth, setTextInputWidth] = useState(0);
const [textInputHeight, setTextInputHeight] = useState(0);
const [prefixWidth, setPrefixWidth] = useState(0);
const [height, setHeight] = useState(variables.componentSizeLarge);
const [width, setWidth] = useState();
const labelScale = useRef(new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_SCALE : styleConst.INACTIVE_LABEL_SCALE)).current;
Expand Down Expand Up @@ -221,9 +220,20 @@ function BaseTextInput(props) {
setPasswordHidden((prevPasswordHidden) => !prevPasswordHidden);
}, []);

const storePrefixLayoutDimensions = useCallback((event) => {
setPrefixWidth(Math.abs(event.nativeEvent.layout.width));
}, []);
// When adding a new prefix character, adjust this method to add expected character width.
// This is because character width isn't known before it's rendered to the screen, and once it's rendered,
// it's too late to calculate it's width because the change in padding would cause a visible jump.
// Some characters are wider than the others when rendered, e.g. '@' vs '#'. Chosen font-family and font-size
// also have an impact on the width of the character, but as long as there's only one font-family and one font-size,
// this method will produce reliable results.
const getCharacterPadding = (prefix) => {
switch (prefix) {
case CONST.POLICY.ROOM_PREFIX:
return 10;
default:
throw new Error(`Prefix ${prefix} has no padding assigned.`);
}
};

// eslint-disable-next-line react/forbid-foreign-prop-types
const inputProps = _.omit(props, _.keys(baseTextInputPropTypes.propTypes));
Expand Down Expand Up @@ -295,7 +305,6 @@ function BaseTextInput(props) {
pointerEvents="none"
selectable={false}
style={[styles.textInputPrefix, !hasLabel && styles.pv0]}
onLayout={storePrefixLayoutDimensions}
>
{props.prefixCharacter}
</Text>
Expand All @@ -322,7 +331,7 @@ function BaseTextInput(props) {
styles.w100,
props.inputStyle,
(!hasLabel || isMultiline) && styles.pv0,
props.prefixCharacter && StyleUtils.getPaddingLeft(prefixWidth + styles.pl1.paddingLeft),
props.prefixCharacter && StyleUtils.getPaddingLeft(getCharacterPadding(props.prefixCharacter) + styles.pl1.paddingLeft),
props.secureTextEntry && styles.secureInput,

// Explicitly remove `lineHeight` from single line inputs so that long text doesn't disappear
Expand Down
Loading
Loading