You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
5.3 KiB
139 lines
5.3 KiB
import { useEffect, useRef, useState } from 'react';
|
|
import { Alert, Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
|
|
import { colors, spacing } from '../../../src/theme';
|
|
import { SMS_HELP_BODY, SMS_HELP_TITLE } from '../../../src/constants';
|
|
import { useOnboarding } from '../../../src/hooks';
|
|
import { ScreenShell } from '../ui/ScreenShell';
|
|
import { PrimaryButton } from '../ui/PrimaryButton';
|
|
|
|
const RESEND_SECONDS = 60;
|
|
|
|
/** P2W 微信登录短信验证码页:×关闭 + 4格验证码 + 重新发送倒计时 + 语音/收不到短信入口 */
|
|
export function P2WechatCode() {
|
|
const { data, navigate, back } = useOnboarding();
|
|
const [code, setCode] = useState('');
|
|
const [left, setLeft] = useState(RESEND_SECONDS);
|
|
const timer = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
/** 隐藏输入框的引用:点格子区域时把焦点交回它,键盘才会弹出来 */
|
|
const inputRef = useRef<TextInput>(null);
|
|
|
|
useEffect(() => {
|
|
timer.current = setInterval(() => {
|
|
setLeft((l) => {
|
|
if (l <= 1) {
|
|
if (timer.current) clearInterval(timer.current);
|
|
return 0;
|
|
}
|
|
return l - 1;
|
|
});
|
|
}, 1000);
|
|
return () => {
|
|
if (timer.current) clearInterval(timer.current);
|
|
};
|
|
}, []);
|
|
|
|
const phone = data.phone;
|
|
/**
|
|
* 手机尾号(含两侧空格,无尾号时为空串,句子里不留空)。
|
|
* 原先是 `phone.length === 11 ? phone.slice(7) : '9367'` —— 没有号码时拿编造的
|
|
* 「9367」当用户的号码展示。
|
|
*/
|
|
const tail = phone.length === 11 ? ` ${phone.slice(7)} ` : '';
|
|
const canNext = code.length >= 4;
|
|
|
|
/** 「接收不到短信」提示,与 P2 用同一份文案常量 */
|
|
const showSmsHelp = () => Alert.alert(SMS_HELP_TITLE, SMS_HELP_BODY);
|
|
|
|
const resend = () => {
|
|
if (left > 0) return;
|
|
setLeft(RESEND_SECONDS);
|
|
setCode('');
|
|
timer.current = setInterval(() => {
|
|
setLeft((l) => {
|
|
if (l <= 1) {
|
|
if (timer.current) clearInterval(timer.current);
|
|
return 0;
|
|
}
|
|
return l - 1;
|
|
});
|
|
}, 1000);
|
|
};
|
|
|
|
return (
|
|
<ScreenShell showBack onBack={back} backIcon="close">
|
|
<Text style={styles.h1}>输入短信验证码</Text>
|
|
<Text style={styles.subtitle}>已向您的手机{tail}发送验证码</Text>
|
|
|
|
{/* 4 格分离输入:透明输入层覆盖,格子仅做展示;点格子把焦点交回输入框 */}
|
|
<Pressable style={styles.cellsWrap} onPress={() => inputRef.current?.focus()}>
|
|
<View style={styles.cellsRow}>
|
|
{[0, 1, 2, 3].map((i) => (
|
|
<View key={i} style={styles.cell}>
|
|
<Text style={styles.cellText}>{code[i] ?? ''}</Text>
|
|
<View style={[styles.cellLine, code.length === i && styles.cellLineActive]} />
|
|
</View>
|
|
))}
|
|
</View>
|
|
<TextInput
|
|
ref={inputRef}
|
|
style={styles.hiddenInput}
|
|
value={code}
|
|
onChangeText={(t) => setCode(t.replace(/[^\d]/g, '').slice(0, 4))}
|
|
keyboardType="number-pad"
|
|
maxLength={4}
|
|
autoFocus
|
|
/>
|
|
</Pressable>
|
|
|
|
<Pressable onPress={resend} hitSlop={6} disabled={left > 0}>
|
|
<Text style={[styles.resend, left > 0 && styles.resendDisabled]}>
|
|
{left > 0 ? `重新发送(${left}s)` : '重新发送'}
|
|
</Text>
|
|
</Pressable>
|
|
|
|
<View style={styles.btnArea}>
|
|
<PrimaryButton label="下一步" active={canNext} onPress={() => navigate('B1')} />
|
|
</View>
|
|
|
|
{/* 「语音接听验证码」入口已移除:项目没有语音验证码通道,摆一个点了没反应的入口
|
|
等于宣称有这个能力。等接入语音通道再恢复(同 P5 移除「自动定位」的理由) */}
|
|
<Pressable onPress={showSmsHelp} hitSlop={6} style={styles.helpRow}>
|
|
<Text style={styles.helpText}>接收不到短信?</Text>
|
|
</Pressable>
|
|
|
|
<View style={styles.toastBar}>
|
|
<Text style={styles.toastText}>短信验证码正在发送,请注意查收</Text>
|
|
</View>
|
|
</ScreenShell>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
h1: { fontSize: 24, fontWeight: '800', color: colors.textPrimary, marginTop: spacing.block },
|
|
subtitle: { fontSize: 14, color: colors.textSecondary, marginTop: spacing.block },
|
|
cellsWrap: { marginTop: 34 },
|
|
cellsRow: { flexDirection: 'row', gap: spacing.block },
|
|
cell: { flex: 1, alignItems: 'center' },
|
|
cellText: { fontSize: 26, fontWeight: '700', color: colors.textPrimary, minHeight: 36 },
|
|
cellLine: { width: '100%', height: 2, backgroundColor: colors.border, marginTop: 8 },
|
|
cellLineActive: { backgroundColor: colors.primary },
|
|
hiddenInput: {
|
|
...StyleSheet.absoluteFill,
|
|
opacity: 0,
|
|
},
|
|
resend: { textAlign: 'center', fontSize: 14, color: colors.textSecondary, marginTop: spacing.section },
|
|
resendDisabled: { color: colors.textHint },
|
|
btnArea: { marginTop: 30 },
|
|
helpRow: { alignSelf: 'flex-start', marginTop: spacing.section },
|
|
// 已是可点入口,用可读对比度的 primaryText(textHint 在 13pt 下不达 AA)
|
|
helpText: { fontSize: 13, color: colors.primaryText },
|
|
toastBar: {
|
|
alignSelf: 'center',
|
|
marginTop: 30,
|
|
paddingHorizontal: 18,
|
|
paddingVertical: spacing.sm,
|
|
borderRadius: 8,
|
|
backgroundColor: colors.borderLight,
|
|
},
|
|
toastText: { fontSize: 13, color: colors.textDisabled },
|
|
});
|
|
|