207 lines
6.5 KiB
TypeScript
207 lines
6.5 KiB
TypeScript
import React from 'react';
|
|
import { View, TextInput, TouchableOpacity, Text, StyleSheet, ActivityIndicator } from 'react-native';
|
|
import { WalletType } from 'rnwalletman';
|
|
import { useOTPBind } from '../hooks/useOTPBind';
|
|
|
|
interface OTPBindUIProps {
|
|
walletType: WalletType;
|
|
title: string;
|
|
otpLength?: number;
|
|
mobileLength?: number;
|
|
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
|
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
|
onSuccess: (result: any) => void;
|
|
onError: (error: string) => void;
|
|
isDebug: boolean;
|
|
additionalParams?: any;
|
|
}
|
|
|
|
export const OTPBindUI: React.FC<OTPBindUIProps> = ({
|
|
walletType,
|
|
title,
|
|
otpLength = 6,
|
|
mobileLength = 10,
|
|
onRequestOTP,
|
|
onVerifyOTP,
|
|
onSuccess,
|
|
onError,
|
|
isDebug,
|
|
additionalParams = {},
|
|
}) => {
|
|
const [state, actions] = useOTPBind(
|
|
walletType,
|
|
{
|
|
onRequestOTP,
|
|
onVerifyOTP,
|
|
onSuccess,
|
|
onError,
|
|
isDebug,
|
|
},
|
|
{
|
|
otpLength,
|
|
mobileLength,
|
|
additionalParams,
|
|
}
|
|
);
|
|
|
|
if (state.step === 'processing') {
|
|
return (
|
|
<View style={styles.container}>
|
|
<ActivityIndicator size="large" color="#fff" />
|
|
<Text style={styles.processingText}>处理中...</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={styles.form}>
|
|
<Text style={styles.title}>{title}</Text>
|
|
|
|
{state.step === 'mobile' && (
|
|
<>
|
|
{state.errorMessage ? (
|
|
<Text style={styles.errorText}>{state.errorMessage}</Text>
|
|
) : null}
|
|
<TextInput
|
|
style={[styles.input, state.errorMessage ? styles.inputError : {}]}
|
|
placeholder="请输入手机号"
|
|
placeholderTextColor="#999"
|
|
keyboardType="phone-pad"
|
|
maxLength={mobileLength}
|
|
value={state.mobile}
|
|
onChangeText={(text) => {
|
|
actions.setMobile(text);
|
|
if (state.errorMessage) actions.clearError();
|
|
}}
|
|
editable={!state.loading}
|
|
/>
|
|
<TouchableOpacity
|
|
style={[styles.button, state.loading && styles.buttonDisabled]}
|
|
onPress={actions.requestOTP}
|
|
disabled={state.loading}
|
|
>
|
|
{state.loading ? (
|
|
<ActivityIndicator color="#fff" />
|
|
) : (
|
|
<Text style={styles.buttonText}>获取验证码</Text>
|
|
)}
|
|
</TouchableOpacity>
|
|
</>
|
|
)}
|
|
|
|
{state.step === 'otp' && (
|
|
<>
|
|
<Text style={styles.hint}>验证码已发送至 {state.mobile}</Text>
|
|
{state.errorMessage ? (
|
|
<Text style={styles.errorText}>{state.errorMessage}</Text>
|
|
) : null}
|
|
<TextInput
|
|
style={[styles.input, state.errorMessage ? styles.inputError : {}]}
|
|
placeholder={`请输入 ${otpLength} 位验证码`}
|
|
placeholderTextColor="#999"
|
|
keyboardType="number-pad"
|
|
maxLength={otpLength}
|
|
value={state.otp}
|
|
onChangeText={(text) => {
|
|
actions.setOtp(text);
|
|
if (state.errorMessage) actions.clearError();
|
|
}}
|
|
editable={!state.loading}
|
|
/>
|
|
<TouchableOpacity
|
|
style={[styles.button, state.loading && styles.buttonDisabled]}
|
|
onPress={actions.verifyOTP}
|
|
disabled={state.loading}
|
|
>
|
|
{state.loading ? (
|
|
<ActivityIndicator color="#fff" />
|
|
) : (
|
|
<Text style={styles.buttonText}>验证并绑定</Text>
|
|
)}
|
|
</TouchableOpacity>
|
|
<TouchableOpacity
|
|
style={styles.linkButton}
|
|
onPress={actions.resetToMobile}
|
|
disabled={state.loading}
|
|
>
|
|
<Text style={styles.linkText}>重新输入手机号</Text>
|
|
</TouchableOpacity>
|
|
</>
|
|
)}
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: 'rgba(0,0,0,0.8)',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
form: {
|
|
width: '80%',
|
|
backgroundColor: '#fff',
|
|
borderRadius: 10,
|
|
padding: 20,
|
|
},
|
|
title: {
|
|
fontSize: 20,
|
|
fontWeight: 'bold',
|
|
textAlign: 'center',
|
|
marginBottom: 20,
|
|
},
|
|
input: {
|
|
borderWidth: 1,
|
|
borderColor: '#ddd',
|
|
borderRadius: 5,
|
|
padding: 12,
|
|
fontSize: 16,
|
|
marginBottom: 15,
|
|
},
|
|
inputError: {
|
|
borderColor: '#ff3b30',
|
|
},
|
|
errorText: {
|
|
color: '#ff3b30',
|
|
fontSize: 14,
|
|
marginBottom: 10,
|
|
textAlign: 'center',
|
|
},
|
|
button: {
|
|
backgroundColor: '#007AFF',
|
|
borderRadius: 5,
|
|
padding: 15,
|
|
alignItems: 'center',
|
|
},
|
|
buttonDisabled: {
|
|
backgroundColor: '#ccc',
|
|
},
|
|
buttonText: {
|
|
color: '#fff',
|
|
fontSize: 16,
|
|
fontWeight: 'bold',
|
|
},
|
|
linkButton: {
|
|
marginTop: 10,
|
|
alignItems: 'center',
|
|
},
|
|
linkText: {
|
|
color: '#007AFF',
|
|
fontSize: 14,
|
|
},
|
|
hint: {
|
|
fontSize: 14,
|
|
color: '#666',
|
|
marginBottom: 10,
|
|
textAlign: 'center',
|
|
},
|
|
processingText: {
|
|
color: '#fff',
|
|
fontSize: 16,
|
|
marginTop: 10,
|
|
},
|
|
});
|