轻松聘
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.
 
 
 

173 lines
6.7 KiB

import { Alert, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
import { colors } 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 (
<ScreenShell showBack onBack={back}>
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={styles.content}>
<View style={styles.brandArea}>
<Illustration name="logo" size={88} radius={22} />
</View>
{ready ? (
<Text style={styles.phone}>{maskedPhone}</Text>
) : (
<Text style={[styles.phonePending, failed && styles.phoneFailed]}>
{failed ? '未能获取本机号码' : '正在获取本机号码…'}
</Text>
)}
<Text style={styles.subtitle}>
{failed ? '可重新获取,或改用验证码登录' : '首次验证通过即注册轻松聘账号'}
</Text>
<View style={styles.btnArea}>
<PrimaryButton
label="一键登录"
active={ready && !busy}
loading={logging}
onPress={() => gate(handleLogin)}
/>
{failed ? (
<View style={styles.fallbackRow}>
<Pressable style={styles.fallbackBtn} onPress={refresh} hitSlop={6}>
<Text style={styles.fallbackText}>重新获取</Text>
</Pressable>
<Pressable
style={styles.fallbackBtn}
onPress={() => gate(() => navigate('P2'))}
hitSlop={6}
>
<Text style={styles.fallbackText}>改用验证码登录</Text>
</Pressable>
</View>
) : null}
</View>
{loginError ? <Text style={styles.formError}>{loginError.message}</Text> : null}
{fetchError && !loginError ? (
<Text style={styles.formError}>{fetchError.message}</Text>
) : null}
<View style={styles.agree}>
<AgreementCheckbox
checked={agreed}
onToggle={() => setData({ agreed: !agreed })}
text={
<Text style={styles.agreeText}>
已阅读并同意
<Text style={styles.link} onPress={showCarrierTerms}>
《{carrierName}认证服务条款》
</Text>
、
<Text style={styles.link} onPress={() => navigate('B2')}>《轻松聘用户协议》</Text>
和
<Text style={styles.link} onPress={() => navigate('B2')}>《隐私政策》</Text>
,允许轻松聘统一管理本人账号信息
</Text>
}
/>
</View>
</ScrollView>
<View style={styles.footer}>
<SupportFooter />
<Text style={styles.carrierNote}>认证服务由{carrierName}提供</Text>
</View>
<AgreementModal {...modalProps} />
</ScreenShell>
);
}
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: 12 },
btnArea: { marginTop: 36 },
fallbackRow: {
flexDirection: 'row',
justifyContent: 'center',
gap: 24,
marginTop: 18,
},
fallbackBtn: { paddingVertical: 6, paddingHorizontal: 4 },
fallbackText: { fontSize: 14, fontWeight: '600', color: colors.primaryText },
formError: { marginTop: 10, 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: 12 },
});