feat(ui): improve navigation, component props, and chat logic
Some checks failed
Frontend CI / ota-android (push) Successful in 1m31s
Frontend CI / build-and-push-web (push) Failing after 3m56s
Frontend CI / ota-ios (push) Successful in 4m59s
Frontend CI / build-android-apk (push) Successful in 26m18s

- update `app.json` with splash screen configuration
- implement tab navigation redirection in `TabsLayout` for apps and profile tabs
- update `HighlightText` prop usage from `style` to `highlightStyle` in `PostCard` and `PostContentRenderer`
- fix chat message segment construction to avoid empty text segments when sending only images
- refine UI styling by removing unnecessary shadows and adjusting `UserScreen` header visibility
- improve `HomeScreen` scroll behavior by disabling animation on FlashList scroll-to-top
This commit is contained in:
2026-06-07 10:37:19 +08:00
parent b15e0c0b0b
commit afbbee337d
9 changed files with 41 additions and 18 deletions

View File

@@ -155,7 +155,14 @@
], ],
"./plugins/withHuaweiPush", "./plugins/withHuaweiPush",
"expo-callkit-telecom", "expo-callkit-telecom",
"expo-splash-screen", [
"expo-splash-screen",
{
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
}
],
"expo-status-bar" "expo-status-bar"
], ],
"extra": { "extra": {

View File

@@ -1,6 +1,6 @@
import { useMemo, useCallback } from 'react'; import { useMemo, useCallback } from 'react';
import { Platform, Pressable, useWindowDimensions } from 'react-native'; import { Platform, Pressable, useWindowDimensions } from 'react-native';
import { Tabs, usePathname } from 'expo-router'; import { Tabs, usePathname, useRouter } from 'expo-router';
import { MaterialCommunityIcons } from '@expo/vector-icons'; import { MaterialCommunityIcons } from '@expo/vector-icons';
import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useSafeAreaInsets } from 'react-native-safe-area-context';
import type { PressableProps } from 'react-native'; import type { PressableProps } from 'react-native';
@@ -28,6 +28,7 @@ export default function TabsLayout() {
const { width } = useWindowDimensions(); const { width } = useWindowDimensions();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const pathname = usePathname(); const pathname = usePathname();
const router = useRouter();
const unreadCount = useTotalUnreadCount(); const unreadCount = useTotalUnreadCount();
const scrollHideTabBar = useHomeTabBarVisibilityStore((s) => s.bottomTabBarHiddenByScroll); const scrollHideTabBar = useHomeTabBarVisibilityStore((s) => s.bottomTabBarHiddenByScroll);
const triggerHomeTabPress = useHomeTabPressStore((s) => s.triggerPress); const triggerHomeTabPress = useHomeTabPressStore((s) => s.triggerPress);
@@ -41,6 +42,18 @@ export default function TabsLayout() {
} }
}, [pathname, triggerHomeTabPress]); }, [pathname, triggerHomeTabPress]);
const handleAppsTabPress = useCallback(() => {
if (pathname.startsWith('/apps/')) {
router.replace('/apps');
}
}, [pathname, router]);
const handleProfileTabPress = useCallback(() => {
if (pathname.startsWith('/profile/')) {
router.replace('/profile');
}
}, [pathname, router]);
const tabBarStyle = useMemo(() => { const tabBarStyle = useMemo(() => {
if (hideTabBar) { if (hideTabBar) {
return { display: 'none' as const, height: 0, overflow: 'hidden' as const }; return { display: 'none' as const, height: 0, overflow: 'hidden' as const };
@@ -116,6 +129,9 @@ export default function TabsLayout() {
/> />
), ),
}} }}
listeners={{
tabPress: handleAppsTabPress,
}}
/> />
<Tabs.Screen <Tabs.Screen
name="messages" name="messages"
@@ -143,6 +159,9 @@ export default function TabsLayout() {
/> />
), ),
}} }}
listeners={{
tabPress: handleProfileTabPress,
}}
/> />
</Tabs> </Tabs>
); );

View File

@@ -662,7 +662,7 @@ const PostCardInner: React.FC<PostCardProps> = (normalizedProps) => {
) : ( ) : (
<View style={styles.gridNoImagePreview}> <View style={styles.gridNoImagePreview}>
<Text style={styles.gridNoImageText} numberOfLines={6}> <Text style={styles.gridNoImageText} numberOfLines={6}>
{highlightKeyword ? <HighlightText text={contentPreview} keyword={highlightKeyword} style={styles.highlight} /> : contentPreview} {highlightKeyword ? <HighlightText text={contentPreview} keyword={highlightKeyword} highlightStyle={styles.highlight} /> : contentPreview}
</Text> </Text>
</View> </View>
)} )}
@@ -676,7 +676,7 @@ const PostCardInner: React.FC<PostCardProps> = (normalizedProps) => {
{!!post.title && ( {!!post.title && (
<Text style={styles.gridTitleMain} numberOfLines={hasImage ? 2 : 3}> <Text style={styles.gridTitleMain} numberOfLines={hasImage ? 2 : 3}>
{highlightKeyword ? <HighlightText text={post.title} keyword={highlightKeyword} style={styles.highlight} /> : post.title} {highlightKeyword ? <HighlightText text={post.title} keyword={highlightKeyword} highlightStyle={styles.highlight} /> : post.title}
</Text> </Text>
)} )}
@@ -748,7 +748,7 @@ const PostCardInner: React.FC<PostCardProps> = (normalizedProps) => {
{!!post.title && ( {!!post.title && (
<Text style={styles.title} numberOfLines={isCompact ? 2 : undefined}> <Text style={styles.title} numberOfLines={isCompact ? 2 : undefined}>
{highlightKeyword ? <HighlightText text={post.title} keyword={highlightKeyword} style={styles.highlight} /> : post.title} {highlightKeyword ? <HighlightText text={post.title} keyword={highlightKeyword} highlightStyle={styles.highlight} /> : post.title}
</Text> </Text>
)} )}
@@ -758,7 +758,7 @@ const PostCardInner: React.FC<PostCardProps> = (normalizedProps) => {
style={styles.content} style={styles.content}
numberOfLines={isExpanded ? undefined : contentNumberOfLines} numberOfLines={isExpanded ? undefined : contentNumberOfLines}
> >
{highlightKeyword ? <HighlightText text={content} keyword={highlightKeyword} style={styles.highlight} /> : content} {highlightKeyword ? <HighlightText text={content} keyword={highlightKeyword} highlightStyle={styles.highlight} /> : content}
</Text> </Text>
{shouldTruncate && ( {shouldTruncate && (
<TouchableOpacity onPress={() => setIsExpanded((prev) => !prev)} style={styles.expandBtn}> <TouchableOpacity onPress={() => setIsExpanded((prev) => !prev)} style={styles.expandBtn}>

View File

@@ -61,7 +61,7 @@ const PostContentRenderer: React.FC<PostContentRendererProps> = ({
return ( return (
<Text numberOfLines={numberOfLines} selectable style={textStyle}> <Text numberOfLines={numberOfLines} selectable style={textStyle}>
{highlightKeyword && content ? ( {highlightKeyword && content ? (
<HighlightText text={content} keyword={highlightKeyword} style={highlightStyle} /> <HighlightText text={content} keyword={highlightKeyword} highlightStyle={highlightStyle} />
) : ( ) : (
content || '' content || ''
)} )}
@@ -85,7 +85,7 @@ const PostContentRenderer: React.FC<PostContentRendererProps> = ({
elements.push( elements.push(
<Text key={key} selectable style={textStyle}> <Text key={key} selectable style={textStyle}>
{highlightKeyword ? ( {highlightKeyword ? (
<HighlightText text={text} keyword={highlightKeyword} style={highlightStyle} /> <HighlightText text={text} keyword={highlightKeyword} highlightStyle={highlightStyle} />
) : ( ) : (
text text
)} )}
@@ -125,7 +125,7 @@ const PostContentRenderer: React.FC<PostContentRendererProps> = ({
return ( return (
<Text numberOfLines={numberOfLines} selectable style={textStyle}> <Text numberOfLines={numberOfLines} selectable style={textStyle}>
{highlightKeyword && content ? ( {highlightKeyword && content ? (
<HighlightText text={content} keyword={highlightKeyword} style={highlightStyle} /> <HighlightText text={content} keyword={highlightKeyword} highlightStyle={highlightStyle} />
) : ( ) : (
content || '' content || ''
)} )}

View File

@@ -551,7 +551,6 @@ function createUserProfileHeaderStyles(colors: AppColors) {
minWidth: 160, minWidth: 160,
backgroundColor: colors.background.paper, backgroundColor: colors.background.paper,
borderRadius: borderRadius.lg, borderRadius: borderRadius.lg,
...shadows.lg,
borderWidth: StyleSheet.hairlineWidth, borderWidth: StyleSheet.hairlineWidth,
borderColor: colors.divider, borderColor: colors.divider,
overflow: 'hidden', overflow: 'hidden',

View File

@@ -364,7 +364,7 @@ export const HomeScreen: React.FC = () => {
useEffect(() => { useEffect(() => {
if (homeTabPressCount > 0) { if (homeTabPressCount > 0) {
// 滚动 FlashList 到顶部 // 滚动 FlashList 到顶部
flashListRef.current?.scrollToOffset({ offset: 0, animated: true }); flashListRef.current?.scrollToOffset({ offset: 0, animated: false });
// 滚动 ScrollView 到顶部(网格模式) // 滚动 ScrollView 到顶部(网格模式)
scrollViewRef.current?.scrollTo({ y: 0, animated: true }); scrollViewRef.current?.scrollTo({ y: 0, animated: true });
// 重置底部 Tab 栏状态 // 重置底部 Tab 栏状态

View File

@@ -208,11 +208,6 @@ export function createChatScreenStyles(colors: AppColors, dynamicStyles?: {
backgroundColor: 'rgba(255, 255, 255, 0.96)', backgroundColor: 'rgba(255, 255, 255, 0.96)',
borderWidth: StyleSheet.hairlineWidth, borderWidth: StyleSheet.hairlineWidth,
borderColor: 'rgba(0, 0, 0, 0.08)', borderColor: 'rgba(0, 0, 0, 0.08)',
shadowColor: colors.chat.shadow,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.12,
shadowRadius: 6,
elevation: 4,
}, },
jumpToLatestFabText: { jumpToLatestFabText: {
fontSize: 13, fontSize: 13,

View File

@@ -1104,7 +1104,10 @@ export const useChatScreen = (props?: ChatScreenProps) => {
setSending(true); setSending(true);
try { try {
const segments: MessageSegment[] = [...buildTextSegments(trimmedText, replyingTo)]; // 有文本或回复时才构建文本段,纯图片时不塞空 text
const segments: MessageSegment[] = (trimmedText || replyingTo)
? [...buildTextSegments(trimmedText, replyingTo)]
: [];
for (const url of uploadedUrls) { for (const url of uploadedUrls) {
segments.push({ segments.push({
type: 'image', type: 'image',

View File

@@ -24,7 +24,7 @@ export const UserScreen: React.FC = () => {
<UserProfileScreen <UserProfileScreen
mode={isSelfProfile ? 'self' : 'other'} mode={isSelfProfile ? 'self' : 'other'}
userId={isSelfProfile ? undefined : userId} userId={isSelfProfile ? undefined : userId}
hasHeader={false} hasHeader
/> />
</SafeAreaView> </SafeAreaView>
); );