feat(service): add foreground service for background message sync
Implement Android foreground service to keep app alive in background for real-time message sync: - Add withForegroundService expo config plugin for notification channel setup - Add BackgroundSyncManager with REALTIME, BATTERY_SAVER, and DISABLED modes - Add ForegroundServiceModule for native Android foreground service binding - Refactor backgroundService to integrate foreground service and expo-background-task Also refactor auth and profile screens to use dynamic theme colors: - Replace hardcoded THEME_COLORS with colors.primary.main, colors.background.paper, etc. - Add useResolvedColorScheme for dynamic StatusBar styling - Update NotificationSettingsScreen with sync mode selection UI BREAKING CHANGE: Switched from expo-background-fetch to expo-background-task, minimum background sync interval changed from 900s to 15min in config
This commit is contained in:
@@ -26,19 +26,13 @@ import {
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { useAppColors, type AppColors } from '../../theme';
|
||||
import { useAppColors, useResolvedColorScheme, type AppColors } from '../../theme';
|
||||
import { authService, resolveAuthApiError } from '../../services/authService';
|
||||
import { showPrompt } from '../../services/promptService';
|
||||
|
||||
// 胡萝卜橙主题色
|
||||
const THEME_COLORS = {
|
||||
primary: '#FF6B35',
|
||||
primaryLight: '#FF8C5A',
|
||||
primaryDark: '#E55A2B',
|
||||
};
|
||||
|
||||
export const ForgotPasswordScreen: React.FC = () => {
|
||||
const colors = useAppColors();
|
||||
const resolved = useResolvedColorScheme();
|
||||
const styles = useMemo(() => createForgotPasswordStyles(colors), [colors]);
|
||||
const router = useRouter();
|
||||
|
||||
@@ -156,7 +150,7 @@ export const ForgotPasswordScreen: React.FC = () => {
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
|
||||
<StatusBar barStyle={resolved === 'dark' ? 'light-content' : 'dark-content'} backgroundColor={colors.background.paper} />
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
style={styles.keyboardView}
|
||||
@@ -198,7 +192,7 @@ export const ForgotPasswordScreen: React.FC = () => {
|
||||
keyboardType="email-address"
|
||||
/>
|
||||
{email.length > 0 && validateEmail(email) && (
|
||||
<MaterialCommunityIcons name="check-circle" size={20} color={THEME_COLORS.primary} style={styles.checkIcon} />
|
||||
<MaterialCommunityIcons name="check-circle" size={20} color={colors.primary.main} style={styles.checkIcon} />
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
@@ -225,7 +219,7 @@ export const ForgotPasswordScreen: React.FC = () => {
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
{sendingCode ? (
|
||||
<ActivityIndicator size="small" color={THEME_COLORS.primary} />
|
||||
<ActivityIndicator size="small" color={colors.primary.main} />
|
||||
) : (
|
||||
<Text style={styles.sendCodeButtonText}>{countdown > 0 ? `${countdown}s` : '获取验证码'}</Text>
|
||||
)}
|
||||
@@ -295,7 +289,7 @@ export const ForgotPasswordScreen: React.FC = () => {
|
||||
activeOpacity={0.9}
|
||||
>
|
||||
{loading ? (
|
||||
<ActivityIndicator size="small" color="#fff" />
|
||||
<ActivityIndicator size="small" color={colors.text.inverse} />
|
||||
) : (
|
||||
<Text style={styles.submitButtonText}>重置密码</Text>
|
||||
)}
|
||||
@@ -307,7 +301,7 @@ export const ForgotPasswordScreen: React.FC = () => {
|
||||
onPress={() => router.back()}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<MaterialCommunityIcons name="arrow-left" size={18} color={THEME_COLORS.primary} style={{ marginRight: 4 }} />
|
||||
<MaterialCommunityIcons name="arrow-left" size={18} color={colors.primary.main} style={{ marginRight: 4 }} />
|
||||
<Text style={styles.backButtonText}>返回登录</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
@@ -322,7 +316,7 @@ function createForgotPasswordStyles(colors: AppColors) {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: colors.background.paper,
|
||||
},
|
||||
keyboardView: {
|
||||
flex: 1,
|
||||
@@ -354,7 +348,7 @@ function createForgotPasswordStyles(colors: AppColors) {
|
||||
underline: {
|
||||
width: 40,
|
||||
height: 4,
|
||||
backgroundColor: THEME_COLORS.primary,
|
||||
backgroundColor: colors.primary.main,
|
||||
borderRadius: 2,
|
||||
marginTop: 12,
|
||||
marginBottom: 16,
|
||||
@@ -382,7 +376,7 @@ function createForgotPasswordStyles(colors: AppColors) {
|
||||
inputWrapper: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5F5F7',
|
||||
backgroundColor: colors.background.default,
|
||||
borderRadius: 14,
|
||||
paddingHorizontal: 18,
|
||||
height: 56,
|
||||
@@ -412,7 +406,7 @@ function createForgotPasswordStyles(colors: AppColors) {
|
||||
sendCodeButton: {
|
||||
height: 56,
|
||||
paddingHorizontal: 16,
|
||||
backgroundColor: 'rgba(255, 107, 53, 0.1)',
|
||||
backgroundColor: colors.primary.light + '20',
|
||||
borderRadius: 14,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
@@ -424,14 +418,14 @@ function createForgotPasswordStyles(colors: AppColors) {
|
||||
sendCodeButtonText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
color: THEME_COLORS.primary,
|
||||
color: colors.primary.main,
|
||||
},
|
||||
|
||||
// 提交按钮
|
||||
submitButton: {
|
||||
height: 56,
|
||||
borderRadius: 14,
|
||||
backgroundColor: THEME_COLORS.primary,
|
||||
backgroundColor: colors.primary.main,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginTop: 8,
|
||||
@@ -440,7 +434,7 @@ function createForgotPasswordStyles(colors: AppColors) {
|
||||
opacity: 0.7,
|
||||
},
|
||||
submitButtonText: {
|
||||
color: '#fff',
|
||||
color: colors.text.inverse,
|
||||
fontSize: 17,
|
||||
fontWeight: '600',
|
||||
},
|
||||
@@ -453,7 +447,7 @@ function createForgotPasswordStyles(colors: AppColors) {
|
||||
marginTop: 24,
|
||||
},
|
||||
backButtonText: {
|
||||
color: THEME_COLORS.primary,
|
||||
color: colors.primary.main,
|
||||
fontSize: 15,
|
||||
fontWeight: '500',
|
||||
},
|
||||
|
||||
@@ -26,21 +26,17 @@ import {
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { useAppColors, type AppColors } from '../../theme';
|
||||
import { useAppColors, useResolvedColorScheme, type AppColors } from '../../theme';
|
||||
import { useAuthStore } from '../../stores';
|
||||
import * as hrefs from '../../navigation/hrefs';
|
||||
import { useResponsive } from '../../hooks';
|
||||
import { showPrompt } from '../../services/promptService';
|
||||
|
||||
// 胡萝卜橙主题色
|
||||
const THEME_COLORS = {
|
||||
primary: '#FF6B35',
|
||||
primaryLight: '#FF8C5A',
|
||||
primaryDark: '#E55A2B',
|
||||
};
|
||||
|
||||
|
||||
export const LoginScreen: React.FC = () => {
|
||||
const colors = useAppColors();
|
||||
const resolved = useResolvedColorScheme();
|
||||
const styles = useMemo(() => createLoginStyles(colors), [colors]);
|
||||
const router = useRouter();
|
||||
const login = useAuthStore((state) => state.login);
|
||||
@@ -129,7 +125,7 @@ export const LoginScreen: React.FC = () => {
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
|
||||
<StatusBar barStyle={resolved === 'dark' ? 'light-content' : 'dark-content'} backgroundColor={colors.background.paper} />
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
style={styles.keyboardView}
|
||||
@@ -175,7 +171,7 @@ export const LoginScreen: React.FC = () => {
|
||||
returnKeyType="next"
|
||||
/>
|
||||
{username.length > 0 && (
|
||||
<MaterialCommunityIcons name="check-circle" size={20} color={THEME_COLORS.primary} style={styles.checkIcon} />
|
||||
<MaterialCommunityIcons name="check-circle" size={20} color={colors.primary.main} style={styles.checkIcon} />
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
@@ -218,17 +214,17 @@ export const LoginScreen: React.FC = () => {
|
||||
|
||||
{/* 服务条款勾选 */}
|
||||
<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>
|
||||
<TouchableOpacity
|
||||
onPress={() => setAgreedToTerms(!agreedToTerms)}
|
||||
activeOpacity={0.8}
|
||||
style={styles.checkboxWrapper}
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name={agreedToTerms ? 'checkbox-marked' : 'checkbox-blank-outline'}
|
||||
size={22}
|
||||
color={agreedToTerms ? colors.primary.main : colors.text.hint}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.termsText}>
|
||||
我已阅读并同意
|
||||
<Text
|
||||
@@ -255,7 +251,7 @@ export const LoginScreen: React.FC = () => {
|
||||
activeOpacity={0.9}
|
||||
>
|
||||
{loading ? (
|
||||
<ActivityIndicator size="small" color="#FFF" />
|
||||
<ActivityIndicator size="small" color={colors.text.inverse} />
|
||||
) : (
|
||||
<Text style={styles.loginButtonText}>登 录</Text>
|
||||
)}
|
||||
@@ -282,7 +278,7 @@ function createLoginStyles(colors: AppColors) {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: colors.background.paper,
|
||||
},
|
||||
keyboardView: {
|
||||
flex: 1,
|
||||
@@ -317,7 +313,7 @@ function createLoginStyles(colors: AppColors) {
|
||||
underline: {
|
||||
width: 40,
|
||||
height: 4,
|
||||
backgroundColor: THEME_COLORS.primary,
|
||||
backgroundColor: colors.primary.main,
|
||||
borderRadius: 2,
|
||||
marginTop: 12,
|
||||
marginBottom: 16,
|
||||
@@ -345,7 +341,7 @@ function createLoginStyles(colors: AppColors) {
|
||||
inputWrapper: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5F5F7',
|
||||
backgroundColor: colors.background.default,
|
||||
borderRadius: 14,
|
||||
paddingHorizontal: 18,
|
||||
height: 56,
|
||||
@@ -372,7 +368,7 @@ function createLoginStyles(colors: AppColors) {
|
||||
},
|
||||
forgotPasswordText: {
|
||||
fontSize: 14,
|
||||
color: THEME_COLORS.primary,
|
||||
color: colors.primary.main,
|
||||
fontWeight: '500',
|
||||
},
|
||||
|
||||
@@ -380,7 +376,7 @@ function createLoginStyles(colors: AppColors) {
|
||||
loginButton: {
|
||||
height: 56,
|
||||
borderRadius: 14,
|
||||
backgroundColor: THEME_COLORS.primary,
|
||||
backgroundColor: colors.primary.main,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
@@ -390,7 +386,7 @@ function createLoginStyles(colors: AppColors) {
|
||||
loginButtonText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '600',
|
||||
color: '#FFFFFF',
|
||||
color: colors.text.inverse,
|
||||
},
|
||||
|
||||
// 底部
|
||||
@@ -406,7 +402,7 @@ function createLoginStyles(colors: AppColors) {
|
||||
},
|
||||
registerLink: {
|
||||
fontSize: 15,
|
||||
color: THEME_COLORS.primary,
|
||||
color: colors.primary.main,
|
||||
fontWeight: '600',
|
||||
marginLeft: 6,
|
||||
},
|
||||
@@ -419,7 +415,7 @@ function createLoginStyles(colors: AppColors) {
|
||||
color: colors.text.hint,
|
||||
},
|
||||
policyLink: {
|
||||
color: THEME_COLORS.primary,
|
||||
color: colors.primary.main,
|
||||
fontWeight: '500',
|
||||
},
|
||||
termsContainer: {
|
||||
@@ -439,7 +435,7 @@ function createLoginStyles(colors: AppColors) {
|
||||
lineHeight: 22,
|
||||
},
|
||||
termsLink: {
|
||||
color: THEME_COLORS.primary,
|
||||
color: colors.primary.main,
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { useAppColors, type AppColors } from '../../theme';
|
||||
import { useAppColors, useResolvedColorScheme, type AppColors } from '../../theme';
|
||||
import { authService, resolveAuthApiError } from '../../services/authService';
|
||||
import { useAuthStore } from '../../stores';
|
||||
import {
|
||||
@@ -58,6 +58,7 @@ const STEP_COMPONENTS = [
|
||||
|
||||
export const RegisterScreen: React.FC = () => {
|
||||
const colors = useAppColors();
|
||||
const resolved = useResolvedColorScheme();
|
||||
const styles = useMemo(() => createRegisterStyles(colors), [colors]);
|
||||
const router = useRouter();
|
||||
const register = useAuthStore((state) => state.register);
|
||||
@@ -232,7 +233,7 @@ export const RegisterScreen: React.FC = () => {
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
|
||||
<StatusBar barStyle={resolved === 'dark' ? 'light-content' : 'dark-content'} backgroundColor={colors.background.paper} />
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
style={styles.keyboardView}
|
||||
@@ -295,7 +296,7 @@ function createRegisterStyles(colors: AppColors) {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: colors.background.paper,
|
||||
},
|
||||
keyboardView: {
|
||||
flex: 1,
|
||||
@@ -316,7 +317,7 @@ function createRegisterStyles(colors: AppColors) {
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 20,
|
||||
backgroundColor: '#F5F5F7',
|
||||
backgroundColor: colors.background.default,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
@@ -346,7 +347,7 @@ function createRegisterStyles(colors: AppColors) {
|
||||
},
|
||||
loginLink: {
|
||||
fontSize: 15,
|
||||
color: '#FF6B35',
|
||||
color: colors.primary.main,
|
||||
fontWeight: '600',
|
||||
marginLeft: 6,
|
||||
},
|
||||
|
||||
@@ -15,12 +15,6 @@ import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { useAppColors, type AppColors } from '../../theme';
|
||||
import { RegisterStepProps } from './types';
|
||||
|
||||
const THEME_COLORS = {
|
||||
primary: '#FF6B35',
|
||||
primaryLight: '#FF8C5A',
|
||||
primaryDark: '#E55A2B',
|
||||
};
|
||||
|
||||
export const RegisterStep1Email: React.FC<RegisterStepProps> = ({
|
||||
formData,
|
||||
updateFormData,
|
||||
@@ -91,7 +85,7 @@ export const RegisterStep1Email: React.FC<RegisterStepProps> = ({
|
||||
<MaterialCommunityIcons
|
||||
name="check-circle"
|
||||
size={20}
|
||||
color={THEME_COLORS.primary}
|
||||
color={colors.primary.main}
|
||||
style={styles.checkIcon}
|
||||
/>
|
||||
)}
|
||||
@@ -145,7 +139,7 @@ function createStyles(colors: AppColors) {
|
||||
inputWrapper: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5F5F7',
|
||||
backgroundColor: colors.background.default,
|
||||
borderRadius: 14,
|
||||
paddingHorizontal: 18,
|
||||
height: 56,
|
||||
@@ -173,7 +167,7 @@ function createStyles(colors: AppColors) {
|
||||
actionButton: {
|
||||
height: 56,
|
||||
borderRadius: 14,
|
||||
backgroundColor: '#FF6B35',
|
||||
backgroundColor: colors.primary.main,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginTop: 8,
|
||||
@@ -184,7 +178,7 @@ function createStyles(colors: AppColors) {
|
||||
actionButtonText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '600',
|
||||
color: '#FFFFFF',
|
||||
color: colors.text.inverse,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
/**
|
||||
* 注册步骤2:填写验证码
|
||||
* 使用 VerificationCodeInput 组件
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
View,
|
||||
@@ -15,12 +10,6 @@ import { useAppColors, type AppColors } from '../../theme';
|
||||
import { VerificationCodeInput } from '../../components/common';
|
||||
import { RegisterStepProps } from './types';
|
||||
|
||||
const THEME_COLORS = {
|
||||
primary: '#FF6B35',
|
||||
primaryLight: '#FF8C5A',
|
||||
primaryDark: '#E55A2B',
|
||||
};
|
||||
|
||||
export const RegisterStep2Verification: React.FC<RegisterStepProps> = ({
|
||||
formData,
|
||||
updateFormData,
|
||||
@@ -175,13 +164,13 @@ function createStyles(colors: AppColors) {
|
||||
},
|
||||
resendText: {
|
||||
fontSize: 14,
|
||||
color: THEME_COLORS.primary,
|
||||
color: colors.primary.main,
|
||||
fontWeight: '500',
|
||||
},
|
||||
actionButton: {
|
||||
height: 56,
|
||||
borderRadius: 14,
|
||||
backgroundColor: '#FF6B35',
|
||||
backgroundColor: colors.primary.main,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
@@ -193,7 +182,7 @@ function createStyles(colors: AppColors) {
|
||||
actionButtonText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '600',
|
||||
color: '#FFFFFF',
|
||||
color: colors.text.inverse,
|
||||
},
|
||||
backButton: {
|
||||
flexDirection: 'row',
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
/**
|
||||
* 注册步骤3:设置用户信息
|
||||
* 包含用户名、昵称、密码等
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
View,
|
||||
@@ -18,12 +13,6 @@ import { useAppColors, type AppColors } from '../../theme';
|
||||
import { RegisterStepProps } from './types';
|
||||
import * as hrefs from '../../navigation/hrefs';
|
||||
|
||||
const THEME_COLORS = {
|
||||
primary: '#FF6B35',
|
||||
primaryLight: '#FF8C5A',
|
||||
primaryDark: '#E55A2B',
|
||||
};
|
||||
|
||||
export const RegisterStep3Profile: React.FC<RegisterStepProps> = ({
|
||||
formData,
|
||||
updateFormData,
|
||||
@@ -208,7 +197,7 @@ export const RegisterStep3Profile: React.FC<RegisterStepProps> = ({
|
||||
<MaterialCommunityIcons
|
||||
name={agreedToTerms ? 'checkbox-marked' : 'checkbox-blank-outline'}
|
||||
size={22}
|
||||
color={agreedToTerms ? THEME_COLORS.primary : colors.text?.hint || '#999'}
|
||||
color={agreedToTerms ? colors.primary.main : colors.text?.hint || '#999'}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.termsText}>
|
||||
@@ -288,7 +277,7 @@ function createStyles(colors: AppColors) {
|
||||
inputWrapper: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5F5F7',
|
||||
backgroundColor: colors.background.default,
|
||||
borderRadius: 14,
|
||||
paddingHorizontal: 18,
|
||||
height: 52,
|
||||
@@ -331,13 +320,13 @@ function createStyles(colors: AppColors) {
|
||||
lineHeight: 22,
|
||||
},
|
||||
termsLink: {
|
||||
color: '#FF6B35',
|
||||
color: colors.primary.main,
|
||||
fontWeight: '500',
|
||||
},
|
||||
actionButton: {
|
||||
height: 56,
|
||||
borderRadius: 14,
|
||||
backgroundColor: '#FF6B35',
|
||||
backgroundColor: colors.primary.main,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
@@ -347,7 +336,7 @@ function createStyles(colors: AppColors) {
|
||||
actionButtonText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '600',
|
||||
color: '#FFFFFF',
|
||||
color: colors.text.inverse,
|
||||
},
|
||||
backButton: {
|
||||
flexDirection: 'row',
|
||||
|
||||
@@ -26,12 +26,6 @@ import { showPrompt } from '../../services/promptService';
|
||||
import { uploadService } from '../../services/uploadService';
|
||||
import type { UserIdentity } from '../../types/dto';
|
||||
|
||||
const THEME_COLORS = {
|
||||
primary: '#FF6B35',
|
||||
primaryLight: '#FF8C5A',
|
||||
primaryDark: '#E55A2B',
|
||||
};
|
||||
|
||||
const IDENTITY_CONFIG: Record<string, { title: string; idField: string; idLabel: string }> = {
|
||||
student: { title: '在校学生', idField: 'student_id', idLabel: '学号' },
|
||||
teacher: { title: '教职工', idField: 'employee_id', idLabel: '工号' },
|
||||
@@ -230,7 +224,7 @@ export const VerificationFormScreen: React.FC = () => {
|
||||
<MaterialCommunityIcons
|
||||
name="information-outline"
|
||||
size={20}
|
||||
color={THEME_COLORS.primary}
|
||||
color={colors.primary.main}
|
||||
/>
|
||||
<Text style={styles.infoText}>
|
||||
请填写真实信息,上传清晰的证明材料照片,审核通过后将获得认证标识
|
||||
@@ -303,7 +297,7 @@ export const VerificationFormScreen: React.FC = () => {
|
||||
disabled={isLoading || uploading}
|
||||
>
|
||||
{(isLoading || uploading) ? (
|
||||
<ActivityIndicator size="small" color="#FFF" />
|
||||
<ActivityIndicator size="small" color={colors.text.inverse} />
|
||||
) : (
|
||||
<Text style={styles.submitButtonText}>提交认证申请</Text>
|
||||
)}
|
||||
@@ -317,7 +311,7 @@ function createStyles(colors: AppColors) {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: colors.background.paper,
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
@@ -349,7 +343,7 @@ function createStyles(colors: AppColors) {
|
||||
infoCard: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
backgroundColor: '#FFF5F0',
|
||||
backgroundColor: colors.primary.main + '15',
|
||||
borderRadius: 12,
|
||||
padding: 16,
|
||||
marginBottom: 24,
|
||||
@@ -374,7 +368,7 @@ function createStyles(colors: AppColors) {
|
||||
marginBottom: 8,
|
||||
},
|
||||
required: {
|
||||
color: THEME_COLORS.primary,
|
||||
color: colors.primary.main,
|
||||
},
|
||||
hintText: {
|
||||
fontSize: 13,
|
||||
@@ -382,7 +376,7 @@ function createStyles(colors: AppColors) {
|
||||
marginBottom: 8,
|
||||
},
|
||||
inputWrapper: {
|
||||
backgroundColor: '#F5F5F7',
|
||||
backgroundColor: colors.background.default,
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 16,
|
||||
height: 50,
|
||||
@@ -404,7 +398,7 @@ function createStyles(colors: AppColors) {
|
||||
marginTop: 6,
|
||||
},
|
||||
uploadButton: {
|
||||
backgroundColor: '#F5F5F7',
|
||||
backgroundColor: colors.background.default,
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden',
|
||||
minHeight: 200,
|
||||
@@ -433,7 +427,7 @@ function createStyles(colors: AppColors) {
|
||||
submitButton: {
|
||||
height: 56,
|
||||
borderRadius: 14,
|
||||
backgroundColor: THEME_COLORS.primary,
|
||||
backgroundColor: colors.primary.main,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
@@ -443,7 +437,7 @@ function createStyles(colors: AppColors) {
|
||||
submitButtonText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '600',
|
||||
color: '#FFFFFF',
|
||||
color: colors.text.inverse,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
/**
|
||||
* 注册后身份认证引导页面
|
||||
* 可跳过的认证界面,引导用户完成身份认证
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
View,
|
||||
@@ -10,7 +5,6 @@ import {
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
ScrollView,
|
||||
Image,
|
||||
Alert,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
@@ -19,12 +13,6 @@ import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { useAppColors, type AppColors } from '../../theme';
|
||||
import * as hrefs from '../../navigation/hrefs';
|
||||
|
||||
const THEME_COLORS = {
|
||||
primary: '#FF6B35',
|
||||
primaryLight: '#FF8C5A',
|
||||
primaryDark: '#E55A2B',
|
||||
};
|
||||
|
||||
// 身份类型选项
|
||||
const IDENTITY_OPTIONS = [
|
||||
{
|
||||
@@ -98,7 +86,7 @@ export const VerificationGuideScreen: React.FC = () => {
|
||||
<MaterialCommunityIcons
|
||||
name={option.icon as any}
|
||||
size={32}
|
||||
color={isSelected ? THEME_COLORS.primary : colors.text?.secondary || '#666'}
|
||||
color={isSelected ? colors.primary.main : colors.text?.secondary || '#666'}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.identityContent}>
|
||||
@@ -112,7 +100,7 @@ export const VerificationGuideScreen: React.FC = () => {
|
||||
<MaterialCommunityIcons
|
||||
name="check-circle"
|
||||
size={24}
|
||||
color={THEME_COLORS.primary}
|
||||
color={colors.primary.main}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
@@ -132,7 +120,7 @@ export const VerificationGuideScreen: React.FC = () => {
|
||||
<MaterialCommunityIcons
|
||||
name="shield-check-outline"
|
||||
size={48}
|
||||
color={THEME_COLORS.primary}
|
||||
color={colors.primary.main}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.title}>身份认证</Text>
|
||||
@@ -147,7 +135,7 @@ export const VerificationGuideScreen: React.FC = () => {
|
||||
<MaterialCommunityIcons
|
||||
name="check-decagram"
|
||||
size={20}
|
||||
color={THEME_COLORS.primary}
|
||||
color={colors.primary.main}
|
||||
/>
|
||||
<Text style={styles.benefitText}>获得认证标识</Text>
|
||||
</View>
|
||||
@@ -155,7 +143,7 @@ export const VerificationGuideScreen: React.FC = () => {
|
||||
<MaterialCommunityIcons
|
||||
name="lock-open-outline"
|
||||
size={20}
|
||||
color={THEME_COLORS.primary}
|
||||
color={colors.primary.main}
|
||||
/>
|
||||
<Text style={styles.benefitText}>解锁专属频道</Text>
|
||||
</View>
|
||||
@@ -163,7 +151,7 @@ export const VerificationGuideScreen: React.FC = () => {
|
||||
<MaterialCommunityIcons
|
||||
name="account-group-outline"
|
||||
size={20}
|
||||
color={THEME_COLORS.primary}
|
||||
color={colors.primary.main}
|
||||
/>
|
||||
<Text style={styles.benefitText}>加入社群圈子</Text>
|
||||
</View>
|
||||
@@ -198,7 +186,7 @@ function createStyles(colors: AppColors) {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: colors.background.paper,
|
||||
},
|
||||
scrollContent: {
|
||||
flexGrow: 1,
|
||||
@@ -214,7 +202,7 @@ function createStyles(colors: AppColors) {
|
||||
width: 100,
|
||||
height: 100,
|
||||
borderRadius: 50,
|
||||
backgroundColor: '#FFF5F0',
|
||||
backgroundColor: colors.primary.main + '15',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginBottom: 20,
|
||||
@@ -258,15 +246,15 @@ function createStyles(colors: AppColors) {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: 16,
|
||||
backgroundColor: '#F5F5F7',
|
||||
backgroundColor: colors.background.default,
|
||||
borderRadius: 14,
|
||||
marginBottom: 12,
|
||||
borderWidth: 2,
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
identityCardSelected: {
|
||||
backgroundColor: '#FFF5F0',
|
||||
borderColor: THEME_COLORS.primary,
|
||||
backgroundColor: colors.primary.main + '15',
|
||||
borderColor: colors.primary.main,
|
||||
},
|
||||
identityIconContainer: {
|
||||
width: 48,
|
||||
@@ -285,7 +273,7 @@ function createStyles(colors: AppColors) {
|
||||
marginBottom: 4,
|
||||
},
|
||||
identityTitleSelected: {
|
||||
color: THEME_COLORS.primary,
|
||||
color: colors.primary.main,
|
||||
},
|
||||
identityDescription: {
|
||||
fontSize: 13,
|
||||
@@ -303,17 +291,17 @@ function createStyles(colors: AppColors) {
|
||||
continueButton: {
|
||||
height: 56,
|
||||
borderRadius: 14,
|
||||
backgroundColor: THEME_COLORS.primary,
|
||||
backgroundColor: colors.primary.main,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
continueButtonDisabled: {
|
||||
backgroundColor: '#ccc',
|
||||
backgroundColor: colors.background.disabled,
|
||||
},
|
||||
continueButtonText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '600',
|
||||
color: '#fff',
|
||||
color: colors.text.inverse,
|
||||
},
|
||||
skipButton: {
|
||||
height: 56,
|
||||
|
||||
Reference in New Issue
Block a user