textContentType = oneTimeCode не работает для iOS

#react-native #react-native-ios

#react-native #react-native-ios

Вопрос:

Ну, раньше это работало, но после того, как я изменил некоторые стили и добавил другие компоненты, оно перестало работать.

Это нерабочая версия OTP:

 import React, { useContext, useEffect, useState } from 'react'
import { StyleSheet, View } from 'react-native'
import { ScrollView } from 'react-native-gesture-handler'
import {SafeAreaView} from 'react-native-safe-area-context'
import styled from 'styled-components/native'
import { CodeField, Cursor, useBlurOnFulfill, useClearByFocusCell } from 'react-native-confirmation-code-field'
import SmsRetriever from 'react-native-sms-retriever'
import {Button} from '../../components/button/button'
import {Text} from '../../components/text'
import { Breakpoint } from '../../components'
import {useHttp} from '../../hooks/use-http/use-http'
import {authService} from '../../api/auth.service'
import {Auth} from '../../models/auth.model'
import { OTPScreenProps } from './otp.props'
import { extractOTP } from '../../utils/extract-otp'
import { message } from '../../utils/message-handler'
import { AuthContext } from '../../contexts/auth/auth.context'
import chevronLeftIcon from '../../assets/icons/chevron-left-icon.png'
import { TouchableOpacity } from 'react-native-gesture-handler'

const CELL_COUNT = 6

const Container = styled(SafeAreaView)`
  flex: 1;
  background-color: ${({theme}) => theme.colors.background.primary};
`

const Content = styled.View`
  align-items: center;
  z-index: 1;
  padding-horizontal: ${({ theme }) => theme.spacing[7]}px;
`

const Keyboad = styled.KeyboardAvoidingView.attrs({
  behavior: 'position'
})`
  padding-bottom: 85px;
`

const BackButtonContainer = styled.View`
  align-self: flex-start;
  padding-horizontal: ${({ theme }) => theme.spacing[7]}px
  padding-top: ${({ theme }) => theme.spacing[7]}px
  z-index: 2;
`

const BackButton = styled.Image.attrs({
  source: chevronLeftIcon
})`
  width: 12px;
  height: 21px;
  tint-color: white;
`

export const OTPScreen = ({ route, navigation }: OTPScreenProps) => {
  const phone = route.params amp;amp; route.params.phone || '6203221059'
  const [firstOTPSent, setFirstOTPSent] = useState<boolean>(true)
  const { setAuth } = useContext(AuthContext)
  const [code, setCode] = useState('')
  const ref = useBlurOnFulfill({ value: code, cellCount: CELL_COUNT })
  const [inputProps, getCellOnLayoutHandler] = useClearByFocusCell({
    value: code,
    setValue: setCode,
  })
  const [sendOTP, { loading: sendingOTP }] = useHttp<{ message: string }>(
    () => authService.sendOTP(phone),
    async (_, err) => {
      if (err) message.danger('otp.sentError')
      else if(!firstOTPSent) message.info('otp.sent')
    }
  )
  const [confirmOTP, { loading }] = useHttp<Auth>(
    (otp: string) => authService.confirmOTP(phone, otp),
    async (auth, err) => {
      if (err) message.danger('otp.loginError')
      else setAuth(auth)
    }
  )

  useEffect(() => {
    if(phone) {
      sendOTP()
      setFirstOTPSent(false)
    }
  }, [])

  useEffect(() => {
    if(sendingOTP) listenToSMS()
  }, [sendingOTP])


  const listenToSMS = async () => {
    try {
      const registered = await SmsRetriever.startSmsRetriever()
      if (!registered) return
      SmsRetriever.addSmsListener(event => {
        if(event amp;amp; event.message) {
          const otp = extractOTP(event.message)
          if(otp) {
            setCode(otp)
            confirmOTP(otp)
          }
        }
        SmsRetriever.removeSmsListener()
      })
    } catch (error) {}
  }

  return (
    <Container>
      <BackButtonContainer>
        <TouchableOpacity onPress={() => navigation.goBack()}>
          <BackButton />
        </TouchableOpacity>
      </BackButtonContainer>

      <ScrollView keyboardShouldPersistTaps="handled">
        <Keyboad>
          <Content>
            <Breakpoint spacing={12} />
            <Text tx="otp.confirmIdentity" color="tertiary" preset="h5" bold/>
            <Breakpoint spacing={14} />
            <Text tx="otp.enterCode" color="tertiary" preset="h5" opacity={0.7} align="center">
              <Text color="tertiary" preset="h5" bold> {phone.slice(-4)}</Text>
            </Text>
            <Breakpoint spacing={2} />
            <Button
              preset="link"
              tx="otp.again"
              textProps={{ 
                color: '#71A1F4',
                preset: 'h4',
                bold: true,
                opacity: 0.7
              }}
              onPress={sendOTP}
              loading={sendingOTP}
            />
            <Breakpoint spacing={16} />
            <CodeField
              ref={ref}
              {...inputProps}
              value={code}
              rootStyle={styles.root}
              onChangeText={setCode}
              cellCount={CELL_COUNT}
              keyboardType="number-pad"
              textContentType="oneTimeCode"
              onEndEditing={() => confirmOTP(code)}
              renderCell={({index, symbol, isFocused}) => (
                <View
                  onLayout={getCellOnLayoutHandler(index)}
                  key={index}
                  style={[styles.cellRoot, isFocused amp;amp; styles.focusCell]}>
                  <Text style={styles.cellText}>
                    {symbol || (isFocused ? <Cursor /> : null)}
                  </Text>
                </View>
              )}
            />
            <Button
              style={{ width: '100%' }}
              onPress={() => confirmOTP(code)}
              tx="otp.login"
              disabled={code.length < 6}
              textProps={{ uppercase: true }}
              loading={loading}
            />
          </Content>
        </Keyboad>
      </ScrollView>
    </Container>
  )
}


const styles = StyleSheet.create({
  root: {
    marginBottom: 36,
    width: '75%',
    height: 55
  },
  cellRoot: {
    flex: 1,
    marginHorizontal: 5,
    justifyContent: 'center',
    alignItems: 'center',
    borderBottomColor: '#71A1F4',
    borderBottomWidth: 2
  },
  cellText: {
    color: 'white',
    fontSize: 38,
    textAlign: 'center',
  },
  focusCell: {
    borderBottomColor: '#007AFF'
  },
});
  

И это рабочая версия:

 import React, { useContext, useEffect, useState } from 'react'
import { StyleSheet, Dimensions, View } from 'react-native'
import {SafeAreaView} from 'react-native-safe-area-context'
import styled from 'styled-components/native'
import { CodeField, Cursor, useBlurOnFulfill, useClearByFocusCell } from 'react-native-confirmation-code-field'
import SmsRetriever from 'react-native-sms-retriever'
import {Button} from '../../components/button/button'
import {Text} from '../../components/text'
import { Breakpoint } from '../../components'
import {useHttp} from '../../hooks/use-http/use-http'
import {authService} from '../../api/auth.service'
import {Auth} from '../../models/auth.model'
import { OTPScreenProps } from './otp.props'
import { extractOTP } from '../../utils/extract-otp'
import { message } from '../../utils/message-handler'
import { AuthContext } from '../../contexts/auth/auth.context'
import chevronLeftIcon from '../../assets/icons/chevron-left-icon.png'
import { TouchableOpacity } from 'react-native-gesture-handler'

const CELL_COUNT = 6

const Container = styled(SafeAreaView)`
  background-color: ${({theme}) => theme.colors.background.primary};
  height: 100%;
`

const Keyboad = styled.KeyboardAvoidingView.attrs({
  behavior: 'padding'
})`
  align-items: center;
  height: ${Dimensions.get('window').height}px;
  padding: 25px;
`

const BackButtonContainer = styled.View`
  align-self: flex-start;
`

const BackButton = styled.Image.attrs({
  source: chevronLeftIcon
})`
  width: 12px;
  height: 21px;
  tint-color: white;
`

export const OTPScreen = ({ route, navigation }: OTPScreenProps) => {
  const phone = route.params amp;amp; route.params.phone || '6203221059'
  const [firstOTPSent, setFirstOTPSent] = useState<boolean>(true)
  const { setAuth } = useContext(AuthContext)
  const [code, setCode] = useState('')
  const ref = useBlurOnFulfill({ value: code, cellCount: CELL_COUNT })
  const [inputProps, getCellOnLayoutHandler] = useClearByFocusCell({
    value: code,
    setValue: setCode,
  })
  const [sendOTP, { loading: sendingOTP }] = useHttp<{ message: string }>(
    () => authService.sendOTP(phone),
    async (_, err) => {
      if (err) message.danger('otp.sentError')
      else if(!firstOTPSent) message.info('otp.sent')
    }
  )
  const [confirmOTP, { loading }] = useHttp<Auth>(
    (otp: string) => authService.confirmOTP(phone, otp),
    async (auth, err) => {
      if (err) message.danger('otp.loginError')
      else setAuth(auth)
    }
  )

  useEffect(() => {
    if(phone) {
      sendOTP()
      setFirstOTPSent(false)
    }
  }, [])

  useEffect(() => {
    if(sendingOTP) listenToSMS()
  }, [sendingOTP])


  const listenToSMS = async () => {
    try {
      const registered = await SmsRetriever.startSmsRetriever()
      if (!registered) return
      SmsRetriever.addSmsListener(event => {
        if(event amp;amp; event.message) {
          const otp = extractOTP(event.message)
          if(otp) {
            setCode(otp)
            confirmOTP(otp)
          }
        }
        SmsRetriever.removeSmsListener()
      })
    } catch (error) {}
  }

  return (
    <Container>
      <Keyboad>
        <BackButtonContainer>
          <TouchableOpacity onPress={() => navigation.goBack()}>
            <BackButton />
          </TouchableOpacity>
        </BackButtonContainer>
        <Breakpoint spacing={12} />
        <Text tx="otp.confirmIdentity" color="tertiary" preset="h5" bold/>
        <Breakpoint spacing={14} />
        <Text tx="otp.enterCode" color="tertiary" preset="h5" opacity={0.7} align="center">
          <Text color="tertiary" preset="h5" bold> {phone.slice(-4)}</Text>
        </Text>
        <Breakpoint spacing={2} />
        <Button
          preset="link"
          tx="otp.again"
          textProps={{ 
            color: '#71A1F4',
            preset: 'h4',
            bold: true,
            opacity: 0.7
          }}
          onPress={sendOTP}
          loading={sendingOTP}
        />
        <Breakpoint spacing={16} />
        <CodeField
          ref={ref}
          {...inputProps}
          value={code}
          rootStyle={styles.root}
          onChangeText={setCode}
          cellCount={CELL_COUNT}
          keyboardType="number-pad"
          textContentType="oneTimeCode"
          onEndEditing={() => confirmOTP(code)}
          renderCell={({index, symbol, isFocused}) => (
            <View
              onLayout={getCellOnLayoutHandler(index)}
              key={index}
              style={[styles.cellRoot, isFocused amp;amp; styles.focusCell]}>
              <Text style={styles.cellText}>
                {symbol || (isFocused ? <Cursor /> : null)}
              </Text>
            </View>
          )}
        />
        <Button
          style={{ width: '100%' }}
          onPress={() => confirmOTP(code)}
          tx="otp.login"
          disabled={code.length < 6}
          textProps={{ uppercase: true }}
          loading={loading}
        />
      </Keyboad>
    </Container>
  )
}


const styles = StyleSheet.create({
  root: {
    marginBottom: 36,
    width: '75%',
    height: 55
  },
  cellRoot: {
    flex: 1,
    marginHorizontal: 5,
    justifyContent: 'center',
    alignItems: 'center',
    borderBottomColor: '#71A1F4',
    borderBottomWidth: 2
  },
  cellText: {
    color: 'white',
    fontSize: 38,
    textAlign: 'center',
  },
  focusCell: {
    borderBottomColor: '#007AFF'
  },
});
  

Кроме того, я ничего не изменил, ничего не обновил. Единственное большое различие, которое я вижу между этими двумя версиями, заключается в том, что я добавил ScrollView, кроме того, я просто изменил некоторые стили и изменил позицию компонента KeyboardAvoidingView.

Ответ №1:

проверьте отправляемое вами SMS: в моем случае на французском,

  • Veuillez saisir le code ${code} dans l'application работает
  • Veuillez entrer ce code dans l'application : ${code} не работает

Я проверял RN в течение нескольких часов, и, наконец, изменение SMS было решением.

Комментарии:

1. это сработало и для меня! Большое спасибо. Похоже, что код должен быть в конце SMS и без каких-либо символов, как : перед ним. Просто взглянул, как выглядит мое банковское приложение SMS.

2. ммм, не уверен, сообщение Apple похоже Your Apple ID code is: 123456. Do not share it with anyone. @apple.com #123456 %apple.com