Skip to content

Commit

Permalink
Merge pull request #30532 from Expensify/revert-29771-from-migration/…
Browse files Browse the repository at this point in the history
…room-name-page

Revert "[Form Provider Refactor] RoomNameInput"

(cherry picked from commit f3e2cde)
  • Loading branch information
pecanoro authored and OSBotify committed Oct 27, 2023
1 parent c7a6420 commit 399b1bd
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 49 deletions.
24 changes: 8 additions & 16 deletions src/components/Form/FormProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,13 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC
.first()
.value() || '';

const value = !_.isUndefined(inputValues[`${inputID}ToDisplay`]) ? inputValues[`${inputID}ToDisplay`] : inputValues[inputID];

return {
...propsToParse,
ref: newRef,
inputID,
key: propsToParse.key || inputID,
errorText: errors[inputID] || fieldErrorMessage,
value,
value: inputValues[inputID],
// As the text input is controlled, we never set the defaultValue prop
// as this is already happening by the value prop.
defaultValue: undefined,
Expand Down Expand Up @@ -277,19 +275,13 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC
propsToParse.onBlur(event);
}
},
onInputChange: (inputValue, key) => {
onInputChange: (value, key) => {
const inputKey = key || inputID;
setInputValues((prevState) => {
const newState = _.isFunction(propsToParse.valueParser)
? {
...prevState,
[inputKey]: propsToParse.valueParser(inputValue),
[`${inputKey}ToDisplay`]: inputValue,
}
: {
...prevState,
[inputKey]: inputValue,
};
const newState = {
...prevState,
[inputKey]: value,
};

if (shouldValidateOnChange) {
onValidate(newState);
Expand All @@ -298,11 +290,11 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC
});

if (propsToParse.shouldSaveDraft) {
FormActions.setDraftValues(propsToParse.formID, {[inputKey]: inputValue});
FormActions.setDraftValues(propsToParse.formID, {[inputKey]: value});
}

if (_.isFunction(propsToParse.onValueChange)) {
propsToParse.onValueChange(inputValue, inputKey);
propsToParse.onValueChange(value, inputKey);
}
},
};
Expand Down
2 changes: 0 additions & 2 deletions src/components/Form/InputWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ const propTypes = {
inputID: PropTypes.string.isRequired,
valueType: PropTypes.string,
forwardedRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({current: PropTypes.instanceOf(React.Component)})]),
valueParser: PropTypes.func,
};

const defaultProps = {
forwardedRef: undefined,
valueType: 'string',
valueParser: undefined,
};

function InputWrapper(props) {
Expand Down
50 changes: 39 additions & 11 deletions src/components/RoomNameInput/index.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,68 @@
import React from 'react';
import React, {useState} from 'react';
import _ from 'underscore';
import CONST from '../../CONST';
import TextInput from '../TextInput';
import useLocalize from '../../hooks/useLocalize';
import * as roomNameInputPropTypes from './roomNameInputPropTypes';
import InputWrapper from '../Form/InputWrapper';
import getOperatingSystem from '../../libs/getOperatingSystem';
import * as RoomNameInputUtils from '../../libs/RoomNameInputUtils';

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

const keyboardType = getOperatingSystem() === CONST.OS.IOS ? CONST.KEYBOARD_TYPE.ASCII_CAPABLE : CONST.KEYBOARD_TYPE.VISIBLE_PASSWORD;
const [selection, setSelection] = useState();

const valueParser = (roomName) => RoomNameInputUtils.modifyRoomName(roomName);
/**
* Calls the onChangeText callback with a modified room name
* @param {Event} event
*/
const setModifiedRoomName = (event) => {
const roomName = event.nativeEvent.text;
const modifiedRoomName = RoomNameInputUtils.modifyRoomName(roomName);
onChangeText(modifiedRoomName);

// if custom component has onInputChange, use it to trigger changes (Form input)
if (_.isFunction(onInputChange)) {
onInputChange(modifiedRoomName);
}

// Prevent cursor jump behaviour:
// Check if newRoomNameWithHash is the same as modifiedRoomName
// If it is then the room name is valid (does not contain unallowed characters); no action required
// If not then the room name contains unvalid characters and we must adjust the cursor position manually
// Read more: https://github.com/Expensify/App/issues/12741
const oldRoomNameWithHash = value || '';
const newRoomNameWithHash = `${CONST.POLICY.ROOM_PREFIX}${roomName}`;
if (modifiedRoomName !== newRoomNameWithHash) {
const offset = modifiedRoomName.length - oldRoomNameWithHash.length;
const newSelection = {
start: selection.start + offset,
end: selection.end + offset,
};
setSelection(newSelection);
}
};

return (
<InputWrapper
InputComponent={TextInput}
inputID={inputID}
<TextInput
ref={forwardedRef}
disabled={disabled}
label={translate('newRoomPage.roomName')}
accessibilityLabel={translate('newRoomPage.roomName')}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT}
prefixCharacter={CONST.POLICY.ROOM_PREFIX}
placeholder={translate('newRoomPage.social')}
onChange={setModifiedRoomName}
value={value.substring(1)} // Since the room name always starts with a prefix, we omit the first character to avoid displaying it twice.
selection={selection}
onSelectionChange={(event) => setSelection(event.nativeEvent.selection)}
errorText={errorText}
valueParser={valueParser}
autoCapitalize="none"
onBlur={() => isFocused && onBlur()}
shouldDelayFocus={shouldDelayFocus}
autoFocus={isFocused && autoFocus}
maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH}
spellCheck={false}
shouldInterceptSwipe
keyboardType={keyboardType} // this is a bit hacky solution to a RN issue https://github.com/facebook/react-native/issues/27449
/>
);
}
Expand Down
66 changes: 66 additions & 0 deletions src/components/RoomNameInput/index.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from 'react';
import _ from 'underscore';
import CONST from '../../CONST';
import useLocalize from '../../hooks/useLocalize';
import TextInput from '../TextInput';
import * as roomNameInputPropTypes from './roomNameInputPropTypes';
import * as RoomNameInputUtils from '../../libs/RoomNameInputUtils';
import getOperatingSystem from '../../libs/getOperatingSystem';

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

/**
* Calls the onChangeText callback with a modified room name
* @param {Event} event
*/
const setModifiedRoomName = (event) => {
const roomName = event.nativeEvent.text;
const modifiedRoomName = RoomNameInputUtils.modifyRoomName(roomName);
onChangeText(modifiedRoomName);

// if custom component has onInputChange, use it to trigger changes (Form input)
if (_.isFunction(onInputChange)) {
onInputChange(modifiedRoomName);
}
};

const keyboardType = getOperatingSystem() === CONST.OS.IOS ? CONST.KEYBOARD_TYPE.ASCII_CAPABLE : CONST.KEYBOARD_TYPE.VISIBLE_PASSWORD;

return (
<TextInput
ref={forwardedRef}
disabled={disabled}
label={translate('newRoomPage.roomName')}
accessibilityLabel={translate('newRoomPage.roomName')}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT}
prefixCharacter={CONST.POLICY.ROOM_PREFIX}
placeholder={translate('newRoomPage.social')}
onChange={setModifiedRoomName}
value={value.substring(1)} // Since the room name always starts with a prefix, we omit the first character to avoid displaying it twice.
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={() => isFocused && onBlur()}
autoFocus={isFocused && autoFocus}
autoCapitalize="none"
shouldDelayFocus={shouldDelayFocus}
/>
);
}

RoomNameInput.propTypes = roomNameInputPropTypes.propTypes;
RoomNameInput.defaultProps = roomNameInputPropTypes.defaultProps;
RoomNameInput.displayName = 'RoomNameInput';

const RoomNameInputWithRef = React.forwardRef((props, ref) => (
<RoomNameInput
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

RoomNameInputWithRef.displayName = 'RoomNameInputWithRef';

export default RoomNameInputWithRef;
6 changes: 3 additions & 3 deletions src/components/RoomNameInput/roomNameInputPropTypes.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import PropTypes from 'prop-types';
import refPropTypes from '../refPropTypes';

const propTypes = {
/** Callback to execute when the text input is modified correctly */
Expand All @@ -15,10 +14,10 @@ const propTypes = {
errorText: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object]))]),

/** A ref forwarded to the TextInput */
forwardedRef: refPropTypes,
forwardedRef: PropTypes.func,

/** The ID used to uniquely identify the input in a Form */
inputID: PropTypes.string.isRequired,
inputID: PropTypes.string,

/** Callback that is called when the text input is blurred */
onBlur: PropTypes.func,
Expand All @@ -40,6 +39,7 @@ const defaultProps = {
errorText: '',
forwardedRef: () => {},

inputID: undefined,
onBlur: () => {},
autoFocus: false,
shouldDelayFocus: false,
Expand Down
8 changes: 4 additions & 4 deletions src/pages/settings/Report/RoomNamePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import CONST from '../../../CONST';
import ScreenWrapper from '../../../components/ScreenWrapper';
import HeaderWithBackButton from '../../../components/HeaderWithBackButton';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import Form from '../../../components/Form';
import ONYXKEYS from '../../../ONYXKEYS';
import styles from '../../../styles/styles';
import Navigation from '../../../libs/Navigation/Navigation';
Expand All @@ -20,7 +21,6 @@ import * as Report from '../../../libs/actions/Report';
import RoomNameInput from '../../../components/RoomNameInput';
import * as ReportUtils from '../../../libs/ReportUtils';
import FullPageNotFoundView from '../../../components/BlockingViews/FullPageNotFoundView';
import FormProvider from '../../../components/Form/FormProvider';

const propTypes = {
...withLocalizePropTypes,
Expand Down Expand Up @@ -90,7 +90,7 @@ function RoomNamePage(props) {
title={translate('newRoomPage.roomName')}
onBackButtonPress={() => Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report.reportID))}
/>
<FormProvider
<Form
style={[styles.flexGrow1, styles.ph5]}
formID={ONYXKEYS.FORMS.ROOM_NAME_FORM}
onSubmit={(values) => Report.updatePolicyRoomNameAndNavigate(report, values.roomName)}
Expand All @@ -100,13 +100,13 @@ function RoomNamePage(props) {
>
<View style={styles.mb4}>
<RoomNameInput
ref={roomNameInputRef}
ref={(ref) => (roomNameInputRef.current = ref)}
inputID="roomName"
defaultValue={report.reportName}
isFocused={isFocused}
/>
</View>
</FormProvider>
</Form>
</FullPageNotFoundView>
</ScreenWrapper>
);
Expand Down
21 changes: 8 additions & 13 deletions src/pages/workspace/WorkspaceNewRoomPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@ import * as ErrorUtils from '../../libs/ErrorUtils';
import * as ValidationUtils from '../../libs/ValidationUtils';
import * as ReportUtils from '../../libs/ReportUtils';
import * as PolicyUtils from '../../libs/PolicyUtils';
import Form from '../../components/Form';
import policyMemberPropType from '../policyMemberPropType';
import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView';
import compose from '../../libs/compose';
import variables from '../../styles/variables';
import useDelayedInputFocus from '../../hooks/useDelayedInputFocus';
import ValuePicker from '../../components/ValuePicker';
import FormProvider from '../../components/Form/FormProvider';
import InputWrapper from '../../components/Form/InputWrapper';

const propTypes = {
/** All reports shared with the user */
Expand Down Expand Up @@ -184,7 +183,7 @@ function WorkspaceNewRoomPage(props) {
// This is because when wrapping whole screen the screen was freezing when changing Tabs.
keyboardVerticalOffset={variables.contentHeaderHeight + variables.tabSelectorButtonHeight + variables.tabSelectorButtonPadding + insets.top}
>
<FormProvider
<Form
formID={ONYXKEYS.FORMS.NEW_ROOM_FORM}
submitButtonText={translate('newRoomPage.createRoom')}
style={[styles.mh5, styles.flexGrow1]}
Expand All @@ -194,16 +193,15 @@ function WorkspaceNewRoomPage(props) {
>
<View style={styles.mb5}>
<RoomNameInput
ref={roomNameInputRef}
ref={(el) => (roomNameInputRef.current = el)}
inputID="roomName"
isFocused={props.isFocused}
shouldDelayFocus
autoFocus
/>
</View>
<View style={styles.mb5}>
<InputWrapper
InputComponent={TextInput}
<TextInput
inputID="welcomeMessage"
label={translate('welcomeMessagePage.welcomeMessageOptional')}
accessibilityLabel={translate('welcomeMessagePage.welcomeMessageOptional')}
Expand All @@ -216,8 +214,7 @@ function WorkspaceNewRoomPage(props) {
/>
</View>
<View style={[styles.mhn5]}>
<InputWrapper
InputComponent={ValuePicker}
<ValuePicker
inputID="policyID"
label={translate('workspace.common.workspace')}
items={workspaceOptions}
Expand All @@ -226,8 +223,7 @@ function WorkspaceNewRoomPage(props) {
</View>
{isPolicyAdmin && (
<View style={styles.mhn5}>
<InputWrapper
InputComponent={ValuePicker}
<ValuePicker
inputID="writeCapability"
label={translate('writeCapabilityPage.label')}
items={writeCapabilityOptions}
Expand All @@ -237,8 +233,7 @@ function WorkspaceNewRoomPage(props) {
</View>
)}
<View style={[styles.mb1, styles.mhn5]}>
<InputWrapper
InputComponent={ValuePicker}
<ValuePicker
inputID="visibility"
label={translate('newRoomPage.visibility')}
items={visibilityOptions}
Expand All @@ -247,7 +242,7 @@ function WorkspaceNewRoomPage(props) {
/>
</View>
<Text style={[styles.textLabel, styles.colorMuted]}>{visibilityDescription}</Text>
</FormProvider>
</Form>
</KeyboardAvoidingView>
)}
</ScreenWrapper>
Expand Down

0 comments on commit 399b1bd

Please sign in to comment.