Skip to content

Commit

Permalink
Merge pull request #23354 from jczekalski/migrate-room-name-input
Browse files Browse the repository at this point in the history
Migrate RoomNameInput to function component
  • Loading branch information
srikarparsi authored Jul 27, 2023
2 parents 0d876a4 + 6b2e85a commit f8bb2b1
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 109 deletions.
104 changes: 43 additions & 61 deletions src/components/RoomNameInput/index.js
Original file line number Diff line number Diff line change
@@ -1,95 +1,77 @@
import React, {Component} from 'react';
import React, {useState} from 'react';
import _ from 'underscore';
import CONST from '../../CONST';
import withLocalize from '../withLocalize';
import TextInput from '../TextInput';
import useLocalize from '../../hooks/useLocalize';
import * as roomNameInputPropTypes from './roomNameInputPropTypes';
import * as RoomNameInputUtils from '../../libs/RoomNameInputUtils';

class RoomNameInput extends Component {
constructor(props) {
super(props);
function RoomNameInput({autoFocus, disabled, errorText, forwardedRef, value, onBlur, onChangeText, onInputChange}) {
const {translate} = useLocalize();

this.setModifiedRoomName = this.setModifiedRoomName.bind(this);
this.setSelection = this.setSelection.bind(this);

this.state = {
selection: undefined,
};
}
const [selection, setSelection] = useState();

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

// if custom component has onInputChange, use it to trigger changes (Form input)
if (_.isFunction(this.props.onInputChange)) {
this.props.onInputChange(modifiedRoomName);
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 = this.props.value || '';
const oldRoomNameWithHash = value || '';
const newRoomNameWithHash = `${CONST.POLICY.ROOM_PREFIX}${roomName}`;
if (modifiedRoomName !== newRoomNameWithHash) {
const offset = modifiedRoomName.length - oldRoomNameWithHash.length;
const selection = {
start: this.state.selection.start + offset,
end: this.state.selection.end + offset,
const newSelection = {
start: selection.start + offset,
end: selection.end + offset,
};
this.setSelection(selection);
setSelection(newSelection);
}
}

/**
* Set the selection
* @param {Object} selection
*/
setSelection(selection) {
this.setState({selection});
}
};

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

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

export default withLocalize(
React.forwardRef((props, ref) => (
<RoomNameInput
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
)),
);
export default React.forwardRef((props, ref) => (
<RoomNameInput
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));
84 changes: 39 additions & 45 deletions src/components/RoomNameInput/index.native.js
Original file line number Diff line number Diff line change
@@ -1,68 +1,62 @@
import React, {Component} from 'react';
import React from 'react';
import _ from 'underscore';
import CONST from '../../CONST';
import withLocalize from '../withLocalize';
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';

class RoomNameInput extends Component {
constructor(props) {
super(props);

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

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

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

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

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

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

export default withLocalize(
React.forwardRef((props, ref) => (
<RoomNameInput
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
)),
);
export default React.forwardRef((props, ref) => (
<RoomNameInput
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));
3 changes: 0 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 {withLocalizePropTypes} from '../withLocalize';

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

...withLocalizePropTypes,

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

Expand Down

0 comments on commit f8bb2b1

Please sign in to comment.