import { Alert, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'; import { colors, spacing } from '../../../src/theme'; import { CARRIER_LABEL } from '../../../src/constants'; import { useAgreementGate, useCarrierPhone, useLogin, useOnboarding } from '../../../src/hooks'; import { useAuth } from '../../../src/context/AuthContext'; import { ScreenShell } from '../ui/ScreenShell'; import { Illustration } from '../ui/Illustration'; import { AgreementCheckbox } from '../ui/AgreementCheckbox'; import { PrimaryButton } from '../ui/PrimaryButton'; import { SupportFooter } from '../ui/SupportFooter'; import { AgreementModal } from '../ui/AgreementModal'; /** * P2Q 一键登录确认:本机号码认证。 * * 取号(运营商 SDK)与换登录态是两步,本页负责串起来: * 1. 进入页面即取号(useCarrierPhone)→ 展示掩码号码; * 2. 点「一键登录」→ 用取号令牌换正式登录态 → 写登录态 → 把号码写回引导资料 → 进 P3。 * * 原先这里读的是 `data.phone`(只有短信登录才会写),运营商通道的号码既没取也没存, * 所以永远显示占位「本机号码」;登录接口返回的 phone 也被直接丢掉。 * 现在取号失败时不再假装号码还在:「一键登录」置灰,同时给出「重新获取」与「改用验证码登录」。 */ export function P2QuickLogin() { const { data, setData, navigate, back } = useOnboarding(); const { gate, modalProps } = useAgreementGate(); const { carrierToken, maskedPhone, carrier, ready, loading: fetching, error: fetchError, refresh } = useCarrierPhone(); const { loginByCarrier, loading: logging, error: loginError } = useLogin(); /** 登录态写入:此前本页只跳 P3 不写 token */ const { login: writeAuth } = useAuth(); const agreed = data.agreed; const carrierName = CARRIER_LABEL[carrier]; /** 取号失败:号码与令牌都没有,本页只能提供重试或改走验证码登录 */ const failed = !fetching && !ready; const busy = fetching || logging; /** 一键登录:用取号令牌换登录态,成功后才进 P3 */ const handleLogin = async () => { try { const result = await loginByCarrier(carrierToken); writeAuth(result.token); // 号码写回引导资料:后续步骤(P2P/P2R 预填、B3 联系人电话)都以这里为准 setData({ phone: result.phone }); } catch { // 失败原因由 useLogin 记录,页面下方展示 return; } navigate('P3'); }; /** 运营商认证服务条款由运营商在认证页内提供,本仓库没有该文档,不冒充能打开它 */ const showCarrierTerms = () => { Alert.alert( `${carrierName}认证服务条款`, `一键登录的认证服务由${carrierName}提供,服务条款以认证页展示的内容为准。`, ); }; return ( {ready ? ( {maskedPhone} ) : ( {failed ? '未能获取本机号码' : '正在获取本机号码…'} )} {failed ? '可重新获取,或改用验证码登录' : '首次验证通过即注册轻松聘账号'} gate(handleLogin)} /> {failed ? ( 重新获取 gate(() => navigate('P2'))} hitSlop={6} > 改用验证码登录 ) : null} {loginError ? {loginError.message} : null} {fetchError && !loginError ? ( {fetchError.message} ) : null} setData({ agreed: !agreed })} text={ 已阅读并同意 《{carrierName}认证服务条款》 、 navigate('B2')}>《轻松聘用户协议》 和 navigate('B2')}>《隐私政策》 ,允许轻松聘统一管理本人账号信息 } /> 认证服务由{carrierName}提供 ); } const styles = StyleSheet.create({ content: { flexGrow: 1 }, brandArea: { alignItems: 'center', marginTop: '16%' }, phone: { textAlign: 'center', fontSize: 30, fontWeight: '800', color: colors.textPrimary, marginTop: 40, }, phonePending: { textAlign: 'center', fontSize: 22, fontWeight: '700', color: colors.textHint, marginTop: 44, }, phoneFailed: { fontSize: 18, color: colors.dangerText }, subtitle: { textAlign: 'center', fontSize: 14, color: colors.textSecondary, marginTop: spacing.block }, btnArea: { marginTop: 36 }, fallbackRow: { flexDirection: 'row', justifyContent: 'center', gap: 24, marginTop: spacing.block, }, fallbackBtn: { paddingVertical: 6, paddingHorizontal: 4 }, fallbackText: { fontSize: 14, fontWeight: '600', color: colors.primaryText }, formError: { marginTop: spacing.sm, textAlign: 'center', fontSize: 12, color: colors.danger }, agree: { marginTop: 20 }, agreeText: { fontSize: 12, color: colors.textSecondary, lineHeight: 18 }, link: { color: colors.primary, fontWeight: '600' }, footer: { paddingTop: 16, paddingBottom: 8 }, carrierNote: { textAlign: 'center', fontSize: 12, color: colors.textHint, marginTop: spacing.block }, });