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

Unable to use textContentType="oneTimeCode" #52

Open
aravi365 opened this issue Mar 21, 2019 · 6 comments
Open

Unable to use textContentType="oneTimeCode" #52

aravi365 opened this issue Mar 21, 2019 · 6 comments

Comments

@aravi365
Copy link

I need OTPs to be automatically filled from the keyboard using the prop : textContentType="oneTimeCode" , but when i tried only one field gets filled by the prop. How to fix this??

@chevulkar
Copy link

Same even i am looking of the same solution.

@shubhamverma27
Copy link

+1 Need this functionality

export-mike added a commit to export-mike/react-native-confirmation-code-input that referenced this issue Apr 29, 2020
oneTimeCode acts like a paste action in react native.


fixes issue:
ttdung11t2#52
@Yasoobh1
Copy link

Android is working fine but IOS not working properly its just one field gets filled

@codeapp17
Copy link

Is is still open? are there any solutions for this.. I am getting the same issue

@AzranAzwer
Copy link

any update on this ?

@Garamani
Copy link

This is the only way I could make it work after many hours of testing:

You can replace the code below with ConfirmationCodeInput.js:

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { View, TextInput, StyleSheet, Keyboard, ViewPropTypes } from 'react-native';
import _ from 'lodash';

// if ViewPropTypes is not defined fall back to View.propType (to support RN < 0.44)
const viewPropTypes = ViewPropTypes || View.propTypes;

export default class ConfirmationCodeInput extends Component {
  static propTypes = {
    codeLength: PropTypes.number,
    compareWithCode: PropTypes.string,
    inputPosition: PropTypes.string,
    size: PropTypes.number,
    space: PropTypes.number,
    className: PropTypes.string,
    cellBorderWidth: PropTypes.number,
    activeColor: PropTypes.string,
    inactiveColor: PropTypes.string,
    ignoreCase: PropTypes.bool,
    autoFocus: PropTypes.bool,
    codeInputStyle: TextInput.propTypes.style,
    containerStyle: viewPropTypes.style,
    onFulfill: PropTypes.func,
    onCodeChange: PropTypes.func,
  };
  
  static defaultProps = {
    codeLength: 5,
    inputPosition: 'center',
    autoFocus: true,
    size: 40,
    className: 'border-box',
    cellBorderWidth: 1,
    activeColor: 'rgba(255, 255, 255, 1)',
    inactiveColor: 'rgba(255, 255, 255, 0.2)',
    space: 8,
    compareWithCode: '',
    ignoreCase: false,
  };
  
  constructor(props) {
    super(props);
    
    this.state = {
      codeArr: new Array(this.props.codeLength).fill(''),
      currentIndex: 0
    };
    
    this.codeInputRefs = [];
  }
  
  componentDidMount() {
    const { compareWithCode, codeLength, inputPosition } = this.props;
    if (compareWithCode && compareWithCode.length !== codeLength) {
      console.error("Invalid props: compareWith length is not equal to codeLength");
    }
    
    if (_.indexOf(['center', 'left', 'right', 'full-width'], inputPosition) === -1) {
      console.error('Invalid input position. Must be in: center, left, right, full');
    }
  }
  
  clear() {
    this.setState({
      codeArr: new Array(this.props.codeLength).fill(''),
      currentIndex: 0
    });
    this._setFocus(0);
  }
  
  _setFocus(index) {
    this.codeInputRefs[index].focus();
  }
  
  _blur(index) {
    this.codeInputRefs[index].blur();
  }
  
  _onFocus(index) {
    const newCodeArr = _.clone(this.state.codeArr);
    const currentEmptyIndex = _.findIndex(newCodeArr, c => !c);
    if (currentEmptyIndex !== -1 && currentEmptyIndex < index) {
      return this._setFocus(currentEmptyIndex);
    }
    for (const i in newCodeArr) {
      if (i >= index) {
        newCodeArr[i] = '';
      }
    }
    
    this.setState({
      codeArr: newCodeArr,
      currentIndex: index
    })
  }
  
  _isMatchingCode(code, compareWithCode, ignoreCase = false) {
    if (ignoreCase) {
      return code.toLowerCase() === compareWithCode.toLowerCase();
    }
    return code === compareWithCode;
  }
  
  _getContainerStyle(size, position) {
    switch (position) {
      case 'left':
        return {
          justifyContent: 'flex-start',
          height: size
        };
      case 'center':
        return {
          justifyContent: 'center',
          height: size
        };
      case 'right':
        return {
          justifyContent: 'flex-end',
          height: size
        };
      default:
        return {
          justifyContent: 'space-between',
          height: size
        }
    }
  }
  
  _getInputSpaceStyle(space) {
    const { inputPosition } = this.props;
    switch (inputPosition) {
      case 'left':
        return {
          marginRight: space
        };
      case 'center':
        return {
          marginRight: space/2,
          marginLeft: space/2
        };
      case 'right':
        return {
          marginLeft: space
        };
      default:
        return {
          marginRight: 0,
          marginLeft: 0
        };
    }
  }
  
  _getClassStyle(className, active) {
    const { cellBorderWidth, activeColor, inactiveColor, space } = this.props;
    const classStyle = {
      ...this._getInputSpaceStyle(space),
      color: activeColor
    };
    
    switch (className) {
      case 'clear':
        return _.merge(classStyle, { borderWidth: 0 });
      case 'border-box':
        return _.merge(classStyle, {
          borderWidth: cellBorderWidth,
          borderColor: (active ? activeColor : inactiveColor)
        });
      case 'border-circle':
        return _.merge(classStyle, {
          borderWidth: cellBorderWidth,
          borderRadius: 50,
          borderColor: (active ? activeColor : inactiveColor)
        });
      case 'border-b':
        return _.merge(classStyle, {
          borderBottomWidth: cellBorderWidth,
          borderColor: (active ? activeColor : inactiveColor),
        });
      case 'border-b-t':
        return _.merge(classStyle, {
          borderTopWidth: cellBorderWidth,
          borderBottomWidth: cellBorderWidth,
          borderColor: (active ? activeColor : inactiveColor)
        });
      case 'border-l-r':
        return _.merge(classStyle, {
          borderLeftWidth: cellBorderWidth,
          borderRightWidth: cellBorderWidth,
          borderColor: (active ? activeColor : inactiveColor)
        });
      default:
        return className;
    }
  }
  
  _onKeyPress(e) {
    if (e.nativeEvent.key === 'Backspace') {
      const { currentIndex } = this.state;
      const newCodeArr = _.clone(this.state.codeArr);
      const nextIndex = currentIndex > 0 ? currentIndex - 1 : 0;
      for (const i in newCodeArr) {
        if (i >= nextIndex) {
          newCodeArr[i] = '';
        }
      }
      this.props.onCodeChange(newCodeArr.join(''))
      this._setFocus(nextIndex);
    }
  }
  
  _onInputCode = async (character, index) => {
    const { codeLength, onFulfill, compareWithCode, ignoreCase } = this.props;
    if (index === 0 && character.length === codeLength) {
      setTimeout(() => {

        this.setState(
          {
            codeArr: character.split(''),
          },
          () => {
            const code = character;
        
            if (compareWithCode) {
              const isMatching = this._isMatchingCode(code, compareWithCode, ignoreCase);
              onFulfill(isMatching, code);
              !isMatching && this.clear();
            } else {
              onFulfill(code);
            }
            
            Keyboard.dismiss();
          }
        )
      }, 100);
    } else if (character.length === 1){

      const { codeLength, onFulfill, compareWithCode, ignoreCase, onCodeChange } = this.props;
      let newCodeArr = _.clone(this.state.codeArr);
      newCodeArr[index] = character;
      
      if (index == codeLength - 1) {
        const code = newCodeArr.join('');
        
        if (compareWithCode) {
          const isMatching = this._isMatchingCode(code, compareWithCode, ignoreCase);
          onFulfill(isMatching, code);
          !isMatching && this.clear();
        } else {
          onFulfill(code);
        }
        this._blur(this.state.currentIndex);
      } else {
        this._setFocus(this.state.currentIndex + 1);
      }
      
      this.setState(prevState => {
        return {
          codeArr: newCodeArr,
          currentIndex: prevState.currentIndex + 1
        };
      }, () => { onCodeChange(newCodeArr.join('')) });
    }
  }
  render() {
    const {
      codeLength,
      codeInputStyle,
      containerStyle,
      inputPosition,
      autoFocus,
      className,
      size,
      textContentType,
      activeColor
    } = this.props;
    
    const initialCodeInputStyle = {
      width: size,
      height: size
    };
    
    const codeInputs = [];
    for (let i = 0; i < codeLength; i++) {
      const id = i;
      codeInputs.push(
        <TextInput
          key={id}
          ref={ref => (this.codeInputRefs[id] = ref)}
          style={[
            styles.codeInput, 
            initialCodeInputStyle, 
            this._getClassStyle(className, this.state.currentIndex === id),
            codeInputStyle
          ]}
          underlineColorAndroid="transparent"
          selectionColor={activeColor}
          keyboardType={'name-phone-pad'}
          returnKeyType={'done'}
          textContentType={textContentType}
          {...this.props}
          autoFocus={autoFocus && id === 0}
          onFocus={() => this._onFocus(id)}
          value={this.state.codeArr[id] ? this.state.codeArr[id].toString() : ''}
          onChangeText={text => this._onInputCode(text, id)}
          onKeyPress={(e) => this._onKeyPress(e)}
          maxLength={(i === 0 && textContentType === 'oneTimeCode' ) ? codeLength : 1}
        />
      )
    }
    
    return (
      <View style={[styles.container, this._getContainerStyle(size, inputPosition), containerStyle]}>
        {codeInputs}
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    marginTop: 20
  },
  codeInput: {
    backgroundColor: 'transparent',
    textAlign: 'center',
    padding: 0
  }
});

The parts that I have added:
1.Keyboard -->

import { View, TextInput, StyleSheet, Keyboard, ViewPropTypes } from 'react-native';

  1. lines 213 - 236 -->
const { codeLength, onFulfill, compareWithCode, ignoreCase } = this.props;
    if (index === 0 && character.length === codeLength) {
      setTimeout(() => {

        this.setState(
          {
            codeArr: character.split(''),
          },
          () => {
            const code = character;
        
            if (compareWithCode) {
              const isMatching = this._isMatchingCode(code, compareWithCode, ignoreCase);
              onFulfill(isMatching, code);
              !isMatching && this.clear();
            } else {
              onFulfill(code);
            }
            
            Keyboard.dismiss();
          }
        )
      }, 100);
    } else if (character.length === 1){
  1. the 3 lines with this variable textContentType

Note: I haven't tested the isMatching part of the code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants