feat(Theme): enhance theming and UI consistency across components
- 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:
@@ -3,7 +3,7 @@
|
||||
* 嵌入式聊天组件 - 用于桌面端双栏布局右侧
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
||||
import {
|
||||
View,
|
||||
FlatList,
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
import { useRouter } from 'expo-router';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { Image as ExpoImage } from 'expo-image';
|
||||
import { colors, spacing, fontSizes, shadows } from '../../../theme';
|
||||
import { spacing, fontSizes, shadows, useAppColors, type AppColors } from '../../../theme';
|
||||
import { ConversationResponse, MessageResponse, MessageSegment, GroupMemberResponse, ImageSegmentData } from '../../../types/dto';
|
||||
import { Avatar, Text, ImageGallery, AppBackButton } from '../../../components/common';
|
||||
import { useAuthStore, messageManager, MessageEvent, MessageSubscriber } from '../../../stores';
|
||||
@@ -34,12 +34,210 @@ import { LongPressMenu, MenuPosition, GroupMessage } from './ChatScreen';
|
||||
const { width: screenWidth } = Dimensions.get('window');
|
||||
const PANEL_WIDTH = 360;
|
||||
|
||||
function createEmbeddedChatStyles(colors: AppColors) {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.chat.screen,
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
backgroundColor: colors.background.paper,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: colors.divider,
|
||||
shadowColor: 'transparent',
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowOpacity: 0,
|
||||
shadowRadius: 0,
|
||||
elevation: 0,
|
||||
height: 56,
|
||||
},
|
||||
headerButton: {
|
||||
padding: spacing.xs,
|
||||
width: 40,
|
||||
height: 40,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
headerCenter: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: fontSizes.lg,
|
||||
fontWeight: '600',
|
||||
color: colors.text.primary,
|
||||
marginLeft: spacing.sm,
|
||||
maxWidth: 200,
|
||||
},
|
||||
messageList: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.chat.screen,
|
||||
},
|
||||
centerContainer: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: spacing.xl,
|
||||
},
|
||||
emptyText: {
|
||||
marginTop: spacing.md,
|
||||
fontSize: 16,
|
||||
color: colors.text.secondary,
|
||||
},
|
||||
emptySubtext: {
|
||||
marginTop: spacing.xs,
|
||||
fontSize: 14,
|
||||
color: colors.text.disabled,
|
||||
},
|
||||
listContent: {
|
||||
padding: spacing.md,
|
||||
paddingBottom: spacing.xl,
|
||||
},
|
||||
messageRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-end',
|
||||
marginBottom: spacing.md,
|
||||
maxWidth: screenWidth * 0.7,
|
||||
},
|
||||
messageRowLeft: {
|
||||
alignSelf: 'flex-start',
|
||||
},
|
||||
messageRowRight: {
|
||||
alignSelf: 'flex-end',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
messageBubble: {
|
||||
maxWidth: screenWidth * 0.5,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
borderRadius: 18,
|
||||
marginHorizontal: spacing.sm,
|
||||
},
|
||||
messageBubbleMe: {
|
||||
backgroundColor: colors.chat.replyTint,
|
||||
borderBottomRightRadius: 4,
|
||||
},
|
||||
messageBubbleOther: {
|
||||
backgroundColor: colors.chat.bubbleIncoming,
|
||||
borderBottomLeftRadius: 4,
|
||||
shadowColor: colors.chat.shadow,
|
||||
shadowOffset: { width: 0, height: 0.5 },
|
||||
shadowOpacity: 0.04,
|
||||
shadowRadius: 1,
|
||||
elevation: 1,
|
||||
},
|
||||
senderName: {
|
||||
fontSize: 12,
|
||||
color: colors.text.secondary,
|
||||
marginBottom: 2,
|
||||
},
|
||||
messageText: {
|
||||
fontSize: 15,
|
||||
lineHeight: 20,
|
||||
},
|
||||
messageTextMe: {
|
||||
color: colors.chat.textPrimary,
|
||||
},
|
||||
messageTextOther: {
|
||||
color: colors.chat.textPrimary,
|
||||
},
|
||||
inputArea: {
|
||||
backgroundColor: colors.chat.screen,
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
borderTopColor: colors.divider,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
paddingBottom: Platform.OS === 'ios' ? spacing.md : spacing.sm,
|
||||
},
|
||||
inputRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
iconButton: {
|
||||
padding: spacing.xs,
|
||||
width: 44,
|
||||
height: 44,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
inputContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.chat.surfaceInput,
|
||||
borderRadius: 20,
|
||||
paddingHorizontal: spacing.md,
|
||||
minHeight: 40,
|
||||
justifyContent: 'center',
|
||||
shadowColor: 'transparent',
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowOpacity: 0,
|
||||
shadowRadius: 0,
|
||||
elevation: 0,
|
||||
},
|
||||
textInput: {
|
||||
fontSize: 15,
|
||||
color: colors.text.primary,
|
||||
padding: 0,
|
||||
maxHeight: 100,
|
||||
},
|
||||
sendButton: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 20,
|
||||
backgroundColor: colors.primary.main,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginLeft: spacing.xs,
|
||||
},
|
||||
sendButtonDisabled: {
|
||||
backgroundColor: colors.background.disabled,
|
||||
},
|
||||
messageTextRecalled: {
|
||||
fontStyle: 'italic',
|
||||
color: colors.text.secondary,
|
||||
},
|
||||
imageGrid: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
marginTop: 4,
|
||||
},
|
||||
messageImage: {
|
||||
borderRadius: 8,
|
||||
marginRight: 4,
|
||||
marginBottom: 4,
|
||||
},
|
||||
imagePlaceholder: {
|
||||
width: 120,
|
||||
height: 120,
|
||||
borderRadius: 8,
|
||||
backgroundColor: colors.background.disabled,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginRight: 4,
|
||||
marginBottom: 4,
|
||||
},
|
||||
imagePlaceholderText: {
|
||||
color: colors.text.secondary,
|
||||
fontSize: 12,
|
||||
marginTop: 4,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
interface EmbeddedChatProps {
|
||||
conversation: ConversationResponse;
|
||||
onBack: () => void;
|
||||
}
|
||||
|
||||
export const EmbeddedChat: React.FC<EmbeddedChatProps> = ({ conversation, onBack }) => {
|
||||
const colors = useAppColors();
|
||||
const styles = useMemo(() => createEmbeddedChatStyles(colors), [colors]);
|
||||
const router = useRouter();
|
||||
const currentUser = useAuthStore(state => state.currentUser);
|
||||
|
||||
@@ -411,7 +609,7 @@ export const EmbeddedChat: React.FC<EmbeddedChatProps> = ({ conversation, onBack
|
||||
<View style={styles.header}>
|
||||
{/* 大屏幕(>= 768px)时隐藏返回按钮 */}
|
||||
{!isWideScreen ? (
|
||||
<AppBackButton onPress={onBack} style={styles.headerButton} iconColor="#666" />
|
||||
<AppBackButton onPress={onBack} style={styles.headerButton} iconColor={colors.text.secondary} />
|
||||
) : (
|
||||
<View style={styles.headerButton} />
|
||||
)}
|
||||
@@ -426,11 +624,11 @@ export const EmbeddedChat: React.FC<EmbeddedChatProps> = ({ conversation, onBack
|
||||
{/* 大屏幕 + 群聊时显示群信息按钮,否则显示放大按钮 */}
|
||||
{isWideScreen && isGroupChat ? (
|
||||
<TouchableOpacity onPress={toggleGroupInfoPanel} style={styles.headerButton}>
|
||||
<MaterialCommunityIcons name="dots-horizontal" size={22} color="#666" />
|
||||
<MaterialCommunityIcons name="dots-horizontal" size={22} color={colors.text.secondary} />
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
<TouchableOpacity onPress={handleNavigateToFullChat} style={styles.headerButton}>
|
||||
<MaterialCommunityIcons name="arrow-expand" size={22} color="#666" />
|
||||
<MaterialCommunityIcons name="arrow-expand" size={22} color={colors.text.secondary} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
@@ -443,7 +641,7 @@ export const EmbeddedChat: React.FC<EmbeddedChatProps> = ({ conversation, onBack
|
||||
</View>
|
||||
) : messages.length === 0 ? (
|
||||
<View style={styles.centerContainer}>
|
||||
<MaterialCommunityIcons name="message-text-outline" size={48} color="#DDD" />
|
||||
<MaterialCommunityIcons name="message-text-outline" size={48} color={colors.text.disabled} />
|
||||
<Text style={styles.emptyText}>暂无消息</Text>
|
||||
<Text style={styles.emptySubtext}>发送第一条消息开始聊天</Text>
|
||||
</View>
|
||||
@@ -472,7 +670,7 @@ export const EmbeddedChat: React.FC<EmbeddedChatProps> = ({ conversation, onBack
|
||||
<View style={styles.inputArea}>
|
||||
<View style={styles.inputRow}>
|
||||
<TouchableOpacity style={styles.iconButton}>
|
||||
<MaterialCommunityIcons name="plus-circle-outline" size={28} color="#666" />
|
||||
<MaterialCommunityIcons name="plus-circle-outline" size={28} color={colors.text.secondary} />
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={styles.inputContainer}>
|
||||
@@ -480,7 +678,7 @@ export const EmbeddedChat: React.FC<EmbeddedChatProps> = ({ conversation, onBack
|
||||
ref={inputRef}
|
||||
style={styles.textInput}
|
||||
placeholder="发送消息..."
|
||||
placeholderTextColor="#999"
|
||||
placeholderTextColor={colors.text.hint}
|
||||
value={inputText}
|
||||
onChangeText={setInputText}
|
||||
multiline={false}
|
||||
@@ -494,7 +692,7 @@ export const EmbeddedChat: React.FC<EmbeddedChatProps> = ({ conversation, onBack
|
||||
</View>
|
||||
|
||||
<TouchableOpacity style={styles.iconButton}>
|
||||
<MaterialCommunityIcons name="emoticon-outline" size={28} color="#666" />
|
||||
<MaterialCommunityIcons name="emoticon-outline" size={28} color={colors.text.secondary} />
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
@@ -503,9 +701,9 @@ export const EmbeddedChat: React.FC<EmbeddedChatProps> = ({ conversation, onBack
|
||||
disabled={!inputText.trim() || sending}
|
||||
>
|
||||
{sending ? (
|
||||
<ActivityIndicator size="small" color="#FFF" />
|
||||
<ActivityIndicator size="small" color={colors.text.inverse} />
|
||||
) : (
|
||||
<MaterialCommunityIcons name="send" size={20} color="#FFF" />
|
||||
<MaterialCommunityIcons name="send" size={20} color={colors.text.inverse} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
@@ -604,190 +802,3 @@ export const EmbeddedChat: React.FC<EmbeddedChatProps> = ({ conversation, onBack
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#F5F7FA',
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
backgroundColor: '#FFF',
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#E8E8E8',
|
||||
...shadows.sm,
|
||||
height: 56,
|
||||
},
|
||||
headerButton: {
|
||||
padding: spacing.xs,
|
||||
width: 40,
|
||||
height: 40,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
headerCenter: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: fontSizes.lg,
|
||||
fontWeight: '600',
|
||||
color: '#333',
|
||||
marginLeft: spacing.sm,
|
||||
maxWidth: 200,
|
||||
},
|
||||
messageList: {
|
||||
flex: 1,
|
||||
backgroundColor: '#F5F7FA',
|
||||
},
|
||||
centerContainer: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: spacing.xl,
|
||||
},
|
||||
emptyText: {
|
||||
marginTop: spacing.md,
|
||||
fontSize: 16,
|
||||
color: '#999',
|
||||
},
|
||||
emptySubtext: {
|
||||
marginTop: spacing.xs,
|
||||
fontSize: 14,
|
||||
color: '#BBB',
|
||||
},
|
||||
listContent: {
|
||||
padding: spacing.md,
|
||||
paddingBottom: spacing.xl,
|
||||
},
|
||||
messageRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-end',
|
||||
marginBottom: spacing.md,
|
||||
maxWidth: screenWidth * 0.7,
|
||||
},
|
||||
messageRowLeft: {
|
||||
alignSelf: 'flex-start',
|
||||
},
|
||||
messageRowRight: {
|
||||
alignSelf: 'flex-end',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
messageBubble: {
|
||||
maxWidth: screenWidth * 0.5,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
borderRadius: 18,
|
||||
marginHorizontal: spacing.sm,
|
||||
},
|
||||
messageBubbleMe: {
|
||||
backgroundColor: colors.primary.main,
|
||||
borderBottomRightRadius: 4,
|
||||
},
|
||||
messageBubbleOther: {
|
||||
backgroundColor: '#FFF',
|
||||
borderBottomLeftRadius: 4,
|
||||
...shadows.sm,
|
||||
},
|
||||
senderName: {
|
||||
fontSize: 12,
|
||||
color: '#999',
|
||||
marginBottom: 2,
|
||||
},
|
||||
messageText: {
|
||||
fontSize: 15,
|
||||
lineHeight: 20,
|
||||
},
|
||||
messageTextMe: {
|
||||
color: '#FFF',
|
||||
},
|
||||
messageTextOther: {
|
||||
color: '#333',
|
||||
},
|
||||
inputArea: {
|
||||
backgroundColor: '#FFF',
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: '#E8E8E8',
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
paddingBottom: Platform.OS === 'ios' ? spacing.md : spacing.sm,
|
||||
},
|
||||
inputRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
iconButton: {
|
||||
padding: spacing.xs,
|
||||
width: 44,
|
||||
height: 44,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
inputContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: '#F5F5F5',
|
||||
borderRadius: 20,
|
||||
paddingHorizontal: spacing.md,
|
||||
minHeight: 40,
|
||||
justifyContent: 'center',
|
||||
// 移除阴影
|
||||
shadowColor: 'transparent',
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowOpacity: 0,
|
||||
shadowRadius: 0,
|
||||
elevation: 0,
|
||||
},
|
||||
textInput: {
|
||||
fontSize: 15,
|
||||
color: '#333',
|
||||
padding: 0,
|
||||
maxHeight: 100,
|
||||
},
|
||||
sendButton: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 20,
|
||||
backgroundColor: colors.primary.main,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginLeft: spacing.xs,
|
||||
},
|
||||
sendButtonDisabled: {
|
||||
backgroundColor: '#E0E0E0',
|
||||
},
|
||||
messageTextRecalled: {
|
||||
fontStyle: 'italic',
|
||||
color: '#999',
|
||||
},
|
||||
imageGrid: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
marginTop: 4,
|
||||
},
|
||||
messageImage: {
|
||||
borderRadius: 8,
|
||||
marginRight: 4,
|
||||
marginBottom: 4,
|
||||
},
|
||||
imagePlaceholder: {
|
||||
width: 120,
|
||||
height: 120,
|
||||
borderRadius: 8,
|
||||
backgroundColor: 'rgba(0,0,0,0.1)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginRight: 4,
|
||||
marginBottom: 4,
|
||||
},
|
||||
imagePlaceholderText: {
|
||||
color: '#999',
|
||||
fontSize: 12,
|
||||
marginTop: 4,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user