feat(auth): add user identity verification system with new screens
Add comprehensive identity verification flow including: - New verification guide screen at /verification-guide for selecting identity type - New verification form screen at /verification-form for submitting verification documents - Verification settings screen at /profile/verification for managing verification status - Move terms and privacy policy to root-level routes (/terms, /privacy) - Update login to require active terms agreement checkbox - Database: add in-memory fallback when OPFS is unavailable on web BREAKING CHANGE: Terms and privacy policy routes moved from /profile/terms and /profile/privacy to /terms and /privacy
This commit is contained in:
@@ -40,6 +40,7 @@ export default function ProfileStackLayout() {
|
|||||||
<Stack.Screen name="about" options={{ title: '关于我们' }} />
|
<Stack.Screen name="about" options={{ title: '关于我们' }} />
|
||||||
<Stack.Screen name="terms" options={{ title: '用户协议' }} />
|
<Stack.Screen name="terms" options={{ title: '用户协议' }} />
|
||||||
<Stack.Screen name="privacy" options={{ title: '隐私政策' }} />
|
<Stack.Screen name="privacy" options={{ title: '隐私政策' }} />
|
||||||
|
<Stack.Screen name="verification" options={{ title: '身份认证' }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
5
app/(app)/(tabs)/profile/verification.tsx
Normal file
5
app/(app)/(tabs)/profile/verification.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { VerificationSettingsScreen } from '@/screens/profile/VerificationSettingsScreen';
|
||||||
|
|
||||||
|
export default function VerificationSettingsPage() {
|
||||||
|
return <VerificationSettingsScreen />;
|
||||||
|
}
|
||||||
5
app/(auth)/verification-form.tsx
Normal file
5
app/(auth)/verification-form.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { VerificationFormScreen } from '@/screens/auth/VerificationFormScreen';
|
||||||
|
|
||||||
|
export default function VerificationFormPage() {
|
||||||
|
return <VerificationFormScreen />;
|
||||||
|
}
|
||||||
5
app/(auth)/verification-guide.tsx
Normal file
5
app/(auth)/verification-guide.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { VerificationGuideScreen } from '@/screens/auth/VerificationGuideScreen';
|
||||||
|
|
||||||
|
export default function VerificationGuidePage() {
|
||||||
|
return <VerificationGuideScreen />;
|
||||||
|
}
|
||||||
@@ -175,6 +175,30 @@ function ThemedStack() {
|
|||||||
<Stack.Screen name="index" options={{ headerShown: false }} />
|
<Stack.Screen name="index" options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="(auth)" options={{ headerShown: false }} />
|
<Stack.Screen name="(auth)" options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="(app)" options={{ headerShown: false }} />
|
<Stack.Screen name="(app)" options={{ headerShown: false }} />
|
||||||
|
<Stack.Screen
|
||||||
|
name="terms"
|
||||||
|
options={{
|
||||||
|
headerShown: true,
|
||||||
|
title: '用户协议',
|
||||||
|
headerStyle: { backgroundColor: colors.background.paper },
|
||||||
|
headerTintColor: colors.text.primary,
|
||||||
|
headerShadowVisible: false,
|
||||||
|
headerBackVisible: false,
|
||||||
|
headerLeft: () => <AppBackButton onPress={() => router.back()} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="privacy"
|
||||||
|
options={{
|
||||||
|
headerShown: true,
|
||||||
|
title: '隐私政策',
|
||||||
|
headerStyle: { backgroundColor: colors.background.paper },
|
||||||
|
headerTintColor: colors.text.primary,
|
||||||
|
headerShadowVisible: false,
|
||||||
|
headerBackVisible: false,
|
||||||
|
headerLeft: () => <AppBackButton onPress={() => router.back()} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="post/[postId]"
|
name="post/[postId]"
|
||||||
options={{
|
options={{
|
||||||
|
|||||||
5
app/privacy.tsx
Normal file
5
app/privacy.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { PrivacyPolicyScreen } from '../src/screens/profile/PrivacyPolicyScreen';
|
||||||
|
|
||||||
|
export default function PrivacyRoute() {
|
||||||
|
return <PrivacyPolicyScreen />;
|
||||||
|
}
|
||||||
5
app/terms.tsx
Normal file
5
app/terms.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { TermsOfServiceScreen } from '../src/screens/profile/TermsOfServiceScreen';
|
||||||
|
|
||||||
|
export default function TermsRoute() {
|
||||||
|
return <TermsOfServiceScreen />;
|
||||||
|
}
|
||||||
@@ -9,6 +9,6 @@ export const DB_CONFIG = {
|
|||||||
DB_NAME_PREFIX: 'carrot_bbs_',
|
DB_NAME_PREFIX: 'carrot_bbs_',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const getDbName = (userId: string | null): string => {
|
export function getDbName(userId: string | null): string {
|
||||||
return userId ? `${DB_CONFIG.DB_NAME_PREFIX}${userId}.db` : DB_CONFIG.DEFAULT_DB_NAME;
|
return userId ? `${DB_CONFIG.DB_NAME_PREFIX}${userId}.db` : DB_CONFIG.DEFAULT_DB_NAME;
|
||||||
};
|
}
|
||||||
@@ -109,25 +109,31 @@ class DatabaseManager {
|
|||||||
const isWeb = typeof window !== 'undefined';
|
const isWeb = typeof window !== 'undefined';
|
||||||
|
|
||||||
return LockManager.withWebLock(`${DB_CONFIG.LOCK_PREFIX}${dbName}`, async () => {
|
return LockManager.withWebLock(`${DB_CONFIG.LOCK_PREFIX}${dbName}`, async () => {
|
||||||
|
let lastError: unknown;
|
||||||
for (let attempt = 0; attempt <= DB_CONFIG.MAX_RETRIES; attempt++) {
|
for (let attempt = 0; attempt <= DB_CONFIG.MAX_RETRIES; attempt++) {
|
||||||
try {
|
try {
|
||||||
const database = await SQLite.openDatabaseAsync(dbName);
|
const database = await SQLite.openDatabaseAsync(dbName);
|
||||||
console.log(`[DatabaseManager] 成功打开数据库 (尝试 ${attempt + 1}/${DB_CONFIG.MAX_RETRIES + 1})`);
|
console.log(`[DatabaseManager] 成功打开数据库 (尝试 ${attempt + 1}/${DB_CONFIG.MAX_RETRIES + 1})`);
|
||||||
return database;
|
return database;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
lastError = error;
|
||||||
const msg = String(error);
|
const msg = String(error);
|
||||||
console.error(`[DatabaseManager] 打开失败 (尝试 ${attempt + 1}/${DB_CONFIG.MAX_RETRIES + 1}):`, msg);
|
console.error(`[DatabaseManager] 打开失败 (尝试 ${attempt + 1}/${DB_CONFIG.MAX_RETRIES + 1}):`, msg);
|
||||||
|
|
||||||
|
if (isWeb && msg.includes('cannot create file')) {
|
||||||
|
console.log('[DatabaseManager] OPFS 不可用,回退到内存数据库');
|
||||||
|
const memoryDb = await SQLite.openDatabaseAsync(':memory:');
|
||||||
|
return memoryDb;
|
||||||
|
}
|
||||||
|
|
||||||
if (attempt < DB_CONFIG.MAX_RETRIES) {
|
if (attempt < DB_CONFIG.MAX_RETRIES) {
|
||||||
const delay = isWeb ? DB_CONFIG.WEB_RETRY_DELAY_BASE * (attempt + 1) : DB_CONFIG.NATIVE_RETRY_DELAY;
|
const delay = isWeb ? DB_CONFIG.WEB_RETRY_DELAY_BASE * (attempt + 1) : DB_CONFIG.NATIVE_RETRY_DELAY;
|
||||||
console.warn(`[DatabaseManager] ${delay}ms 后重试...`);
|
console.warn(`[DatabaseManager] ${delay}ms 后重试...`);
|
||||||
await new Promise(r => setTimeout(r, delay));
|
await new Promise(r => setTimeout(r, delay));
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error('打开数据库失败');
|
throw lastError;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -185,9 +185,24 @@ export function hrefProfileAbout(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function hrefProfileTerms(): string {
|
export function hrefProfileTerms(): string {
|
||||||
return '/profile/terms';
|
return '/terms';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hrefProfilePrivacy(): string {
|
export function hrefProfilePrivacy(): string {
|
||||||
return '/profile/privacy';
|
return '/privacy';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hrefProfileVerification(): string {
|
||||||
|
return '/profile/verification';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hrefVerificationGuide(): string {
|
||||||
|
return '/verification-guide';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hrefVerificationForm(identity?: string): string {
|
||||||
|
if (identity) {
|
||||||
|
return `/verification-form?identity=${encodeURIComponent(identity)}`;
|
||||||
|
}
|
||||||
|
return '/verification-form';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export const LoginScreen: React.FC = () => {
|
|||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const [agreedToTerms, setAgreedToTerms] = useState(false);
|
||||||
|
|
||||||
// 动画值
|
// 动画值
|
||||||
const fadeAnim = useRef(new Animated.Value(0)).current;
|
const fadeAnim = useRef(new Animated.Value(0)).current;
|
||||||
@@ -84,6 +85,10 @@ export const LoginScreen: React.FC = () => {
|
|||||||
showPrompt({ title: '提示', message: '请输入密码', type: 'warning' });
|
showPrompt({ title: '提示', message: '请输入密码', type: 'warning' });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!agreedToTerms) {
|
||||||
|
showPrompt({ title: '提示', message: '请同意用户协议和隐私政策', type: 'warning' });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -211,6 +216,37 @@ export const LoginScreen: React.FC = () => {
|
|||||||
<Text style={styles.forgotPasswordText}>忘记密码?</Text>
|
<Text style={styles.forgotPasswordText}>忘记密码?</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
{/* 服务条款勾选 */}
|
||||||
|
<View style={styles.termsContainer}>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => setAgreedToTerms(!agreedToTerms)}
|
||||||
|
activeOpacity={0.8}
|
||||||
|
style={styles.checkboxWrapper}
|
||||||
|
>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name={agreedToTerms ? 'checkbox-marked' : 'checkbox-blank-outline'}
|
||||||
|
size={22}
|
||||||
|
color={agreedToTerms ? THEME_COLORS.primary : colors.text.hint}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<Text style={styles.termsText}>
|
||||||
|
我已阅读并同意
|
||||||
|
<Text
|
||||||
|
style={styles.termsLink}
|
||||||
|
onPress={() => router.push(hrefs.hrefProfileTerms())}
|
||||||
|
>
|
||||||
|
《用户协议》
|
||||||
|
</Text>
|
||||||
|
和
|
||||||
|
<Text
|
||||||
|
style={styles.termsLink}
|
||||||
|
onPress={() => router.push(hrefs.hrefProfilePrivacy())}
|
||||||
|
>
|
||||||
|
《隐私政策》
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
{/* 登录按钮 */}
|
{/* 登录按钮 */}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.loginButton, loading && styles.loginButtonDisabled]}
|
style={[styles.loginButton, loading && styles.loginButtonDisabled]}
|
||||||
@@ -234,15 +270,7 @@ export const LoginScreen: React.FC = () => {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 协议提示 */}
|
|
||||||
<View style={styles.policyFooter}>
|
|
||||||
<Text style={styles.policyText}>
|
|
||||||
登录即表示您同意
|
|
||||||
<Text style={styles.policyLink} onPress={() => router.push(hrefs.hrefProfileTerms())}>《用户协议》</Text>
|
|
||||||
和
|
|
||||||
<Text style={styles.policyLink} onPress={() => router.push(hrefs.hrefProfilePrivacy())}>《隐私政策》</Text>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</KeyboardAvoidingView>
|
</KeyboardAvoidingView>
|
||||||
@@ -394,6 +422,26 @@ function createLoginStyles(colors: AppColors) {
|
|||||||
color: THEME_COLORS.primary,
|
color: THEME_COLORS.primary,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
|
termsContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
marginBottom: 20,
|
||||||
|
marginTop: 4,
|
||||||
|
},
|
||||||
|
checkboxWrapper: {
|
||||||
|
paddingTop: 2,
|
||||||
|
},
|
||||||
|
termsText: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
marginLeft: 10,
|
||||||
|
flex: 1,
|
||||||
|
lineHeight: 22,
|
||||||
|
},
|
||||||
|
termsLink: {
|
||||||
|
color: THEME_COLORS.primary,
|
||||||
|
fontWeight: '500',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export const RegisterStep3Profile: React.FC<RegisterStepProps> = ({
|
|||||||
|
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||||
const [agreedToTerms, setAgreedToTerms] = useState(true);
|
const [agreedToTerms, setAgreedToTerms] = useState(false);
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||||
|
|
||||||
const validateForm = (): boolean => {
|
const validateForm = (): boolean => {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const IDENTITY_OPTIONS = [
|
|||||||
key: 'teacher',
|
key: 'teacher',
|
||||||
title: '教职工',
|
title: '教职工',
|
||||||
description: '需要提交工作证或教师资格证',
|
description: '需要提交工作证或教师资格证',
|
||||||
icon: 'chalkboard-teacher',
|
icon: 'account-tie',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'staff',
|
key: 'staff',
|
||||||
@@ -82,10 +82,7 @@ export const VerificationGuideScreen: React.FC = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 导航到认证表单页面
|
// 导航到认证表单页面
|
||||||
router.push({
|
router.push(hrefs.hrefVerificationForm(selectedIdentity));
|
||||||
pathname: '/auth/verification-form',
|
|
||||||
params: { identity: selectedIdentity },
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderIdentityCard = (option: typeof IDENTITY_OPTIONS[0]) => {
|
const renderIdentityCard = (option: typeof IDENTITY_OPTIONS[0]) => {
|
||||||
@@ -148,7 +145,7 @@ export const VerificationGuideScreen: React.FC = () => {
|
|||||||
<View style={styles.benefitsContainer}>
|
<View style={styles.benefitsContainer}>
|
||||||
<View style={styles.benefitItem}>
|
<View style={styles.benefitItem}>
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name="verified"
|
name="check-decagram"
|
||||||
size={20}
|
size={20}
|
||||||
color={THEME_COLORS.primary}
|
color={THEME_COLORS.primary}
|
||||||
/>
|
/>
|
||||||
@@ -164,7 +161,7 @@ export const VerificationGuideScreen: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
<View style={styles.benefitItem}>
|
<View style={styles.benefitItem}>
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name="account-group"
|
name="account-group-outline"
|
||||||
size={20}
|
size={20}
|
||||||
color={THEME_COLORS.primary}
|
color={THEME_COLORS.primary}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ export { LoginScreen } from './LoginScreen';
|
|||||||
export { RegisterScreen } from './RegisterScreen';
|
export { RegisterScreen } from './RegisterScreen';
|
||||||
export { ForgotPasswordScreen } from './ForgotPasswordScreen';
|
export { ForgotPasswordScreen } from './ForgotPasswordScreen';
|
||||||
export { WelcomeScreen } from './WelcomeScreen';
|
export { WelcomeScreen } from './WelcomeScreen';
|
||||||
|
export { VerificationGuideScreen } from './VerificationGuideScreen';
|
||||||
|
export { VerificationFormScreen } from './VerificationFormScreen';
|
||||||
|
|
||||||
// 注册步骤组件
|
// 注册步骤组件
|
||||||
export { RegisterStep1Email } from './RegisterStep1Email';
|
export { RegisterStep1Email } from './RegisterStep1Email';
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ export const SettingsScreen: React.FC = () => {
|
|||||||
router.push(hrefs.hrefProfileEdit());
|
router.push(hrefs.hrefProfileEdit());
|
||||||
break;
|
break;
|
||||||
case 'verification':
|
case 'verification':
|
||||||
router.push('/profile/verification');
|
router.push(hrefs.hrefProfileVerification());
|
||||||
break;
|
break;
|
||||||
case 'notification_settings':
|
case 'notification_settings':
|
||||||
router.push(hrefs.hrefProfileNotifications());
|
router.push(hrefs.hrefProfileNotifications());
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export const VerificationSettingsScreen: React.FC = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleStartVerification = () => {
|
const handleStartVerification = () => {
|
||||||
router.push('/auth/verification-guide');
|
router.push(hrefs.hrefVerificationGuide());
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleViewRecords = () => {
|
const handleViewRecords = () => {
|
||||||
@@ -109,27 +109,10 @@ export const VerificationSettingsScreen: React.FC = () => {
|
|||||||
const identityText = IDENTITY_TEXT[status?.identity || 'general'];
|
const identityText = IDENTITY_TEXT[status?.identity || 'general'];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<ScrollView
|
||||||
<View style={styles.header}>
|
contentContainerStyle={styles.scrollContent}
|
||||||
<TouchableOpacity
|
showsVerticalScrollIndicator={false}
|
||||||
style={styles.backButton}
|
>
|
||||||
onPress={() => router.back()}
|
|
||||||
activeOpacity={0.8}
|
|
||||||
>
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name="arrow-left"
|
|
||||||
size={24}
|
|
||||||
color={colors.text?.primary || '#333'}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<Text style={styles.headerTitle}>身份认证</Text>
|
|
||||||
<View style={styles.headerRight} />
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
contentContainerStyle={styles.scrollContent}
|
|
||||||
showsVerticalScrollIndicator={false}
|
|
||||||
>
|
|
||||||
{/* 状态卡片 */}
|
{/* 状态卡片 */}
|
||||||
<View style={styles.statusCard}>
|
<View style={styles.statusCard}>
|
||||||
<View style={[styles.statusIconContainer, { backgroundColor: statusConfig.color + '20' }]}>
|
<View style={[styles.statusIconContainer, { backgroundColor: statusConfig.color + '20' }]}>
|
||||||
@@ -167,7 +150,7 @@ export const VerificationSettingsScreen: React.FC = () => {
|
|||||||
<View style={styles.benefitItem}>
|
<View style={styles.benefitItem}>
|
||||||
<View style={styles.benefitIconContainer}>
|
<View style={styles.benefitIconContainer}>
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name="verified"
|
name="check-decagram"
|
||||||
size={24}
|
size={24}
|
||||||
color={THEME_COLORS.primary}
|
color={THEME_COLORS.primary}
|
||||||
/>
|
/>
|
||||||
@@ -183,7 +166,7 @@ export const VerificationSettingsScreen: React.FC = () => {
|
|||||||
<View style={styles.benefitItem}>
|
<View style={styles.benefitItem}>
|
||||||
<View style={styles.benefitIconContainer}>
|
<View style={styles.benefitIconContainer}>
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name="lock-open-variant"
|
name="lock-open-outline"
|
||||||
size={24}
|
size={24}
|
||||||
color={THEME_COLORS.primary}
|
color={THEME_COLORS.primary}
|
||||||
/>
|
/>
|
||||||
@@ -199,7 +182,7 @@ export const VerificationSettingsScreen: React.FC = () => {
|
|||||||
<View style={styles.benefitItem}>
|
<View style={styles.benefitItem}>
|
||||||
<View style={styles.benefitIconContainer}>
|
<View style={styles.benefitIconContainer}>
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name="account-group"
|
name="account-group-outline"
|
||||||
size={24}
|
size={24}
|
||||||
color={THEME_COLORS.primary}
|
color={THEME_COLORS.primary}
|
||||||
/>
|
/>
|
||||||
@@ -278,7 +261,6 @@ export const VerificationSettingsScreen: React.FC = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</SafeAreaView>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export { AccountSecurityScreen } from './AccountSecurityScreen';
|
|||||||
export { AboutScreen } from './AboutScreen';
|
export { AboutScreen } from './AboutScreen';
|
||||||
export { TermsOfServiceScreen } from './TermsOfServiceScreen';
|
export { TermsOfServiceScreen } from './TermsOfServiceScreen';
|
||||||
export { PrivacyPolicyScreen } from './PrivacyPolicyScreen';
|
export { PrivacyPolicyScreen } from './PrivacyPolicyScreen';
|
||||||
|
export { VerificationSettingsScreen } from './VerificationSettingsScreen';
|
||||||
|
|
||||||
// 导出 Hook 供需要自定义的场景使用
|
// 导出 Hook 供需要自定义的场景使用
|
||||||
export { useUserProfile, createSharedProfileStyles, TABS, TAB_ICONS } from './useUserProfile';
|
export { useUserProfile, createSharedProfileStyles, TABS, TAB_ICONS } from './useUserProfile';
|
||||||
|
|||||||
Reference in New Issue
Block a user