feat(Theme): enhance theming and UI consistency across components
All checks were successful
Frontend CI / build-and-push-web (push) Successful in 8m15s
Frontend CI / ota-android (push) Successful in 10m56s
Frontend CI / build-android-apk (push) Successful in 1h3m22s

- Updated app.json to set userInterfaceStyle to automatic for improved theme adaptability.
- Refactored layout components to utilize useAppColors for dynamic theming, ensuring consistent color usage.
- Introduced SystemChrome component to manage system UI background color based on theme.
- Enhanced TabsLayout, ProfileStackLayout, and other components to leverage new theming structure.
- Improved QRCodeScanner, SearchBar, and CommentItem styles to align with the updated theme system.
- Consolidated styles in SystemMessageItem and TabBar for better maintainability and visual coherence.
This commit is contained in:
lafay
2026-03-25 05:16:54 +08:00
parent 90d834695f
commit 4ee3079b9f
86 changed files with 6777 additions and 5890 deletions

View File

@@ -30,7 +30,14 @@ import { useRouter } from 'expo-router';
import { useIsFocused } from '@react-navigation/native';
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { colors, spacing, fontSizes, shadows, borderRadius } from '../../theme';
import {
spacing,
fontSizes,
shadows,
borderRadius,
useAppColors,
type AppColors,
} from '../../theme';
import { ConversationResponse, UserDTO, MessageResponse, extractTextFromSegments } from '../../types/dto';
import { authService } from '../../services';
import { useUserStore, useAuthStore } from '../../stores';
@@ -73,6 +80,8 @@ interface SearchResultItem {
* 支持响应式双栏布局
*/
export const MessageListScreen: React.FC = () => {
const colors = useAppColors();
const styles = useMemo(() => createMessageListStyles(colors), [colors]);
const router = useRouter();
const isFocused = useIsFocused();
const insets = useSafeAreaInsets();
@@ -565,7 +574,7 @@ export const MessageListScreen: React.FC = () => {
))}
</View>
</View>
<MaterialCommunityIcons name="chevron-right" size={20} color="#CCC" />
<MaterialCommunityIcons name="chevron-right" size={20} color={colors.chat.iconSoft} />
</TouchableOpacity>
);
}
@@ -614,7 +623,7 @@ export const MessageListScreen: React.FC = () => {
</View>
)}
{!user.is_following && (
<MaterialCommunityIcons name="chevron-right" size={20} color="#CCC" />
<MaterialCommunityIcons name="chevron-right" size={20} color={colors.chat.iconSoft} />
)}
</TouchableOpacity>
);
@@ -661,11 +670,11 @@ export const MessageListScreen: React.FC = () => {
const renderSearchMode = () => (
<View style={styles.searchModeContainer}>
<View style={styles.searchInputContainer}>
<AppBackButton onPress={handleCloseSearch} iconColor="#666" />
<AppBackButton onPress={handleCloseSearch} iconColor={colors.text.secondary} />
<TextInput
style={styles.searchInput}
placeholder={activeSearchTab === 'chat' ? '搜索聊天记录' : '搜索用户'}
placeholderTextColor="#999"
placeholderTextColor={colors.text.hint}
value={searchText}
onChangeText={setSearchText}
autoFocus
@@ -676,7 +685,7 @@ export const MessageListScreen: React.FC = () => {
/>
{searchText.length > 0 && (
<TouchableOpacity onPress={() => setSearchText('')}>
<MaterialCommunityIcons name="close-circle" size={18} color="#999" />
<MaterialCommunityIcons name="close-circle" size={18} color={colors.text.hint} />
</TouchableOpacity>
)}
</View>
@@ -733,7 +742,7 @@ export const MessageListScreen: React.FC = () => {
<View style={styles.headerRightContainer}>
<TouchableOpacity style={styles.headerRightBtn} onPress={handleOpenActionMenu}>
<MaterialCommunityIcons name="plus" size={24} color="#666" />
<MaterialCommunityIcons name="plus" size={24} color={colors.text.secondary} />
</TouchableOpacity>
</View>
</View>
@@ -744,7 +753,7 @@ export const MessageListScreen: React.FC = () => {
activeOpacity={0.7}
>
<View style={styles.searchBox}>
<MaterialCommunityIcons name="magnify" size={18} color="#999" />
<MaterialCommunityIcons name="magnify" size={18} color={colors.text.hint} />
<Text style={styles.searchPlaceholder}></Text>
</View>
</TouchableOpacity>
@@ -793,19 +802,19 @@ export const MessageListScreen: React.FC = () => {
<Pressable style={styles.actionMenuBackdrop} onPress={() => setActionMenuVisible(false)}>
<View style={styles.actionMenu}>
<TouchableOpacity style={styles.actionMenuItem} onPress={() => runMenuAction('scanQRCode')}>
<MaterialCommunityIcons name="qrcode-scan" size={21} color="#444" />
<MaterialCommunityIcons name="qrcode-scan" size={21} color={colors.text.primary} />
<Text style={styles.actionMenuText}></Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionMenuItem} onPress={() => runMenuAction('join')}>
<MaterialCommunityIcons name="account-plus-outline" size={21} color="#444" />
<MaterialCommunityIcons name="account-plus-outline" size={21} color={colors.text.primary} />
<Text style={styles.actionMenuText}></Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionMenuItem} onPress={() => runMenuAction('create')}>
<MaterialCommunityIcons name="account-multiple-plus" size={21} color="#444" />
<MaterialCommunityIcons name="account-multiple-plus" size={21} color={colors.text.primary} />
<Text style={styles.actionMenuText}></Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionMenuItem} onPress={() => runMenuAction('readAll')} disabled={isMarking}>
<MaterialCommunityIcons name="message-check-outline" size={21} color="#444" />
<MaterialCommunityIcons name="message-check-outline" size={21} color={colors.text.primary} />
<Text style={styles.actionMenuText}>{isMarking ? '处理中...' : '全部已读'}</Text>
</TouchableOpacity>
</View>
@@ -838,7 +847,7 @@ export const MessageListScreen: React.FC = () => {
// 默认占位符
<View style={styles.chatPlaceholder}>
<View style={styles.chatPlaceholderContent}>
<MaterialCommunityIcons name="message-text-outline" size={64} color="#DDD" />
<MaterialCommunityIcons name="message-text-outline" size={64} color={colors.chat.iconMuted} />
<Text style={styles.chatPlaceholderText}></Text>
</View>
</View>
@@ -870,288 +879,290 @@ export const MessageListScreen: React.FC = () => {
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#FAFAFA',
},
// 桌面端双栏布局
desktopContainer: {
flex: 1,
flexDirection: 'row',
},
sidebar: {
backgroundColor: '#FAFAFA',
borderRightWidth: 1,
borderRightColor: '#E8E8E8',
},
chatArea: {
flex: 1,
backgroundColor: '#F5F7FA',
},
chatPlaceholder: {
flex: 1,
backgroundColor: '#F5F7FA',
alignItems: 'center',
justifyContent: 'center',
},
chatPlaceholderContent: {
alignItems: 'center',
justifyContent: 'center',
},
chatPlaceholderText: {
marginTop: spacing.md,
fontSize: 16,
color: '#999',
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: spacing.md,
paddingVertical: spacing.md,
backgroundColor: '#FAFAFA',
...shadows.sm,
},
headerWide: {
paddingHorizontal: spacing.lg,
paddingVertical: spacing.lg,
},
headerTitleWide: {
fontSize: 22,
},
searchContainerWide: {
paddingHorizontal: spacing.lg,
},
listContentWide: {
paddingHorizontal: spacing.lg,
},
headerLeft: {
width: 44,
},
headerCenter: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.xs,
},
headerTitle: {
fontSize: 19,
fontWeight: '700',
color: '#333',
},
totalBadge: {
minWidth: 18,
height: 18,
borderRadius: 9,
backgroundColor: '#FF4757',
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 5,
},
totalBadgeText: {
color: '#FFF',
fontSize: 11,
fontWeight: '600',
},
headerRight: {
width: 44,
height: 44,
alignItems: 'center',
justifyContent: 'center',
},
headerRightContainer: {
flexDirection: 'row',
alignItems: 'center',
},
headerRightBtn: {
width: 36,
height: 44,
alignItems: 'center',
justifyContent: 'center',
},
actionMenuBackdrop: {
flex: 1,
backgroundColor: 'rgba(0,0,0,0.08)',
},
actionMenu: {
position: 'absolute',
top: 88,
right: spacing.md,
width: 188,
backgroundColor: '#FFF',
borderRadius: 12,
paddingVertical: spacing.sm,
...shadows.md,
},
actionMenuItem: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.md,
paddingVertical: spacing.md,
},
actionMenuText: {
marginLeft: spacing.sm,
fontSize: 16,
color: '#333',
},
searchContainer: {
paddingHorizontal: spacing.md,
paddingBottom: spacing.sm,
backgroundColor: '#FAFAFA',
},
searchBox: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#F0F0F0',
borderRadius: 10,
paddingHorizontal: spacing.sm,
paddingVertical: 10,
},
searchPlaceholder: {
fontSize: 14,
color: '#999',
marginLeft: spacing.xs,
},
listContent: {
flexGrow: 1,
backgroundColor: '#FAFAFA',
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingVertical: spacing.xl * 2,
},
loadingMoreContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: spacing.md,
},
loadingMoreText: {
marginLeft: spacing.sm,
fontSize: 14,
color: '#999',
},
searchModeContainer: {
flex: 1,
backgroundColor: '#FAFAFA',
},
searchInputContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#F0F0F0',
borderRadius: 10,
paddingHorizontal: spacing.md,
marginHorizontal: spacing.md,
marginTop: spacing.md,
height: 40,
},
searchInput: {
flex: 1,
fontSize: 15,
color: '#333',
marginLeft: spacing.md,
},
searchTabs: {
flexDirection: 'row',
paddingHorizontal: spacing.md,
paddingVertical: spacing.sm,
gap: spacing.sm,
},
searchTab: {
flex: 1,
paddingVertical: spacing.sm,
alignItems: 'center',
backgroundColor: '#F0F0F0',
borderRadius: 8,
},
searchTabActive: {
backgroundColor: colors.primary.main,
},
searchTabText: {
fontSize: 14,
color: '#666',
fontWeight: '500',
},
searchTabTextActive: {
color: '#FFF',
},
searchResultsContent: {
flexGrow: 1,
paddingHorizontal: spacing.md,
},
searchResultItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: spacing.md,
backgroundColor: '#FFF',
borderRadius: 10,
marginBottom: spacing.sm,
paddingHorizontal: spacing.md,
},
searchResultContent: {
flex: 1,
marginLeft: spacing.md,
},
searchResultHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 2,
},
searchResultName: {
fontSize: 15,
fontWeight: '600',
color: '#333',
},
searchResultTime: {
fontSize: 12,
color: '#999',
},
searchResultMessage: {
fontSize: 13,
color: '#888',
},
searchingText: {
marginTop: spacing.sm,
fontSize: 14,
color: '#999',
},
followingBadge: {
width: 24,
height: 24,
borderRadius: 12,
backgroundColor: colors.primary.light + '30',
alignItems: 'center',
justifyContent: 'center',
},
highlightText: {
color: colors.primary.main,
fontWeight: '700',
backgroundColor: colors.primary.light + '30',
},
matchedMessagesContainer: {
marginTop: spacing.sm,
},
matchedMessageItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-start',
paddingVertical: spacing.xs,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#F0F0F0',
},
matchedMessageText: {
flex: 1,
fontSize: 13,
color: '#555',
lineHeight: 18,
},
matchedMessageTime: {
fontSize: 11,
color: '#AAA',
marginLeft: spacing.sm,
minWidth: 45,
},
});
function createMessageListStyles(colors: AppColors) {
return StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background.default,
},
// 桌面端双栏布局
desktopContainer: {
flex: 1,
flexDirection: 'row',
},
sidebar: {
backgroundColor: colors.background.default,
borderRightWidth: 1,
borderRightColor: colors.chat.border,
},
chatArea: {
flex: 1,
backgroundColor: colors.chat.surfaceMuted,
},
chatPlaceholder: {
flex: 1,
backgroundColor: colors.chat.surfaceMuted,
alignItems: 'center',
justifyContent: 'center',
},
chatPlaceholderContent: {
alignItems: 'center',
justifyContent: 'center',
},
chatPlaceholderText: {
marginTop: spacing.md,
fontSize: 16,
color: colors.text.hint,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: spacing.md,
paddingVertical: spacing.md,
backgroundColor: colors.background.default,
...shadows.sm,
},
headerWide: {
paddingHorizontal: spacing.lg,
paddingVertical: spacing.lg,
},
headerTitleWide: {
fontSize: 22,
},
searchContainerWide: {
paddingHorizontal: spacing.lg,
},
listContentWide: {
paddingHorizontal: spacing.lg,
},
headerLeft: {
width: 44,
},
headerCenter: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.xs,
},
headerTitle: {
fontSize: 19,
fontWeight: '700',
color: colors.text.primary,
},
totalBadge: {
minWidth: 18,
height: 18,
borderRadius: 9,
backgroundColor: colors.error.main,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 5,
},
totalBadgeText: {
color: colors.primary.contrast,
fontSize: 11,
fontWeight: '600',
},
headerRight: {
width: 44,
height: 44,
alignItems: 'center',
justifyContent: 'center',
},
headerRightContainer: {
flexDirection: 'row',
alignItems: 'center',
},
headerRightBtn: {
width: 36,
height: 44,
alignItems: 'center',
justifyContent: 'center',
},
actionMenuBackdrop: {
flex: 1,
backgroundColor: 'rgba(0,0,0,0.08)',
},
actionMenu: {
position: 'absolute',
top: 88,
right: spacing.md,
width: 188,
backgroundColor: colors.background.paper,
borderRadius: 12,
paddingVertical: spacing.sm,
...shadows.md,
},
actionMenuItem: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.md,
paddingVertical: spacing.md,
},
actionMenuText: {
marginLeft: spacing.sm,
fontSize: 16,
color: colors.text.primary,
},
searchContainer: {
paddingHorizontal: spacing.md,
paddingBottom: spacing.sm,
backgroundColor: colors.background.default,
},
searchBox: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.chat.surfaceInput,
borderRadius: 10,
paddingHorizontal: spacing.sm,
paddingVertical: 10,
},
searchPlaceholder: {
fontSize: 14,
color: colors.text.hint,
marginLeft: spacing.xs,
},
listContent: {
flexGrow: 1,
backgroundColor: colors.background.default,
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingVertical: spacing.xl * 2,
},
loadingMoreContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: spacing.md,
},
loadingMoreText: {
marginLeft: spacing.sm,
fontSize: 14,
color: colors.text.hint,
},
searchModeContainer: {
flex: 1,
backgroundColor: colors.background.default,
},
searchInputContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.chat.surfaceInput,
borderRadius: 10,
paddingHorizontal: spacing.md,
marginHorizontal: spacing.md,
marginTop: spacing.md,
height: 40,
},
searchInput: {
flex: 1,
fontSize: 15,
color: colors.text.primary,
marginLeft: spacing.md,
},
searchTabs: {
flexDirection: 'row',
paddingHorizontal: spacing.md,
paddingVertical: spacing.sm,
gap: spacing.sm,
},
searchTab: {
flex: 1,
paddingVertical: spacing.sm,
alignItems: 'center',
backgroundColor: colors.chat.surfaceInput,
borderRadius: 8,
},
searchTabActive: {
backgroundColor: colors.primary.main,
},
searchTabText: {
fontSize: 14,
color: colors.text.secondary,
fontWeight: '500',
},
searchTabTextActive: {
color: colors.primary.contrast,
},
searchResultsContent: {
flexGrow: 1,
paddingHorizontal: spacing.md,
},
searchResultItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: spacing.md,
backgroundColor: colors.background.paper,
borderRadius: 10,
marginBottom: spacing.sm,
paddingHorizontal: spacing.md,
},
searchResultContent: {
flex: 1,
marginLeft: spacing.md,
},
searchResultHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 2,
},
searchResultName: {
fontSize: 15,
fontWeight: '600',
color: colors.text.primary,
},
searchResultTime: {
fontSize: 12,
color: colors.text.hint,
},
searchResultMessage: {
fontSize: 13,
color: colors.text.secondary,
},
searchingText: {
marginTop: spacing.sm,
fontSize: 14,
color: colors.text.hint,
},
followingBadge: {
width: 24,
height: 24,
borderRadius: 12,
backgroundColor: colors.primary.light + '30',
alignItems: 'center',
justifyContent: 'center',
},
highlightText: {
color: colors.primary.main,
fontWeight: '700',
backgroundColor: colors.primary.light + '30',
},
matchedMessagesContainer: {
marginTop: spacing.sm,
},
matchedMessageItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-start',
paddingVertical: spacing.xs,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.divider,
},
matchedMessageText: {
flex: 1,
fontSize: 13,
color: colors.text.secondary,
lineHeight: 18,
},
matchedMessageTime: {
fontSize: 11,
color: colors.text.hint,
marginLeft: spacing.sm,
minWidth: 45,
},
});
}