refactor(database): migrate to new modular database layer and unify data access
- Remove legacy database.ts, LocalDataSource.ts, and MessageRepository.ts - Create new src/database/ module with messageRepository, userCacheRepository, conversationRepository, and groupCacheRepository - Update all consumers to import from @/database instead of services/database - Add web platform blur handling for modal components to fix focus issues - Flatten SystemMessageItem and NotificationsScreen styles for consistent design - Add draggable slider in ChatSettingsScreen and dynamic font size support - Introduce 9 new chat color themes - Add profile screens for about, terms, and privacy policy with navigation routes - Add policy links to login and registration screens - Fix post share URL format from /posts/ to /post/
This commit is contained in:
@@ -15,6 +15,14 @@ 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';
|
||||
@@ -186,167 +194,173 @@ export const AccountSecurityScreen: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const content = (
|
||||
<>
|
||||
<View style={styles.section}>
|
||||
<View style={styles.sectionHeader}>
|
||||
<Text variant="caption" style={styles.sectionTitle}>
|
||||
邮箱验证
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.card}>
|
||||
<View style={styles.statusRow}>
|
||||
<Text variant="body" color={colors.text.primary}>当前状态</Text>
|
||||
<View style={[styles.statusBadge, isEmailVerified ? styles.statusVerified : styles.statusUnverified]}>
|
||||
<Text variant="caption" color={isEmailVerified ? '#1B5E20' : '#B26A00'}>
|
||||
{emailStatusText}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.inputWrapper}>
|
||||
<MaterialCommunityIcons name="email-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="请输入邮箱"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={email}
|
||||
onChangeText={setEmail}
|
||||
autoCapitalize="none"
|
||||
keyboardType="email-address"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={styles.codeRow}>
|
||||
<View style={[styles.inputWrapper, styles.codeInput]}>
|
||||
<MaterialCommunityIcons name="shield-key-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="验证码"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={verificationCode}
|
||||
onChangeText={setVerificationCode}
|
||||
keyboardType="number-pad"
|
||||
maxLength={6}
|
||||
/>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={[styles.sendCodeButton, (sendingCode || countdown > 0) && styles.buttonDisabled]}
|
||||
onPress={handleSendCode}
|
||||
disabled={sendingCode || countdown > 0}
|
||||
>
|
||||
{sendingCode ? (
|
||||
<ActivityIndicator size="small" color={colors.text.inverse} />
|
||||
) : (
|
||||
<Text style={styles.sendCodeButtonText}>{countdown > 0 ? `${countdown}s` : '发送验证码'}</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.primaryButton, verifyingEmail && styles.buttonDisabled]}
|
||||
onPress={handleVerifyEmail}
|
||||
disabled={verifyingEmail}
|
||||
>
|
||||
{verifyingEmail ? (
|
||||
<ActivityIndicator size="small" color={colors.text.inverse} />
|
||||
) : (
|
||||
<Text style={styles.primaryButtonText}>验证邮箱</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.section}>
|
||||
<View style={styles.sectionHeader}>
|
||||
<Text variant="caption" style={styles.sectionTitle}>
|
||||
修改密码
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.card}>
|
||||
<View style={styles.inputWrapper}>
|
||||
<MaterialCommunityIcons name="lock-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="当前密码"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={oldPassword}
|
||||
onChangeText={setOldPassword}
|
||||
secureTextEntry
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.inputWrapper}>
|
||||
<MaterialCommunityIcons name="lock-plus-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="新密码(至少6位)"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={newPassword}
|
||||
onChangeText={setNewPassword}
|
||||
secureTextEntry
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.codeRow}>
|
||||
<View style={[styles.inputWrapper, styles.codeInput]}>
|
||||
<MaterialCommunityIcons name="shield-key-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="邮箱验证码"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={changePasswordCode}
|
||||
onChangeText={setChangePasswordCode}
|
||||
keyboardType="number-pad"
|
||||
maxLength={6}
|
||||
/>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={[styles.sendCodeButton, (sendingChangePwdCode || changePwdCountdown > 0) && styles.buttonDisabled]}
|
||||
onPress={handleSendChangePasswordCode}
|
||||
disabled={sendingChangePwdCode || changePwdCountdown > 0}
|
||||
>
|
||||
{sendingChangePwdCode ? (
|
||||
<ActivityIndicator size="small" color={colors.text.inverse} />
|
||||
) : (
|
||||
<Text style={styles.sendCodeButtonText}>
|
||||
{changePwdCountdown > 0 ? `${changePwdCountdown}s` : '发送验证码'}
|
||||
</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View style={styles.inputWrapper}>
|
||||
<MaterialCommunityIcons name="lock-check-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="确认新密码"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={confirmPassword}
|
||||
onChangeText={setConfirmPassword}
|
||||
secureTextEntry
|
||||
/>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.primaryButton, updatingPassword && styles.buttonDisabled]}
|
||||
onPress={handleChangePassword}
|
||||
disabled={updatingPassword}
|
||||
>
|
||||
{updatingPassword ? (
|
||||
<ActivityIndicator size="small" color={colors.text.inverse} />
|
||||
) : (
|
||||
<Text style={styles.primaryButtonText}>更新密码</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
|
||||
const responsivePadding = useResponsiveSpacing({ xs: 8, sm: 12, md: 16, lg: 24, xl: 32 });
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['bottom']}>
|
||||
<ScrollView contentContainerStyle={[styles.scrollContent, { paddingBottom: scrollBottomInset, paddingHorizontal: responsivePadding }]}>
|
||||
{content}
|
||||
<View style={styles.content}>
|
||||
{/* 邮箱验证分组 */}
|
||||
<View style={styles.groupHeader}>
|
||||
<Text variant="caption" style={styles.groupTitle}>
|
||||
邮箱验证
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.card}>
|
||||
{/* 状态显示 */}
|
||||
<View style={styles.statusRow}>
|
||||
<Text style={styles.statusLabel}>当前状态</Text>
|
||||
<View style={[styles.statusBadge, isEmailVerified ? styles.statusVerified : styles.statusUnverified]}>
|
||||
<Text style={[styles.statusText, isEmailVerified ? styles.statusVerifiedText : styles.statusUnverifiedText]}>
|
||||
{emailStatusText}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 邮箱输入框 */}
|
||||
<View style={styles.inputWrapper}>
|
||||
<MaterialCommunityIcons name="email-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="请输入邮箱"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={email}
|
||||
onChangeText={setEmail}
|
||||
autoCapitalize="none"
|
||||
keyboardType="email-address"
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* 验证码行 */}
|
||||
<View style={styles.codeRow}>
|
||||
<View style={[styles.inputWrapper, styles.codeInput]}>
|
||||
<MaterialCommunityIcons name="shield-key-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="验证码"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={verificationCode}
|
||||
onChangeText={setVerificationCode}
|
||||
keyboardType="number-pad"
|
||||
maxLength={6}
|
||||
/>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={[styles.sendCodeButton, (sendingCode || countdown > 0) && styles.buttonDisabled]}
|
||||
onPress={handleSendCode}
|
||||
disabled={sendingCode || countdown > 0}
|
||||
>
|
||||
{sendingCode ? (
|
||||
<ActivityIndicator size="small" color="#fff" />
|
||||
) : (
|
||||
<Text style={styles.sendCodeButtonText}>{countdown > 0 ? `${countdown}s` : '发送验证码'}</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* 验证按钮 */}
|
||||
<TouchableOpacity
|
||||
style={[styles.primaryButton, verifyingEmail && styles.buttonDisabled]}
|
||||
onPress={handleVerifyEmail}
|
||||
disabled={verifyingEmail}
|
||||
>
|
||||
{verifyingEmail ? (
|
||||
<ActivityIndicator size="small" color="#fff" />
|
||||
) : (
|
||||
<Text style={styles.primaryButtonText}>验证邮箱</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* 修改密码分组 */}
|
||||
<View style={styles.groupHeader}>
|
||||
<Text variant="caption" style={styles.groupTitle}>
|
||||
修改密码
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.card}>
|
||||
{/* 当前密码 */}
|
||||
<View style={styles.inputWrapper}>
|
||||
<MaterialCommunityIcons name="lock-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="当前密码"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={oldPassword}
|
||||
onChangeText={setOldPassword}
|
||||
secureTextEntry
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* 新密码 */}
|
||||
<View style={styles.inputWrapper}>
|
||||
<MaterialCommunityIcons name="lock-plus-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="新密码(至少6位)"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={newPassword}
|
||||
onChangeText={setNewPassword}
|
||||
secureTextEntry
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* 确认新密码 */}
|
||||
<View style={styles.inputWrapper}>
|
||||
<MaterialCommunityIcons name="lock-check-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="确认新密码"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={confirmPassword}
|
||||
onChangeText={setConfirmPassword}
|
||||
secureTextEntry
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* 验证码行 */}
|
||||
<View style={styles.codeRow}>
|
||||
<View style={[styles.inputWrapper, styles.codeInput]}>
|
||||
<MaterialCommunityIcons name="shield-key-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="邮箱验证码"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={changePasswordCode}
|
||||
onChangeText={setChangePasswordCode}
|
||||
keyboardType="number-pad"
|
||||
maxLength={6}
|
||||
/>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={[styles.sendCodeButton, (sendingChangePwdCode || changePwdCountdown > 0) && styles.buttonDisabled]}
|
||||
onPress={handleSendChangePasswordCode}
|
||||
disabled={sendingChangePwdCode || changePwdCountdown > 0}
|
||||
>
|
||||
{sendingChangePwdCode ? (
|
||||
<ActivityIndicator size="small" color="#fff" />
|
||||
) : (
|
||||
<Text style={styles.sendCodeButtonText}>
|
||||
{changePwdCountdown > 0 ? `${changePwdCountdown}s` : '发送验证码'}
|
||||
</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* 更新密码按钮 */}
|
||||
<TouchableOpacity
|
||||
style={[styles.primaryButton, updatingPassword && styles.buttonDisabled]}
|
||||
onPress={handleChangePassword}
|
||||
disabled={updatingPassword}
|
||||
>
|
||||
{updatingPassword ? (
|
||||
<ActivityIndicator size="small" color="#fff" />
|
||||
) : (
|
||||
<Text style={styles.primaryButtonText}>更新密码</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
@@ -356,37 +370,51 @@ function createAccountSecurityStyles(colors: AppColors) {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.background.paper,
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
scrollContent: {
|
||||
paddingVertical: spacing.lg,
|
||||
},
|
||||
section: {
|
||||
marginBottom: spacing['2xl'],
|
||||
content: {
|
||||
maxWidth: CONTENT_MAX_WIDTH,
|
||||
alignSelf: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
sectionHeader: {
|
||||
// 分组标题
|
||||
groupHeader: {
|
||||
paddingHorizontal: spacing['2xl'],
|
||||
marginBottom: spacing.sm,
|
||||
marginTop: spacing.sm,
|
||||
},
|
||||
sectionTitle: {
|
||||
groupTitle: {
|
||||
fontWeight: '600',
|
||||
fontSize: fontSizes.sm,
|
||||
color: colors.text.secondary,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
// 卡片样式 - 统一
|
||||
card: {
|
||||
backgroundColor: '#F5F5F7',
|
||||
borderRadius: 16,
|
||||
padding: 6,
|
||||
marginBottom: spacing['2xl'],
|
||||
marginHorizontal: spacing['2xl'],
|
||||
maxWidth: CONTENT_MAX_WIDTH,
|
||||
alignSelf: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
// 状态显示
|
||||
statusRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.md,
|
||||
marginBottom: spacing.md,
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: 12,
|
||||
},
|
||||
statusLabel: {
|
||||
fontSize: 15,
|
||||
color: colors.text.secondary,
|
||||
},
|
||||
statusBadge: {
|
||||
paddingHorizontal: spacing.sm,
|
||||
@@ -394,24 +422,29 @@ function createAccountSecurityStyles(colors: AppColors) {
|
||||
borderRadius: borderRadius.sm,
|
||||
},
|
||||
statusVerified: {
|
||||
backgroundColor: colors.success.light + '40',
|
||||
backgroundColor: '#E8F5E9',
|
||||
},
|
||||
statusUnverified: {
|
||||
backgroundColor: colors.warning.light + '40',
|
||||
backgroundColor: '#FFF3E0',
|
||||
},
|
||||
statusText: {
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
},
|
||||
statusVerifiedText: {
|
||||
color: colors.success.dark,
|
||||
color: '#2E7D32',
|
||||
},
|
||||
statusUnverifiedText: {
|
||||
color: colors.warning.dark,
|
||||
color: '#E65100',
|
||||
},
|
||||
// 输入框样式 - 统一
|
||||
inputWrapper: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: colors.background.default,
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: 14,
|
||||
paddingHorizontal: spacing.md,
|
||||
height: 50,
|
||||
height: 56,
|
||||
marginBottom: spacing.sm,
|
||||
},
|
||||
inputIcon: {
|
||||
@@ -421,8 +454,9 @@ function createAccountSecurityStyles(colors: AppColors) {
|
||||
flex: 1,
|
||||
color: colors.text.primary,
|
||||
fontSize: fontSizes.md,
|
||||
height: 50,
|
||||
height: 56,
|
||||
},
|
||||
// 验证码行
|
||||
codeRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
@@ -433,30 +467,32 @@ function createAccountSecurityStyles(colors: AppColors) {
|
||||
flex: 1,
|
||||
marginBottom: 0,
|
||||
},
|
||||
// 发送验证码按钮
|
||||
sendCodeButton: {
|
||||
height: 50,
|
||||
height: 56,
|
||||
minWidth: 110,
|
||||
borderRadius: 14,
|
||||
backgroundColor: colors.primary.main,
|
||||
backgroundColor: THEME_COLORS.primary,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: spacing.sm,
|
||||
},
|
||||
sendCodeButtonText: {
|
||||
color: colors.text.inverse,
|
||||
color: '#fff',
|
||||
fontSize: fontSizes.sm,
|
||||
fontWeight: '600',
|
||||
},
|
||||
// 主按钮样式 - 统一
|
||||
primaryButton: {
|
||||
height: 50,
|
||||
height: 56,
|
||||
borderRadius: 14,
|
||||
backgroundColor: colors.primary.main,
|
||||
backgroundColor: THEME_COLORS.primary,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginTop: spacing.xs,
|
||||
},
|
||||
primaryButtonText: {
|
||||
color: colors.text.inverse,
|
||||
color: '#fff',
|
||||
fontSize: fontSizes.md,
|
||||
fontWeight: '600',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user