feat(chat): implement dynamic theme support in chat components
All checks were successful
Frontend CI / build-and-push-web (push) Successful in 7m31s
Frontend CI / ota-android (push) Successful in 13m19s
Frontend CI / build-android-apk (push) Successful in 1h10m3s

- Enhanced ChatHeader and ChatInput components to support dynamic theme colors, improving visual consistency across different themes.
- Updated styles in ChatScreen to utilize new dynamic styles for better responsiveness and user experience.
- Refactored styles in various profile screens to adopt a unified background color scheme and improved spacing for a cleaner layout.

This update significantly enhances the chat interface and profile settings by allowing for dynamic theming and improved visual elements.
This commit is contained in:
lafay
2026-04-02 17:55:56 +08:00
parent 72842352d9
commit e8651215f7
9 changed files with 213 additions and 245 deletions

View File

@@ -1,6 +1,7 @@
/**
* 聊天头部组件
* 支持响应式布局(宽屏下显示更多信息)
* 支持动态主题颜色
*/
import React, { useMemo } from 'react';
@@ -8,7 +9,7 @@ import { View, TouchableOpacity, StyleSheet } from 'react-native';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { Avatar, Text } from '../../../../components/common';
import { useAppColors, spacing } from '../../../../theme';
import { useChatScreenStyles } from './styles';
import { useChatScreenStyles, useChatDynamicStyles } from './styles';
import { useBreakpointGTE } from '../../../../hooks/useResponsive';
import { ChatHeaderProps } from './types';
@@ -26,6 +27,7 @@ export const ChatHeader: React.FC<ChatHeaderProps> = ({
}) => {
const colors = useAppColors();
const baseStyles = useChatScreenStyles();
const dynamicStyles = useChatDynamicStyles();
// 响应式布局
// 使用 768px 作为大屏幕和小屏幕的分界线
@@ -33,6 +35,9 @@ export const ChatHeader: React.FC<ChatHeaderProps> = ({
// 优先使用 props 传入的 isWideScreen否则使用 hook
const isWideScreen = propIsWideScreen !== undefined ? propIsWideScreen : isWideScreenHook;
// 动态图标颜色 - 使用主题色
const iconColor = dynamicStyles.textPrimaryColor || colors.text.primary;
// 合并样式
const styles = useMemo(() => {
return StyleSheet.create({
@@ -74,7 +79,7 @@ export const ChatHeader: React.FC<ChatHeaderProps> = ({
<View style={styles.headerContainer}>
<View style={styles.header}>
<TouchableOpacity style={styles.backButton} onPress={onBack} activeOpacity={0.7}>
<MaterialCommunityIcons name="chevron-left" size={28} color={colors.text.primary} />
<MaterialCommunityIcons name="chevron-left" size={28} color={iconColor} />
</TouchableOpacity>
<View style={styles.headerCenter}>
@@ -130,7 +135,7 @@ export const ChatHeader: React.FC<ChatHeaderProps> = ({
onPress={onGroupInfoPress}
activeOpacity={0.7}
>
<MaterialCommunityIcons name="dots-horizontal" size={24} color={colors.text.primary} />
<MaterialCommunityIcons name="dots-horizontal" size={24} color={iconColor} />
</TouchableOpacity>
) : (
<TouchableOpacity
@@ -138,7 +143,7 @@ export const ChatHeader: React.FC<ChatHeaderProps> = ({
onPress={onMorePress}
activeOpacity={0.7}
>
<MaterialCommunityIcons name="dots-horizontal" size={24} color={colors.text.primary} />
<MaterialCommunityIcons name="dots-horizontal" size={24} color={iconColor} />
</TouchableOpacity>
)}
</View>

View File

@@ -1,6 +1,7 @@
/**
* 聊天输入框组件
* 支持响应式布局(宽屏下居中显示)
* 支持动态主题颜色
*/
import React, { useMemo } from 'react';
@@ -8,7 +9,7 @@ import { View, TouchableOpacity, TextInput, ActivityIndicator, ScrollView, Style
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { Text } from '../../../../components/common';
import { useAppColors } from '../../../../theme';
import { useChatScreenStyles } from './styles';
import { useChatScreenStyles, useChatDynamicStyles } from './styles';
import { useBreakpointGTE } from '../../../../hooks/useResponsive';
import { ChatInputProps, GroupMessage, SenderInfo } from './types';
import { extractTextFromSegments } from '../../../../types/dto';
@@ -41,11 +42,18 @@ export const ChatInput: React.FC<ChatInputProps & {
}) => {
const colors = useAppColors();
const styles = useChatScreenStyles();
const dynamicStyles = useChatDynamicStyles();
const isDisabled = isGroupChat && isMuted;
// 获取当前用户ID
const currentUserId = currentUser?.id || '';
// 动态颜色
const textPrimary = dynamicStyles.textPrimaryColor || colors.chat.textPrimary;
const textSecondary = dynamicStyles.textSecondaryColor || colors.chat.textSecondary;
const textMuted = '#999'; // 禁用状态颜色
const textDisabled = '#CCC'; // 完全禁用颜色
// 响应式布局
const isWideScreen = useBreakpointGTE('lg');
@@ -91,7 +99,7 @@ export const ChatInput: React.FC<ChatInputProps & {
</View>
</View>
<TouchableOpacity onPress={onCancel} style={styles.replyPreviewClose}>
<MaterialCommunityIcons name="close" size={18} color="#999" />
<MaterialCommunityIcons name="close" size={18} color={textSecondary} />
</TouchableOpacity>
</View>
);
@@ -157,7 +165,7 @@ export const ChatInput: React.FC<ChatInputProps & {
<MaterialCommunityIcons
name={activePanel === 'emoji' ? "keyboard" : "emoticon-happy-outline"}
size={26}
color={isDisabled ? '#CCC' : (activePanel === 'emoji' ? colors.primary.main : '#666')}
color={isDisabled ? textDisabled : (activePanel === 'emoji' ? colors.primary.main : textSecondary)}
/>
</TouchableOpacity>
@@ -165,7 +173,7 @@ export const ChatInput: React.FC<ChatInputProps & {
<TextInput
style={[styles.input, isDisabled && styles.inputMuted]}
placeholder={isGroupChat ? (isMuted ? (muteAll ? "全员禁言中..." : "你已被禁言...") : "发送消息,@提及成员...") : "发送消息..."}
placeholderTextColor={isDisabled ? '#CCC' : '#999'}
placeholderTextColor={isDisabled ? textDisabled : textMuted}
value={inputText}
onChangeText={onInputChange}
multiline

View File

@@ -25,25 +25,34 @@ export function useChatDynamicStyles() {
const fontSize = useChatSettingsStore((s) => s.fontSize);
const messageRadius = useChatSettingsStore((s) => s.messageRadius);
const themeIndex = useChatSettingsStore((s) => s.themeIndex);
const nightMode = useChatSettingsStore((s) => s.nightMode);
const theme = CHAT_THEMES[themeIndex];
const colors = useAppColors();
// 夜间模式优先:当开启夜间模式时,使用系统暗色,忽略主题设置
// 判断是否为暗色模式chat.nightMode 设置 或 系统暗色模式(背景色为暗色)
const isSystemDarkMode = (colors.chat.screen as string).startsWith('#0') ||
(colors.chat.screen as string) === '#0A0A0A' ||
(colors.chat.screen as string) === '#121212';
const isNightMode = nightMode || isSystemDarkMode;
return {
fontSize,
messageRadius,
// 气泡颜
outgoingBubbleColor: theme.bubble,
// 夜间模式时使用系统暗色,否则使用主题
outgoingBubbleColor: isNightMode ? colors.chat.bubbleOutgoing : theme.bubble,
// 背景色
chatBackgroundColor: theme.secondary,
chatBackgroundColor: isNightMode ? colors.chat.screen : theme.secondary,
// 卡片/头部背景色
cardBackgroundColor: theme.card,
cardBackgroundColor: isNightMode ? colors.chat.card : theme.card,
// 输入框背景色
inputBackgroundColor: theme.inputBg,
inputBackgroundColor: isNightMode ? colors.chat.surfaceRaised : theme.inputBg,
// 面板背景色
panelBackgroundColor: theme.panelBg,
panelBackgroundColor: isNightMode ? colors.chat.surfaceMuted : theme.panelBg,
// 主要文字颜色
textPrimaryColor: theme.textPrimary,
textPrimaryColor: isNightMode ? colors.chat.textPrimary : theme.textPrimary,
// 次要文字颜色
textSecondaryColor: theme.textSecondary,
textSecondaryColor: isNightMode ? colors.chat.textSecondary : theme.textSecondary,
};
}

View File

@@ -190,8 +190,7 @@ export const AccountSecurityScreen: React.FC = () => {
<>
<View style={styles.section}>
<View style={styles.sectionHeader}>
<MaterialCommunityIcons name="email-check-outline" size={18} color={colors.primary.main} />
<Text variant="caption" color={colors.text.secondary} style={styles.sectionTitle}>
<Text variant="caption" style={styles.sectionTitle}>
</Text>
</View>
@@ -206,7 +205,7 @@ export const AccountSecurityScreen: React.FC = () => {
</View>
<View style={styles.inputWrapper}>
<MaterialCommunityIcons name="email-outline" size={20} color={colors.primary.main} style={styles.inputIcon} />
<MaterialCommunityIcons name="email-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
<TextInput
style={styles.input}
placeholder="请输入邮箱"
@@ -220,7 +219,7 @@ export const AccountSecurityScreen: React.FC = () => {
<View style={styles.codeRow}>
<View style={[styles.inputWrapper, styles.codeInput]}>
<MaterialCommunityIcons name="shield-key-outline" size={20} color={colors.primary.main} style={styles.inputIcon} />
<MaterialCommunityIcons name="shield-key-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
<TextInput
style={styles.input}
placeholder="验证码"
@@ -260,14 +259,13 @@ export const AccountSecurityScreen: React.FC = () => {
<View style={styles.section}>
<View style={styles.sectionHeader}>
<MaterialCommunityIcons name="lock-reset" size={18} color={colors.primary.main} />
<Text variant="caption" color={colors.text.secondary} style={styles.sectionTitle}>
<Text variant="caption" style={styles.sectionTitle}>
</Text>
</View>
<View style={styles.card}>
<View style={styles.inputWrapper}>
<MaterialCommunityIcons name="lock-outline" size={20} color={colors.primary.main} style={styles.inputIcon} />
<MaterialCommunityIcons name="lock-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
<TextInput
style={styles.input}
placeholder="当前密码"
@@ -278,7 +276,7 @@ export const AccountSecurityScreen: React.FC = () => {
/>
</View>
<View style={styles.inputWrapper}>
<MaterialCommunityIcons name="lock-plus-outline" size={20} color={colors.primary.main} style={styles.inputIcon} />
<MaterialCommunityIcons name="lock-plus-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
<TextInput
style={styles.input}
placeholder="新密码至少6位"
@@ -290,7 +288,7 @@ export const AccountSecurityScreen: React.FC = () => {
</View>
<View style={styles.codeRow}>
<View style={[styles.inputWrapper, styles.codeInput]}>
<MaterialCommunityIcons name="shield-key-outline" size={20} color={colors.primary.main} style={styles.inputIcon} />
<MaterialCommunityIcons name="shield-key-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
<TextInput
style={styles.input}
placeholder="邮箱验证码"
@@ -316,7 +314,7 @@ export const AccountSecurityScreen: React.FC = () => {
</TouchableOpacity>
</View>
<View style={styles.inputWrapper}>
<MaterialCommunityIcons name="lock-check-outline" size={20} color={colors.primary.main} style={styles.inputIcon} />
<MaterialCommunityIcons name="lock-check-outline" size={20} color={colors.text.secondary} style={styles.inputIcon} />
<TextInput
style={styles.input}
placeholder="确认新密码"
@@ -358,29 +356,28 @@ function createAccountSecurityStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
backgroundColor: colors.background.paper,
},
scrollContent: {
paddingVertical: spacing.md,
paddingVertical: spacing.lg,
},
section: {
marginBottom: spacing.lg,
marginBottom: spacing['2xl'],
},
sectionHeader: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.lg,
paddingHorizontal: spacing['2xl'],
marginBottom: spacing.sm,
gap: spacing.xs,
marginTop: spacing.sm,
},
sectionTitle: {
fontWeight: '600',
fontSize: fontSizes.sm,
color: colors.text.secondary,
textTransform: 'uppercase',
letterSpacing: 0.5,
},
card: {
backgroundColor: colors.background.paper,
marginHorizontal: spacing.lg,
borderRadius: borderRadius.lg,
padding: spacing.md,
marginHorizontal: spacing['2xl'],
maxWidth: CONTENT_MAX_WIDTH,
alignSelf: 'center',
width: '100%',
@@ -412,11 +409,9 @@ function createAccountSecurityStyles(colors: AppColors) {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.background.default,
borderRadius: borderRadius.lg,
borderWidth: 1,
borderColor: colors.divider,
borderRadius: 14,
paddingHorizontal: spacing.md,
height: 48,
height: 50,
marginBottom: spacing.sm,
},
inputIcon: {
@@ -426,6 +421,7 @@ function createAccountSecurityStyles(colors: AppColors) {
flex: 1,
color: colors.text.primary,
fontSize: fontSizes.md,
height: 50,
},
codeRow: {
flexDirection: 'row',
@@ -438,9 +434,9 @@ function createAccountSecurityStyles(colors: AppColors) {
marginBottom: 0,
},
sendCodeButton: {
height: 48,
height: 50,
minWidth: 110,
borderRadius: borderRadius.lg,
borderRadius: 14,
backgroundColor: colors.primary.main,
alignItems: 'center',
justifyContent: 'center',
@@ -452,8 +448,8 @@ function createAccountSecurityStyles(colors: AppColors) {
fontWeight: '600',
},
primaryButton: {
height: 48,
borderRadius: borderRadius.lg,
height: 50,
borderRadius: 14,
backgroundColor: colors.primary.main,
alignItems: 'center',
justifyContent: 'center',
@@ -462,7 +458,7 @@ function createAccountSecurityStyles(colors: AppColors) {
primaryButtonText: {
color: colors.text.inverse,
fontSize: fontSizes.md,
fontWeight: '700',
fontWeight: '600',
},
buttonDisabled: {
opacity: 0.6,

View File

@@ -21,7 +21,7 @@ function createBlockedUsersStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
backgroundColor: colors.background.paper,
},
loadingWrap: {
flex: 1,
@@ -39,10 +39,7 @@ function createBlockedUsersStyles(colors: AppColors) {
item: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg,
padding: spacing.md,
...shadows.sm,
},
content: {
flex: 1,

View File

@@ -139,39 +139,36 @@ function createStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
backgroundColor: colors.background.paper,
},
scrollContent: {
paddingVertical: spacing.md,
paddingVertical: spacing.lg,
},
section: {
marginBottom: spacing.lg,
marginBottom: spacing['2xl'],
maxWidth: CARD_MAX_WIDTH,
alignSelf: 'center',
width: '100%',
paddingHorizontal: spacing.lg,
paddingHorizontal: spacing['2xl'],
},
sectionTitle: {
fontSize: fontSizes.sm,
fontWeight: '600',
color: colors.text.secondary,
marginBottom: spacing.sm,
marginLeft: spacing.xs,
},
card: {
backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg,
padding: spacing.md,
marginTop: spacing.sm,
textTransform: 'uppercase',
letterSpacing: 0.5,
},
// 聊天预览样式
previewContainer: {
backgroundColor: '#E8F5E9',
borderRadius: borderRadius.lg,
backgroundColor: colors.primary.light + '20',
borderRadius: 14,
padding: spacing.md,
minHeight: 140,
},
previewMessage: {
backgroundColor: '#FFFFFF',
backgroundColor: colors.background.paper,
borderRadius: borderRadius.md,
padding: spacing.sm,
marginBottom: spacing.sm,
@@ -186,11 +183,11 @@ function createStyles(colors: AppColors) {
},
previewText: {
fontSize: fontSizes.sm,
color: '#333',
color: colors.text.primary,
},
previewTime: {
fontSize: fontSizes.xs,
color: '#999',
color: colors.text.hint,
marginTop: 2,
alignSelf: 'flex-end',
},
@@ -243,13 +240,13 @@ function createStyles(colors: AppColors) {
marginTop: 4,
color: colors.text.secondary,
},
// 设置项样式
// 设置项样式 - 扁平化
settingItem: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: spacing.md,
borderBottomWidth: 1,
paddingVertical: 16,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.divider,
},
settingItemLast: {
@@ -258,15 +255,14 @@ function createStyles(colors: AppColors) {
settingItemLeft: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.md,
flex: 1,
},
iconContainer: {
width: 36,
height: 36,
borderRadius: borderRadius.md,
backgroundColor: colors.primary.light + '20',
width: 24,
height: 24,
justifyContent: 'center',
alignItems: 'center',
marginRight: spacing.lg,
},
settingTitle: {
fontSize: fontSizes.md,
@@ -277,6 +273,10 @@ function createStyles(colors: AppColors) {
color: colors.text.secondary,
marginTop: 2,
},
// 滑块容器 - 扁平化
sliderContainer: {
paddingVertical: 8,
},
// 开关样式
switch: {
width: 50,
@@ -292,7 +292,7 @@ function createStyles(colors: AppColors) {
width: 24,
height: 24,
borderRadius: 12,
backgroundColor: '#fff',
backgroundColor: colors.background.paper,
},
});
}
@@ -349,7 +349,7 @@ export const ChatSettingsScreen: React.FC = () => {
const renderFontSizeSlider = () => (
<View style={styles.section}>
<Text style={styles.sectionTitle}></Text>
<View style={styles.card}>
<View style={styles.sliderContainer}>
<Slider
value={fontSize}
min={12}
@@ -399,7 +399,7 @@ export const ChatSettingsScreen: React.FC = () => {
const renderRadiusSlider = () => (
<View style={styles.section}>
<Text style={styles.sectionTitle}></Text>
<View style={styles.card}>
<View style={styles.sliderContainer}>
<Slider
value={messageRadius}
min={0}
@@ -414,53 +414,49 @@ export const ChatSettingsScreen: React.FC = () => {
const renderOtherSettings = () => (
<View style={styles.section}>
<Text style={styles.sectionTitle}></Text>
<View style={styles.card}>
<TouchableOpacity style={styles.settingItem}>
<View style={styles.settingItemLeft}>
<View style={styles.iconContainer}>
<MaterialCommunityIcons name="image-outline" size={20} color={colors.primary.main} />
</View>
<View>
<Text style={styles.settingTitle}></Text>
</View>
<TouchableOpacity style={styles.settingItem}>
<View style={styles.settingItemLeft}>
<View style={styles.iconContainer}>
<MaterialCommunityIcons name="image-outline" size={22} color={colors.text.secondary} />
</View>
<MaterialCommunityIcons name="chevron-right" size={20} color={colors.text.hint} />
</TouchableOpacity>
<TouchableOpacity style={[styles.settingItem, styles.settingItemLast]}>
<View style={styles.settingItemLeft}>
<View style={styles.iconContainer}>
<MaterialCommunityIcons name="palette-outline" size={20} color={colors.primary.main} />
</View>
<View>
<Text style={styles.settingTitle}></Text>
<Text style={styles.settingSubtitle}>MiMQy</Text>
</View>
<View>
<Text style={styles.settingTitle}></Text>
</View>
<MaterialCommunityIcons name="chevron-right" size={20} color={colors.text.hint} />
</TouchableOpacity>
</View>
</View>
<MaterialCommunityIcons name="chevron-right" size={20} color={colors.text.hint} />
</TouchableOpacity>
<TouchableOpacity style={[styles.settingItem, styles.settingItemLast]}>
<View style={styles.settingItemLeft}>
<View style={styles.iconContainer}>
<MaterialCommunityIcons name="palette-outline" size={22} color={colors.text.secondary} />
</View>
<View>
<Text style={styles.settingTitle}></Text>
<Text style={styles.settingSubtitle}>MiMQy</Text>
</View>
</View>
<MaterialCommunityIcons name="chevron-right" size={20} color={colors.text.hint} />
</TouchableOpacity>
</View>
);
// 渲染夜间模式开关
const renderNightMode = () => (
<View style={styles.section}>
<View style={styles.card}>
<View style={[styles.settingItem, styles.settingItemLast]}>
<View style={styles.settingItemLeft}>
<View style={styles.iconContainer}>
<MaterialCommunityIcons name="weather-night" size={20} color={colors.primary.main} />
</View>
<Text style={styles.settingTitle}></Text>
<View style={[styles.settingItem, styles.settingItemLast]}>
<View style={styles.settingItemLeft}>
<View style={styles.iconContainer}>
<MaterialCommunityIcons name="weather-night" size={22} color={colors.text.secondary} />
</View>
<TouchableOpacity
style={[styles.switch, nightMode && styles.switchActive]}
onPress={() => toggleNightMode()}
>
<View style={[styles.switchThumb, { alignSelf: nightMode ? 'flex-end' : 'flex-start' }]} />
</TouchableOpacity>
<Text style={styles.settingTitle}></Text>
</View>
<TouchableOpacity
style={[styles.switch, nightMode && styles.switchActive]}
onPress={() => toggleNightMode()}
>
<View style={[styles.switchThumb, { alignSelf: nightMode ? 'flex-end' : 'flex-start' }]} />
</TouchableOpacity>
</View>
</View>
);
@@ -468,16 +464,14 @@ export const ChatSettingsScreen: React.FC = () => {
// 渲染浏览其他主题
const renderBrowseThemes = () => (
<View style={styles.section}>
<TouchableOpacity style={styles.card}>
<View style={[styles.settingItem, styles.settingItemLast]}>
<View style={styles.settingItemLeft}>
<View style={styles.iconContainer}>
<MaterialCommunityIcons name="apps" size={20} color={colors.primary.main} />
</View>
<Text style={styles.settingTitle}></Text>
<TouchableOpacity style={[styles.settingItem, styles.settingItemLast]}>
<View style={styles.settingItemLeft}>
<View style={styles.iconContainer}>
<MaterialCommunityIcons name="apps" size={22} color={colors.text.secondary} />
</View>
<MaterialCommunityIcons name="chevron-right" size={20} color={colors.text.hint} />
<Text style={styles.settingTitle}></Text>
</View>
<MaterialCommunityIcons name="chevron-right" size={20} color={colors.text.hint} />
</TouchableOpacity>
</View>
);

View File

@@ -42,7 +42,7 @@ function createEditProfileStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
backgroundColor: colors.background.paper,
},
keyboardView: {
flex: 1,
@@ -58,7 +58,7 @@ function createEditProfileStyles(colors: AppColors) {
coverContainer: {
position: 'relative',
overflow: 'hidden',
borderRadius: borderRadius.lg,
borderRadius: 14,
},
coverTouchable: {
width: '100%',
@@ -112,7 +112,6 @@ function createEditProfileStyles(colors: AppColors) {
marginTop: -50,
borderRadius: borderRadius.xl,
padding: spacing.lg,
...shadows.md,
},
avatarWrapper: {
alignItems: 'center',
@@ -204,15 +203,9 @@ function createEditProfileStyles(colors: AppColors) {
// ===== 表单区域 =====
formCard: {
backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg,
borderRadius: 14,
padding: spacing.lg,
marginBottom: spacing.lg,
shadowColor: colors.text.primary,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.05,
shadowRadius: 8,
elevation: 2,
maxWidth: CONTENT_MAX_WIDTH,
alignSelf: 'center',
width: '100%',
@@ -228,7 +221,7 @@ function createEditProfileStyles(colors: AppColors) {
width: 40,
height: 40,
borderRadius: borderRadius.md,
backgroundColor: colors.primary.light + '20',
backgroundColor: colors.background.default,
justifyContent: 'center',
alignItems: 'center',
marginRight: spacing.md,
@@ -267,14 +260,11 @@ function createEditProfileStyles(colors: AppColors) {
marginTop: spacing.sm,
},
saveButton: {
height: 50,
backgroundColor: colors.primary.main,
borderRadius: borderRadius.lg,
paddingVertical: spacing.md,
shadowColor: colors.primary.main,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 4,
borderRadius: 14,
alignItems: 'center',
justifyContent: 'center',
},
saveButtonDisabled: {
opacity: 0.6,

View File

@@ -13,7 +13,7 @@ import {
} from 'react-native';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { spacing, borderRadius, useAppColors, type AppColors } from '../../theme';
import { spacing, borderRadius, fontSizes, useAppColors, type AppColors } from '../../theme';
import { Text } from '../../components/common';
import {
loadNotificationPreferences,
@@ -122,43 +122,39 @@ export const NotificationSettingsScreen: React.FC = () => {
{/* 消息通知设置 */}
<View style={styles.section}>
<View style={styles.sectionHeader}>
<MaterialCommunityIcons name="message-text-outline" size={18} color={colors.primary.main} />
<Text variant="caption" color={colors.text.secondary} style={styles.sectionTitle}>
<Text variant="caption" style={styles.sectionTitle}>
</Text>
</View>
<View style={styles.card}>
{settings.map((item, index) => (
<View key={item.key}>
<View style={styles.settingItem}>
<View style={styles.iconContainer}>
<MaterialCommunityIcons
name={item.icon as any}
size={20}
color={colors.primary.main}
/>
</View>
<View style={styles.settingContent}>
<Text variant="body" color={colors.text.primary}>
{item.title}
</Text>
{item.subtitle && (
<Text variant="caption" color={colors.text.secondary} style={styles.subtitle}>
{item.subtitle}
</Text>
)}
</View>
<Switch
value={item.value}
onValueChange={item.onValueChange}
trackColor={{ false: colors.divider, true: colors.primary.main }}
thumbColor={colors.background.paper}
{settings.map((item, index) => (
<View key={item.key}>
<View style={styles.settingItem}>
<View style={styles.iconContainer}>
<MaterialCommunityIcons
name={item.icon as any}
size={22}
color={colors.text.secondary}
/>
</View>
{index < settings.length - 1 && <View style={styles.divider} />}
<View style={styles.settingContent}>
<Text variant="body" color={colors.text.primary}>
{item.title}
</Text>
{item.subtitle && (
<Text variant="caption" color={colors.text.secondary} style={styles.subtitle}>
{item.subtitle}
</Text>
)}
</View>
<Switch
value={item.value}
onValueChange={item.onValueChange}
trackColor={{ false: colors.divider, true: colors.primary.main }}
thumbColor={colors.background.paper}
/>
</View>
))}
</View>
</View>
))}
</View>
{/* 提示信息 */}
@@ -186,47 +182,40 @@ function createNotificationSettingsStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
backgroundColor: colors.background.paper,
},
scrollContent: {
paddingVertical: spacing.md,
paddingVertical: spacing.lg,
},
section: {
marginBottom: spacing.lg,
marginBottom: spacing['2xl'],
},
sectionHeader: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.lg,
paddingHorizontal: spacing['2xl'],
marginBottom: spacing.sm,
gap: spacing.xs,
marginTop: spacing.sm,
},
sectionTitle: {
fontWeight: '600',
},
card: {
backgroundColor: colors.background.paper,
marginHorizontal: spacing.lg,
borderRadius: borderRadius.lg,
overflow: 'hidden',
maxWidth: CONTENT_MAX_WIDTH,
alignSelf: 'center',
width: '100%',
fontSize: fontSizes.sm,
color: colors.text.secondary,
textTransform: 'uppercase',
letterSpacing: 0.5,
},
settingItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: spacing.md,
paddingHorizontal: spacing.md,
paddingVertical: 16,
paddingHorizontal: spacing['2xl'],
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.divider,
},
iconContainer: {
width: 36,
height: 36,
borderRadius: borderRadius.md,
backgroundColor: colors.primary.light + '20',
width: 24,
height: 24,
justifyContent: 'center',
alignItems: 'center',
marginRight: spacing.md,
marginRight: spacing.lg,
},
settingContent: {
flex: 1,
@@ -234,18 +223,14 @@ function createNotificationSettingsStyles(colors: AppColors) {
subtitle: {
marginTop: 2,
},
divider: {
height: 1,
backgroundColor: colors.divider,
marginLeft: 36 + spacing.md + spacing.md,
},
divider: {},
tipContainer: {
flexDirection: 'row',
alignItems: 'flex-start',
marginHorizontal: spacing.lg,
marginHorizontal: spacing['2xl'],
padding: spacing.md,
backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg,
backgroundColor: colors.background.default,
borderRadius: 14,
gap: spacing.sm,
},
tipText: {

View File

@@ -82,50 +82,40 @@ function createSettingsStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
backgroundColor: colors.background.paper,
},
scrollContent: {
paddingVertical: spacing.md,
paddingVertical: spacing.lg,
},
groupContainer: {
marginBottom: spacing.lg,
marginBottom: spacing['2xl'],
maxWidth: SETTINGS_CARD_MAX_WIDTH,
alignSelf: 'center',
width: '100%',
},
groupHeader: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.lg,
paddingHorizontal: spacing['2xl'],
marginBottom: spacing.sm,
gap: spacing.xs,
marginTop: spacing.sm,
},
groupTitle: {
fontWeight: '600',
fontSize: fontSizes.sm,
},
card: {
backgroundColor: colors.background.paper,
marginHorizontal: spacing.lg,
borderRadius: borderRadius.lg,
overflow: 'hidden',
color: colors.text.secondary,
textTransform: 'uppercase',
letterSpacing: 0.5,
},
settingItem: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: spacing.md,
paddingHorizontal: spacing.md,
borderBottomWidth: 1,
paddingVertical: 16,
paddingHorizontal: spacing['2xl'],
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.divider,
},
settingItemFirst: {
borderTopLeftRadius: borderRadius.lg,
borderTopRightRadius: borderRadius.lg,
},
settingItemFirst: {},
settingItemLast: {
borderBottomLeftRadius: borderRadius.lg,
borderBottomRightRadius: borderRadius.lg,
borderBottomWidth: 0,
},
settingItemLeft: {
@@ -134,17 +124,13 @@ function createSettingsStyles(colors: AppColors) {
flex: 1,
},
iconContainer: {
width: 36,
height: 36,
borderRadius: borderRadius.md,
backgroundColor: colors.primary.light + '20',
width: 24,
height: 24,
justifyContent: 'center',
alignItems: 'center',
marginRight: spacing.md,
},
dangerIconContainer: {
backgroundColor: colors.error.light + '20',
marginRight: spacing.lg,
},
dangerIconContainer: {},
settingContent: {
flex: 1,
},
@@ -155,19 +141,19 @@ function createSettingsStyles(colors: AppColors) {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: colors.background.paper,
marginHorizontal: spacing.lg,
marginTop: spacing.sm,
marginBottom: spacing.lg,
paddingVertical: spacing.md,
borderRadius: borderRadius.lg,
marginTop: spacing.lg,
marginBottom: spacing['2xl'],
paddingVertical: 16,
marginHorizontal: spacing['2xl'],
borderRadius: 14,
gap: spacing.sm,
maxWidth: SETTINGS_CARD_MAX_WIDTH,
alignSelf: 'center',
width: '100%',
backgroundColor: colors.error.light + '20',
},
logoutText: {
fontWeight: '500',
fontWeight: '600',
},
footer: {
alignItems: 'center',
@@ -318,7 +304,6 @@ export const SettingsScreen: React.FC = () => {
key={item.key}
style={[
styles.settingItem,
index === 0 && styles.settingItemFirst,
index === total - 1 && styles.settingItemLast,
]}
onPress={() => (item.onPress ? item.onPress() : handleItemPress(item.key))}
@@ -328,8 +313,8 @@ export const SettingsScreen: React.FC = () => {
<View style={[styles.iconContainer, item.danger && styles.dangerIconContainer]}>
<MaterialCommunityIcons
name={item.icon as any}
size={20}
color={item.danger ? colors.error.main : colors.primary.main}
size={22}
color={item.danger ? colors.error.main : colors.text.secondary}
/>
</View>
<View style={styles.settingContent}>
@@ -352,12 +337,11 @@ export const SettingsScreen: React.FC = () => {
const renderGroup = (group: (typeof settingsGroups)[0]) => (
<View key={group.title} style={styles.groupContainer}>
<View style={styles.groupHeader}>
<MaterialCommunityIcons name={group.icon as any} size={16} color={colors.primary.main} />
<Text variant="caption" color={colors.text.secondary} style={styles.groupTitle}>
<Text variant="caption" style={styles.groupTitle}>
{group.title}
</Text>
</View>
<View style={styles.card}>
<View>
{group.items.map((item, index) => renderSettingItem(item, index, group.items.length))}
</View>
</View>