合并 verify 和 otp 界面,增加 password 界面
This commit is contained in:
@@ -15,6 +15,11 @@ interface OTPBindUIProps {
|
||||
title: string;
|
||||
otpLength?: number;
|
||||
mobileLength?: number;
|
||||
passwordBeforeOtp?: boolean;
|
||||
passwordLabel?: string;
|
||||
passwordPlaceholder?: string;
|
||||
passwordRequiredMsg?: string;
|
||||
resendCooldown?: number;
|
||||
onRequestOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||
onVerifyOTP: (walletType: WalletType, params: any) => Promise<any>;
|
||||
onSuccess: (result: any) => void;
|
||||
@@ -29,6 +34,11 @@ export const OTPBindUI: React.FC<OTPBindUIProps> = ({
|
||||
title,
|
||||
otpLength = 6,
|
||||
mobileLength = 10,
|
||||
passwordBeforeOtp = false,
|
||||
passwordLabel = 'Password',
|
||||
passwordPlaceholder = 'Enter password',
|
||||
passwordRequiredMsg = 'Please enter your password',
|
||||
resendCooldown = 20,
|
||||
onRequestOTP,
|
||||
onVerifyOTP,
|
||||
onSuccess,
|
||||
@@ -40,10 +50,132 @@ export const OTPBindUI: React.FC<OTPBindUIProps> = ({
|
||||
const [state, actions] = useOTPBind(
|
||||
walletType,
|
||||
{ onRequestOTP, onVerifyOTP, onSuccess, onError, isDebug },
|
||||
{ otpLength, mobileLength, additionalParams, initialMobile }
|
||||
{ otpLength, mobileLength, additionalParams, initialMobile, passwordBeforeOtp, passwordRequiredMsg, resendCooldown }
|
||||
);
|
||||
|
||||
const isLoading = state.loading || state.step === 'processing';
|
||||
|
||||
const renderResendOtp = () => {
|
||||
if (!state.otpSent) return null;
|
||||
const disabled = isLoading || state.resendCountdown > 0;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={styles.linkBtn}
|
||||
onPress={actions.requestOTP}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Text style={[styles.linkText, disabled && styles.linkTextDisabled]}>
|
||||
{state.resendCountdown > 0
|
||||
? `Resend in ${state.resendCountdown}s`
|
||||
: 'Resend OTP'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
if (passwordBeforeOtp) {
|
||||
return (
|
||||
<View style={styles.overlay}>
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
{!!state.errorMessage && (
|
||||
<Text style={styles.errorText}>{state.errorMessage}</Text>
|
||||
)}
|
||||
<Text style={styles.label}>Mobile</Text>
|
||||
<TextInput
|
||||
style={[styles.input, !!state.errorMessage && styles.inputError, state.formStarted && styles.inputLocked]}
|
||||
placeholder={`Enter ${mobileLength}-digit mobile number`}
|
||||
placeholderTextColor="#aaa"
|
||||
keyboardType="phone-pad"
|
||||
maxLength={mobileLength}
|
||||
value={state.mobile}
|
||||
onChangeText={t => {
|
||||
actions.setMobile(t);
|
||||
if (state.errorMessage) actions.clearError();
|
||||
}}
|
||||
editable={!state.formStarted && !isLoading}
|
||||
/>
|
||||
{state.formStarted && state.passwordRequired && (
|
||||
<>
|
||||
<Text style={styles.label}>{passwordLabel}</Text>
|
||||
<TextInput
|
||||
style={[styles.input, !!state.errorMessage && styles.inputError]}
|
||||
placeholder={passwordPlaceholder}
|
||||
placeholderTextColor="#aaa"
|
||||
secureTextEntry
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
value={state.password}
|
||||
onChangeText={t => {
|
||||
actions.setPassword(t);
|
||||
if (state.errorMessage) actions.clearError();
|
||||
}}
|
||||
editable={!isLoading}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{!state.otpSent && (
|
||||
<TouchableOpacity
|
||||
style={[styles.btn, isLoading && styles.btnDisabled]}
|
||||
onPress={actions.requestOTP}
|
||||
disabled={isLoading}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
{isLoading ? (
|
||||
<ActivityIndicator color="#fff" />
|
||||
) : (
|
||||
<Text style={styles.btnText}>Send OTP</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{state.otpSent && (
|
||||
<>
|
||||
<Text style={styles.hint}>OTP sent to {state.mobile}</Text>
|
||||
<Text style={styles.label}>Verification code</Text>
|
||||
<TextInput
|
||||
style={[styles.input, !!state.errorMessage && styles.inputError]}
|
||||
placeholder={`Enter ${otpLength}-digit code`}
|
||||
placeholderTextColor="#aaa"
|
||||
keyboardType="number-pad"
|
||||
maxLength={otpLength}
|
||||
value={state.otp}
|
||||
onChangeText={t => {
|
||||
actions.setOtp(t);
|
||||
if (state.errorMessage) actions.clearError();
|
||||
}}
|
||||
editable={!isLoading}
|
||||
/>
|
||||
<TouchableOpacity
|
||||
style={[styles.btn, isLoading && styles.btnDisabled]}
|
||||
onPress={actions.verifyOTP}
|
||||
disabled={isLoading}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
{isLoading ? (
|
||||
<ActivityIndicator color="#fff" />
|
||||
) : (
|
||||
<Text style={styles.btnText}>Verify & Bind</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
{renderResendOtp()}
|
||||
</>
|
||||
)}
|
||||
{state.formStarted && (
|
||||
<TouchableOpacity
|
||||
style={styles.linkBtn}
|
||||
onPress={actions.resetToMobile}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<Text style={[styles.linkText, isLoading && { opacity: 0.4 }]}>
|
||||
Change mobile number
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const showOtp = state.step === 'otp' || state.step === 'processing';
|
||||
|
||||
return (
|
||||
@@ -51,15 +183,15 @@ export const OTPBindUI: React.FC<OTPBindUIProps> = ({
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
|
||||
{state.step === 'mobile' && (
|
||||
{!showOtp && (
|
||||
<>
|
||||
{!!state.errorMessage && (
|
||||
<Text style={styles.errorText}>{state.errorMessage}</Text>
|
||||
)}
|
||||
<Text style={styles.label}>手机号</Text>
|
||||
<Text style={styles.label}>Mobile</Text>
|
||||
<TextInput
|
||||
style={[styles.input, !!state.errorMessage && styles.inputError]}
|
||||
placeholder={`请输入 ${mobileLength} 位手机号`}
|
||||
placeholder={`Enter ${mobileLength}-digit mobile number`}
|
||||
placeholderTextColor="#aaa"
|
||||
keyboardType="phone-pad"
|
||||
maxLength={mobileLength}
|
||||
@@ -79,7 +211,7 @@ export const OTPBindUI: React.FC<OTPBindUIProps> = ({
|
||||
{isLoading ? (
|
||||
<ActivityIndicator color="#fff" />
|
||||
) : (
|
||||
<Text style={styles.btnText}>获取验证码</Text>
|
||||
<Text style={styles.btnText}>Send OTP</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
@@ -87,37 +219,14 @@ export const OTPBindUI: React.FC<OTPBindUIProps> = ({
|
||||
|
||||
{showOtp && (
|
||||
<>
|
||||
<Text style={styles.hint}>
|
||||
{state.needPassword
|
||||
? `验证码已发送至 ${state.mobile},该账号需输入密码`
|
||||
: `验证码已发送至 ${state.mobile}`}
|
||||
</Text>
|
||||
<Text style={styles.hint}>OTP sent to {state.mobile}</Text>
|
||||
{!!state.errorMessage && (
|
||||
<Text style={styles.errorText}>{state.errorMessage}</Text>
|
||||
)}
|
||||
{state.needPassword && (
|
||||
<>
|
||||
<Text style={styles.label}>Amazon 密码</Text>
|
||||
<TextInput
|
||||
style={[styles.input, !!state.errorMessage && styles.inputError]}
|
||||
placeholder="请输入账号密码"
|
||||
placeholderTextColor="#aaa"
|
||||
secureTextEntry
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
value={state.password}
|
||||
onChangeText={t => {
|
||||
actions.setPassword(t);
|
||||
if (state.errorMessage) actions.clearError();
|
||||
}}
|
||||
editable={!isLoading}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Text style={styles.label}>验证码</Text>
|
||||
<Text style={styles.label}>Verification code</Text>
|
||||
<TextInput
|
||||
style={[styles.input, !!state.errorMessage && styles.inputError]}
|
||||
placeholder={`请输入 ${otpLength} 位验证码`}
|
||||
placeholder={`Enter ${otpLength}-digit code`}
|
||||
placeholderTextColor="#aaa"
|
||||
keyboardType="number-pad"
|
||||
maxLength={otpLength}
|
||||
@@ -137,16 +246,17 @@ export const OTPBindUI: React.FC<OTPBindUIProps> = ({
|
||||
{isLoading ? (
|
||||
<ActivityIndicator color="#fff" />
|
||||
) : (
|
||||
<Text style={styles.btnText}>验证并绑定</Text>
|
||||
<Text style={styles.btnText}>Verify & Bind</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
{renderResendOtp()}
|
||||
<TouchableOpacity
|
||||
style={styles.linkBtn}
|
||||
onPress={actions.resetToMobile}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<Text style={[styles.linkText, isLoading && { opacity: 0.4 }]}>
|
||||
重新输入手机号
|
||||
Change mobile number
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
@@ -200,6 +310,10 @@ const styles = StyleSheet.create({
|
||||
marginBottom: 14,
|
||||
backgroundColor: '#fafafa',
|
||||
},
|
||||
inputLocked: {
|
||||
backgroundColor: '#f0f0f0',
|
||||
color: '#666',
|
||||
},
|
||||
inputError: {
|
||||
borderColor: '#ff3b30',
|
||||
backgroundColor: '#fff8f7',
|
||||
@@ -235,4 +349,7 @@ const styles = StyleSheet.create({
|
||||
color: '#007AFF',
|
||||
fontSize: 13,
|
||||
},
|
||||
linkTextDisabled: {
|
||||
color: '#aaa',
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user