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:
lafay
2026-04-11 04:27:22 +08:00
parent 9bbed8cf5e
commit 6f84e17772
22 changed files with 1271 additions and 394 deletions

View File

@@ -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',
},

View File

@@ -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',
},
});

View File

@@ -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,
},

View File

@@ -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,
},
});
}

View File

@@ -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',

View File

@@ -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',

View File

@@ -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,
},
});
}

View File

@@ -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,

View File

@@ -33,7 +33,7 @@ import { useNavigation, useRouter } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { Text, ImageGallery, ImageGridItem } from '../../components/common';
import { useAppColors } from '../../theme';
import { useAppColors, useResolvedColorScheme } from '../../theme';
import * as hrefs from '../../navigation/hrefs';
import { messageManager, callStore } from '../../stores';
import { useBreakpointGTE } from '../../hooks/useResponsive';
@@ -56,6 +56,7 @@ export const ChatScreen: React.FC = () => {
const router = useRouter();
const insets = useSafeAreaInsets();
const colors = useAppColors();
const resolved = useResolvedColorScheme();
const styles = useChatScreenStyles();
// 响应式布局
@@ -406,7 +407,7 @@ export const ChatScreen: React.FC = () => {
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 0}
>
<StatusBar style="dark" backgroundColor="#FFFFFF" />
<StatusBar style={resolved === 'dark' ? 'light' : 'dark'} backgroundColor={colors.background.paper} />
{/* 顶部栏 */}
<ChatHeader
@@ -499,7 +500,7 @@ export const ChatScreen: React.FC = () => {
>
<View
style={{
backgroundColor: 'rgba(255,255,255,0.9)',
backgroundColor: colors.background.paper + 'E6',
borderRadius: 12,
paddingHorizontal: 10,
paddingVertical: 6,

View File

@@ -28,7 +28,7 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import { useIsFocused } from '@react-navigation/native';
import { useRouter } from 'expo-router';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { spacing, borderRadius, useAppColors, type AppColors } from '../../theme';
import { spacing, borderRadius, useAppColors, useResolvedColorScheme, type AppColors } from '../../theme';
import { SystemMessageResponse } from '../../types/dto';
import { messageService } from '../../services/messageService';
import { commentService } from '../../services/commentService';
@@ -49,18 +49,12 @@ const MESSAGE_TYPES = [
{ key: 'announcement', title: '公告' },
];
// 胡萝卜橙主题色
const THEME_COLORS = {
primary: '#FF6B35',
primaryLight: '#FF8C5A',
primaryDark: '#E55A2B',
};
const LIKE_SYSTEM_TYPES = new Set(['like_post', 'like_comment', 'like_reply', 'favorite_post']);
const GROUP_SYSTEM_TYPES = new Set(['group_invite', 'group_join_apply', 'group_join_approved', 'group_join_rejected']);
export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack }) => {
const colors = useAppColors();
const resolved = useResolvedColorScheme();
const styles = useMemo(() => createNotificationsStyles(colors), [colors]);
const isFocused = useIsFocused();
const { setSystemUnreadCount, decrementSystemUnreadCount } = useMessageManagerSystemUnreadCount();
@@ -424,9 +418,9 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
if (!isHydrated) {
return (
<SafeAreaView style={styles.container} edges={['bottom']}>
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
<StatusBar barStyle={resolved === 'dark' ? 'light-content' : 'dark-content'} backgroundColor={colors.background.paper} />
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={THEME_COLORS.primary} />
<ActivityIndicator size="large" color={colors.primary.main} />
</View>
</SafeAreaView>
);
@@ -434,7 +428,7 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
return (
<SafeAreaView style={styles.container} edges={['bottom']}>
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
<StatusBar barStyle={resolved === 'dark' ? 'light-content' : 'dark-content'} backgroundColor={colors.background.paper} />
<Animated.View
style={[
styles.content,
@@ -499,7 +493,7 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
{/* 消息列表 */}
{isLoading && messages.length === 0 ? (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={THEME_COLORS.primary} />
<ActivityIndicator size="large" color={colors.primary.main} />
</View>
) : (
<FlatList
@@ -519,8 +513,8 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
colors={[THEME_COLORS.primary]}
tintColor={THEME_COLORS.primary}
colors={[colors.primary.main]}
tintColor={colors.primary.main}
/>
}
/>
@@ -580,7 +574,7 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
{/* 消息列表 */}
{isLoading && messages.length === 0 ? (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={THEME_COLORS.primary} />
<ActivityIndicator size="large" color={colors.primary.main} />
</View>
) : (
<FlatList
@@ -600,8 +594,8 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
colors={[THEME_COLORS.primary]}
tintColor={THEME_COLORS.primary}
colors={[colors.primary.main]}
tintColor={colors.primary.main}
/>
}
/>
@@ -617,7 +611,7 @@ function createNotificationsStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
backgroundColor: colors.background.paper,
},
content: {
flex: 1,
@@ -629,7 +623,7 @@ function createNotificationsStyles(colors: AppColors) {
justifyContent: 'space-between',
paddingHorizontal: 20,
paddingVertical: 16,
backgroundColor: '#fff',
backgroundColor: colors.background.paper,
},
headerWide: {
paddingHorizontal: 32,
@@ -650,7 +644,7 @@ function createNotificationsStyles(colors: AppColors) {
fontSize: 20,
},
unreadBadge: {
backgroundColor: THEME_COLORS.primary,
backgroundColor: colors.primary.main,
borderRadius: 10,
paddingHorizontal: 6,
paddingVertical: 2,
@@ -660,7 +654,7 @@ function createNotificationsStyles(colors: AppColors) {
justifyContent: 'center',
},
unreadBadgeText: {
color: '#fff',
color: colors.text.inverse,
fontSize: 11,
fontWeight: '600',
},
@@ -674,7 +668,7 @@ function createNotificationsStyles(colors: AppColors) {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: '#F5F5F7',
backgroundColor: colors.background.default,
justifyContent: 'center',
alignItems: 'center',
},
@@ -686,7 +680,7 @@ function createNotificationsStyles(colors: AppColors) {
flexDirection: 'row',
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: '#fff',
backgroundColor: colors.background.paper,
borderBottomWidth: 1,
borderBottomColor: colors.divider || '#E5E5EA',
},
@@ -702,7 +696,7 @@ function createNotificationsStyles(colors: AppColors) {
paddingVertical: 8,
marginRight: 8,
borderRadius: 14,
backgroundColor: '#F5F5F7',
backgroundColor: colors.background.default,
borderWidth: 1,
borderColor: 'transparent',
},
@@ -712,15 +706,15 @@ function createNotificationsStyles(colors: AppColors) {
marginRight: 10,
},
filterTagActive: {
backgroundColor: THEME_COLORS.primary,
borderColor: THEME_COLORS.primary,
backgroundColor: colors.primary.main,
borderColor: colors.primary.main,
},
filterTagTextActive: {
fontWeight: '600',
},
filterCount: {
marginLeft: 6,
backgroundColor: THEME_COLORS.primaryLight + '40',
backgroundColor: colors.primary.light + '40',
paddingHorizontal: 6,
paddingVertical: 1,
borderRadius: 8,
@@ -770,7 +764,7 @@ function createNotificationsStyles(colors: AppColors) {
width: 120,
height: 120,
borderRadius: 60,
backgroundColor: '#F5F5F7',
backgroundColor: colors.background.default,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 24,

View File

@@ -21,13 +21,6 @@ import * as hrefs from '../../navigation/hrefs';
const CONTENT_MAX_WIDTH = 720;
// 胡萝卜橙主题色
const THEME_COLORS = {
primary: '#FF6B35',
primaryLight: '#FF8C5A',
primaryDark: '#E55A2B',
};
function createAccountDeletionStyles(colors: AppColors) {
return StyleSheet.create({
container: {
@@ -114,7 +107,7 @@ function createAccountDeletionStyles(colors: AppColors) {
},
// 输入框 - 扁平化风格
input: {
backgroundColor: '#F5F5F7',
backgroundColor: colors.background.default,
borderRadius: 14,
padding: spacing.md,
fontSize: 16,
@@ -133,12 +126,12 @@ function createAccountDeletionStyles(colors: AppColors) {
flex: 1,
height: 56,
borderRadius: 14,
backgroundColor: THEME_COLORS.primary,
backgroundColor: colors.primary.main,
alignItems: 'center',
justifyContent: 'center',
},
primaryButtonText: {
color: '#fff',
color: colors.text.inverse,
fontSize: fontSizes.md,
fontWeight: '600',
},
@@ -166,7 +159,7 @@ function createAccountDeletionStyles(colors: AppColors) {
justifyContent: 'center',
},
dangerButtonText: {
color: '#fff',
color: colors.text.inverse,
fontSize: fontSizes.md,
fontWeight: '600',
},
@@ -392,9 +385,9 @@ export const AccountDeletionScreen: React.FC = () => {
onPress={handleRequestDeletion}
disabled={submitting || !password.trim()}
>
{submitting ? (
<ActivityIndicator size="small" color="#fff" />
) : (
{submitting ? (
<ActivityIndicator size="small" color={colors.text.inverse} />
) : (
<Text style={styles.dangerButtonText}></Text>
)}
</TouchableOpacity>

View File

@@ -16,13 +16,6 @@ import { useResponsive, useResponsiveSpacing } from '../../hooks';
// 内容最大宽度
const CONTENT_MAX_WIDTH = 720;
// 胡萝卜橙主题色
const THEME_COLORS = {
primary: '#FF6B35',
primaryLight: '#FF8C5A',
primaryDark: '#E55A2B',
};
import { authService, resolveAuthApiError } from '../../services/authService';
import { showPrompt } from '../../services/promptService';
import { useAuthStore } from '../../stores';
@@ -263,9 +256,9 @@ export const AccountSecurityScreen: React.FC = () => {
onPress={handleSendCode}
disabled={sendingCode || countdown > 0}
>
{sendingCode ? (
<ActivityIndicator size="small" color="#fff" />
) : (
{sendingCode ? (
<ActivityIndicator size="small" color={colors.text.inverse} />
) : (
<Text style={styles.sendCodeButtonText}>{countdown > 0 ? `${countdown}s` : '发送验证码'}</Text>
)}
</TouchableOpacity>
@@ -278,7 +271,7 @@ export const AccountSecurityScreen: React.FC = () => {
disabled={verifyingEmail}
>
{verifyingEmail ? (
<ActivityIndicator size="small" color="#fff" />
<ActivityIndicator size="small" color={colors.text.inverse} />
) : (
<Text style={styles.primaryButtonText}></Text>
)}
@@ -352,7 +345,7 @@ export const AccountSecurityScreen: React.FC = () => {
disabled={sendingChangePwdCode || changePwdCountdown > 0}
>
{sendingChangePwdCode ? (
<ActivityIndicator size="small" color="#fff" />
<ActivityIndicator size="small" color={colors.text.inverse} />
) : (
<Text style={styles.sendCodeButtonText}>
{changePwdCountdown > 0 ? `${changePwdCountdown}s` : '发送验证码'}
@@ -368,7 +361,7 @@ export const AccountSecurityScreen: React.FC = () => {
disabled={updatingPassword}
>
{updatingPassword ? (
<ActivityIndicator size="small" color="#fff" />
<ActivityIndicator size="small" color={colors.text.inverse} />
) : (
<Text style={styles.primaryButtonText}></Text>
)}
@@ -432,26 +425,26 @@ function createAccountSecurityStyles(colors: AppColors) {
borderRadius: borderRadius.sm,
},
statusVerified: {
backgroundColor: '#E8F5E9',
backgroundColor: colors.success.light + '30',
},
statusUnverified: {
backgroundColor: '#FFF3E0',
backgroundColor: colors.warning.light + '30',
},
statusText: {
fontSize: 13,
fontWeight: '600',
},
statusVerifiedText: {
color: '#2E7D32',
color: colors.success.dark,
},
statusUnverifiedText: {
color: '#E65100',
color: colors.warning.dark,
},
// 输入框样式 - 扁平化风格,与登录页一致
inputWrapper: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#F5F5F7',
backgroundColor: colors.background.default,
borderRadius: 14,
paddingHorizontal: spacing.lg,
height: 56,
@@ -485,13 +478,13 @@ function createAccountSecurityStyles(colors: AppColors) {
height: 56,
minWidth: 110,
borderRadius: 14,
backgroundColor: THEME_COLORS.primary,
backgroundColor: colors.primary.main,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: spacing.sm,
},
sendCodeButtonText: {
color: '#fff',
color: colors.text.inverse,
fontSize: fontSizes.sm,
fontWeight: '600',
},
@@ -499,14 +492,14 @@ function createAccountSecurityStyles(colors: AppColors) {
primaryButton: {
height: 56,
borderRadius: 14,
backgroundColor: THEME_COLORS.primary,
backgroundColor: colors.primary.main,
alignItems: 'center',
justifyContent: 'center',
marginTop: spacing.xs,
marginHorizontal: spacing['2xl'],
},
primaryButtonText: {
color: '#fff',
color: colors.text.inverse,
fontSize: fontSizes.md,
fontWeight: '600',
},

View File

@@ -10,6 +10,8 @@ import {
StyleSheet,
Switch,
ScrollView,
Alert,
TouchableOpacity,
} from 'react-native';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { MaterialCommunityIcons } from '@expo/vector-icons';
@@ -22,6 +24,7 @@ import {
setVibrationPreference,
} from '../../services/notificationPreferences';
import { useResponsive, useResponsiveSpacing } from '../../hooks';
import { backgroundSyncManager, BackgroundSyncMode } from '../../services/BackgroundSyncManager';
// 内容最大宽度
const CONTENT_MAX_WIDTH = 720;
@@ -41,6 +44,7 @@ export const NotificationSettingsScreen: React.FC = () => {
const [vibrationEnabled, setVibrationEnabledState] = useState(true);
const [pushEnabled, setPushEnabled] = useState(true);
const [soundEnabled, setSoundEnabled] = useState(true);
const [syncMode, setSyncMode] = useState<BackgroundSyncMode>(BackgroundSyncMode.BATTERY_SAVER);
const { isWideScreen, isMobile } = useResponsive();
const insets = useSafeAreaInsets();
@@ -55,6 +59,7 @@ export const NotificationSettingsScreen: React.FC = () => {
setVibrationEnabledState(prefs.vibrationEnabled);
setPushEnabled(prefs.pushEnabled);
setSoundEnabled(prefs.soundEnabled);
setSyncMode(backgroundSyncManager.getMode());
} catch (error) {
console.error('加载通知设置失败:', error);
}
@@ -89,6 +94,51 @@ export const NotificationSettingsScreen: React.FC = () => {
}
};
const handleSyncModeChange = async (mode: BackgroundSyncMode) => {
if (mode === BackgroundSyncMode.REALTIME) {
Alert.alert(
'实时模式',
'实时模式会在通知栏显示常驻通知以保持应用后台运行,确保消息即时同步。\n\n' +
'建议在系统设置中将应用加入电池优化白名单以获得最佳效果。\n\n' +
'注意:此模式可能增加电池消耗。',
[
{ text: '取消', style: 'cancel' },
{
text: '开启',
onPress: async () => {
await backgroundSyncManager.setMode(mode);
setSyncMode(mode);
},
},
]
);
} else {
await backgroundSyncManager.setMode(mode);
setSyncMode(mode);
}
};
const syncModeOptions: { mode: BackgroundSyncMode; title: string; subtitle: string; icon: string }[] = [
{
mode: BackgroundSyncMode.BATTERY_SAVER,
title: '省电模式',
subtitle: '系统后台任务,每 15 分钟检查一次',
icon: 'leaf',
},
{
mode: BackgroundSyncMode.REALTIME,
title: '实时模式',
subtitle: '通知栏常驻保活,即时同步消息',
icon: 'lightning-bolt',
},
{
mode: BackgroundSyncMode.DISABLED,
title: '禁用',
subtitle: '仅在应用打开时接收消息',
icon: 'close-circle-outline',
},
];
const settings: NotificationSettingItem[] = [
{
key: 'push',
@@ -164,6 +214,62 @@ export const NotificationSettingsScreen: React.FC = () => {
</Text>
</View>
{/* 后台同步模式 */}
<View style={styles.section}>
<View style={styles.sectionHeader}>
<Text variant="caption" style={styles.sectionTitle}>
</Text>
</View>
{syncModeOptions.map((option) => (
<TouchableOpacity
key={option.mode}
style={[
styles.settingItem,
syncMode === option.mode && styles.settingItemActive,
]}
onPress={() => handleSyncModeChange(option.mode)}
activeOpacity={0.7}
>
<View style={styles.iconContainer}>
<MaterialCommunityIcons
name={option.icon as any}
size={22}
color={syncMode === option.mode ? colors.primary.main : colors.text.secondary}
/>
</View>
<View style={styles.settingContent}>
<Text
variant="body"
color={syncMode === option.mode ? colors.primary.main : colors.text.primary}
>
{option.title}
</Text>
<Text variant="caption" color={colors.text.secondary} style={styles.subtitle}>
{option.subtitle}
</Text>
</View>
{syncMode === option.mode && (
<MaterialCommunityIcons
name="check"
size={20}
color={colors.primary.main}
/>
)}
</TouchableOpacity>
))}
</View>
{/* 实时模式说明 */}
{syncMode === BackgroundSyncMode.REALTIME && (
<View style={styles.tipContainer}>
<MaterialCommunityIcons name="shield-check-outline" size={16} color={colors.text.hint} />
<Text variant="caption" color={colors.text.hint} style={styles.tipText}>
</Text>
</View>
)}
</>
);
@@ -210,6 +316,9 @@ function createNotificationSettingsStyles(colors: AppColors) {
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.divider,
},
settingItemActive: {
backgroundColor: colors.primary.main + '0D',
},
iconContainer: {
width: 24,
height: 24,

View File

@@ -18,13 +18,6 @@ import type { PrivacySettingsDTO, VisibilityLevel } from '../../types/dto';
const CONTENT_MAX_WIDTH = 720;
// 胡萝卜橙主题色
const THEME_COLORS = {
primary: '#FF6B35',
primaryLight: '#FF8C5A',
primaryDark: '#E55A2B',
};
const VISIBILITY_OPTIONS: { value: VisibilityLevel; label: string; description: string }[] = [
{ value: 'everyone', label: '所有人可见', description: '任何人都可查看' },
{ value: 'following', label: '仅关注的人可见', description: '只有你关注的人可查看' },
@@ -74,7 +67,7 @@ function createPrivacySettingsStyles(colors: AppColors) {
},
// 隐私设置项 - 扁平化卡片风格
item: {
backgroundColor: '#F5F5F7',
backgroundColor: colors.background.default,
borderRadius: 14,
padding: spacing.lg,
marginHorizontal: spacing['2xl'],
@@ -111,15 +104,15 @@ function createPrivacySettingsStyles(colors: AppColors) {
backgroundColor: colors.background.paper,
},
optionButtonActive: {
backgroundColor: THEME_COLORS.primary,
borderColor: THEME_COLORS.primary,
backgroundColor: colors.primary.main,
borderColor: colors.primary.main,
},
optionText: {
fontSize: 13,
color: colors.text.primary,
},
optionTextActive: {
color: '#fff',
color: colors.text.inverse,
fontWeight: '500',
},
});

View File

@@ -26,12 +26,6 @@ import {
import { showPrompt } from '../../services/promptService';
import * as hrefs from '../../navigation/hrefs';
const THEME_COLORS = {
primary: '#FF6B35',
primaryLight: '#FF8C5A',
primaryDark: '#E55A2B',
};
const STATUS_CONFIG = {
not_submitted: {
title: '未认证',
@@ -99,7 +93,7 @@ export const VerificationSettingsScreen: React.FC = () => {
return (
<SafeAreaView style={styles.container}>
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={THEME_COLORS.primary} />
<ActivityIndicator size="large" color={colors.primary.main} />
</View>
</SafeAreaView>
);
@@ -110,10 +104,11 @@ export const VerificationSettingsScreen: React.FC = () => {
const identityText = IDENTITY_TEXT[status?.identity || 'general'];
return (
<ScrollView
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
>
<SafeAreaView style={styles.container}>
<ScrollView
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
>
{/* 状态卡片 */}
<View style={styles.statusCard}>
<View style={[styles.statusIconContainer, { backgroundColor: statusConfig.color + '20' }]}>
@@ -128,7 +123,7 @@ export const VerificationSettingsScreen: React.FC = () => {
{status?.verification_status === 'approved' && (
<View style={styles.identityBadge}>
<MaterialCommunityIcons name="check-circle" size={16} color={THEME_COLORS.primary} />
<MaterialCommunityIcons name="check-circle" size={16} color={colors.primary.main} />
<Text style={styles.identityText}>{identityText}</Text>
</View>
)}
@@ -153,7 +148,7 @@ export const VerificationSettingsScreen: React.FC = () => {
<MaterialCommunityIcons
name="check-decagram"
size={24}
color={THEME_COLORS.primary}
color={colors.primary.main}
/>
</View>
<View style={styles.benefitContent}>
@@ -169,7 +164,7 @@ export const VerificationSettingsScreen: React.FC = () => {
<MaterialCommunityIcons
name="lock-open-outline"
size={24}
color={THEME_COLORS.primary}
color={colors.primary.main}
/>
</View>
<View style={styles.benefitContent}>
@@ -185,7 +180,7 @@ export const VerificationSettingsScreen: React.FC = () => {
<MaterialCommunityIcons
name="account-group-outline"
size={24}
color={THEME_COLORS.primary}
color={colors.primary.main}
/>
</View>
<View style={styles.benefitContent}>
@@ -208,7 +203,7 @@ export const VerificationSettingsScreen: React.FC = () => {
onPress={handleStartVerification}
activeOpacity={0.9}
>
<MaterialCommunityIcons name="shield-check-outline" size={20} color="#FFF" />
<MaterialCommunityIcons name="shield-check-outline" size={20} color={colors.primary.contrast} />
<Text style={styles.primaryButtonText}>
{status?.verification_status === 'rejected' ? '重新认证' : '开始认证'}
</Text>
@@ -221,7 +216,7 @@ export const VerificationSettingsScreen: React.FC = () => {
onPress={handleViewRecords}
activeOpacity={0.8}
>
<MaterialCommunityIcons name="file-document-outline" size={20} color={THEME_COLORS.primary} />
<MaterialCommunityIcons name="file-document-outline" size={20} color={colors.primary.main} />
<Text style={styles.secondaryButtonText}></Text>
</TouchableOpacity>
)}
@@ -262,6 +257,7 @@ export const VerificationSettingsScreen: React.FC = () => {
</Text>
</View>
</ScrollView>
</SafeAreaView>
);
};
@@ -269,7 +265,7 @@ function createStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
backgroundColor: colors.background.paper,
},
loadingContainer: {
flex: 1,
@@ -307,7 +303,7 @@ function createStyles(colors: AppColors) {
alignItems: 'center',
paddingVertical: 32,
paddingHorizontal: 24,
backgroundColor: '#F9F9F9',
backgroundColor: colors.background.default,
borderRadius: 16,
marginBottom: 24,
},
@@ -334,7 +330,7 @@ function createStyles(colors: AppColors) {
identityBadge: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#FFF5F0',
backgroundColor: colors.primary.main + '15',
borderRadius: 20,
paddingVertical: 8,
paddingHorizontal: 16,
@@ -343,25 +339,25 @@ function createStyles(colors: AppColors) {
identityText: {
fontSize: 14,
fontWeight: '600',
color: THEME_COLORS.primary,
color: colors.primary.main,
marginLeft: 6,
},
rejectReasonContainer: {
marginTop: 16,
padding: 12,
backgroundColor: '#FFF3F3',
backgroundColor: colors.error.light + '20',
borderRadius: 8,
width: '100%',
},
rejectReasonLabel: {
fontSize: 13,
fontWeight: '600',
color: '#F44336',
color: colors.error.main,
marginBottom: 4,
},
rejectReasonText: {
fontSize: 13,
color: '#D32F2F',
color: colors.error.dark,
lineHeight: 18,
},
benefitsSection: {
@@ -379,7 +375,7 @@ function createStyles(colors: AppColors) {
benefitItem: {
flexDirection: 'row',
alignItems: 'flex-start',
backgroundColor: '#F9F9F9',
backgroundColor: colors.background.default,
borderRadius: 12,
padding: 16,
},
@@ -387,7 +383,7 @@ function createStyles(colors: AppColors) {
width: 48,
height: 48,
borderRadius: 12,
backgroundColor: '#FFF5F0',
backgroundColor: colors.primary.main + '15',
justifyContent: 'center',
alignItems: 'center',
marginRight: 12,
@@ -415,12 +411,12 @@ function createStyles(colors: AppColors) {
justifyContent: 'center',
height: 56,
borderRadius: 14,
backgroundColor: THEME_COLORS.primary,
backgroundColor: colors.primary.main,
},
primaryButtonText: {
fontSize: 17,
fontWeight: '600',
color: '#FFF',
color: colors.text.inverse,
marginLeft: 8,
},
secondaryButton: {
@@ -429,18 +425,18 @@ function createStyles(colors: AppColors) {
justifyContent: 'center',
height: 56,
borderRadius: 14,
backgroundColor: '#FFF5F0',
backgroundColor: colors.primary.main + '15',
borderWidth: 1,
borderColor: THEME_COLORS.primary,
borderColor: colors.primary.main,
},
secondaryButtonText: {
fontSize: 17,
fontWeight: '600',
color: THEME_COLORS.primary,
color: colors.primary.main,
marginLeft: 8,
},
verifiedInfo: {
backgroundColor: '#F9F9F9',
backgroundColor: colors.background.default,
borderRadius: 12,
padding: 16,
},