2025-08-07 19:22:55 +08:00

175 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import ChatInSvg from "@/assets/icons/svg/chatIn.svg";
import ChatNotInSvg from "@/assets/icons/svg/chatNotIn.svg";
import PersonInSvg from "@/assets/icons/svg/personIn.svg";
import PersonNotInSvg from "@/assets/icons/svg/personNotIn.svg";
import { WebSocketStatus } from "@/lib/websocket-util";
import { BottomTabBarProps } from "@react-navigation/bottom-tabs";
import { router } from "expo-router";
import React, { useMemo } from 'react';
import { Dimensions, Image, StyleSheet, TouchableOpacity, View } from 'react-native';
import Svg, { Circle, Ellipse, G, Mask, Path, Rect } from "react-native-svg";
// 使用 React.memo 包装 SVG 组件,避免不必要的重渲染
const TabIcon = React.memo(({ isActive, ActiveIcon, InactiveIcon }: {
isActive: boolean;
ActiveIcon: React.FC<{ width: number; height: number }>;
InactiveIcon: React.FC<{ width: number; height: number }>;
}) => {
const Icon = isActive ? ActiveIcon : InactiveIcon;
return <Icon width={24} height={24} />;
});
// 提取 SVG 组件,避免重复渲染
const CenterButtonSvg = React.memo(() => (
<Svg width="100%" height="100%" viewBox="0 0 85 85" fill="none">
<Mask id="mask0_1464_1669" maskUnits="userSpaceOnUse" x="0" y="0" width="85" height="85">
<Circle cx="42.5" cy="42.5" r="42.5" fill="#FFC959" />
</Mask>
<G mask="url(#mask0_1464_1669)">
<Circle cx="42.5" cy="42.5" r="42.5" fill="#FFD38D" />
<Path d="M20.2018 14.6411C21.8694 12.5551 26.4765 16.939 28.5716 19.3917L20.8604 20.0277C19.3178 19.3509 18.5342 16.7271 20.2018 14.6411Z" fill="#FFDBA3" />
<Path d="M21.3021 15.4913C22.503 13.6451 25.3001 17.1089 26.5485 19.0716L22.7323 19.2755C21.7552 18.7834 20.1012 17.3376 21.3021 15.4913Z" fill="#AC7E35" />
<Path d="M65.1253 14.6411C63.4577 12.5551 58.8506 16.939 56.7556 19.3917L64.4667 20.0277C66.0093 19.3509 66.7929 16.7271 65.1253 14.6411Z" fill="#FFDBA3" />
<Path d="M64.0255 15.4913C62.8246 13.6451 60.0276 17.1089 58.7792 19.0716L62.5953 19.2755C63.5724 18.7834 65.2264 17.3376 64.0255 15.4913Z" fill="#AC7E35" />
<Path d="M-15.3352 49.1734C10.3693 4.65192 74.6306 4.65187 100.335 49.1734L117.868 79.5409C143.572 124.062 111.442 179.714 60.0327 179.714H24.9673C-26.4417 179.714 -58.5724 124.062 -32.8679 79.5409L-15.3352 49.1734Z" fill="#FFD18A" />
<Rect x="38.5571" y="46.2812" width="2.62922" height="3.68091" rx="1.31461" transform="rotate(-180 38.5571 46.2812)" fill="#4C320C" />
<Rect x="48.0205" y="46.2812" width="2.62922" height="3.68091" rx="1.31461" transform="rotate(-180 48.0205 46.2812)" fill="#4C320C" />
<Path d="M4.8084 73.2062C22.9876 46.7781 62.0132 46.7782 80.1924 73.2062L100.897 103.306C121.776 133.659 100.046 174.982 63.2051 174.982H21.7957C-15.0453 174.982 -36.7756 133.659 -15.8963 103.306L4.8084 73.2062Z" fill="#FFF8DE" />
<Ellipse cx="79.047" cy="68.6298" rx="43.1193" ry="30.7619" fill="#FFF8DE" />
<Ellipse cx="5.69032" cy="68.6298" rx="42.8563" ry="30.7619" fill="#FFF8DE" />
<Ellipse cx="42.2365" cy="53.3803" rx="3.15507" ry="2.3663" transform="rotate(180 42.2365 53.3803)" fill="#FFB8B9" />
<Path d="M41.7813 56.0095C41.9837 55.6589 42.4897 55.6589 42.6921 56.0095L43.1475 56.7982C43.3499 57.1488 43.0969 57.587 42.6921 57.587H41.7813C41.3765 57.587 41.1235 57.1488 41.3259 56.7982L41.7813 56.0095Z" fill="#4C320C" />
</G>
</Svg>
));
type AskNavbarProps = BottomTabBarProps & {
wsStatus: WebSocketStatus;
};
const AskNavbar = ({ state, descriptors, navigation, wsStatus }: AskNavbarProps) => {
// 获取设备尺寸
const { width } = useMemo(() => Dimensions.get('window'), []);
const { routes, index } = state;
const currentRouteName = routes[index].name;
const statusColor = useMemo(() => {
switch (wsStatus) {
case 'connected':
return '#4CAF50'; // Green
case 'connecting':
case 'reconnecting':
return '#FFC107'; // Amber
case 'disconnected':
default:
return '#F44336'; // Red
}
}, [wsStatus]);
// 使用 useMemo 缓存样式对象
const styles = useMemo(() => StyleSheet.create({
container: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0
},
backgroundImage: {
width,
height: 80,
resizeMode: 'cover'
},
navButton: {
width: width / 2, // 半屏宽度
height: 80, // 与 navbar 高度相同
justifyContent: 'center',
alignItems: 'center'
},
navContainer: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: 80, // Set a fixed height for the navbar
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 32,
backgroundColor: 'transparent', // Make sure it's transparent
},
centerButton: {
position: 'absolute',
left: width / 2,
top: -30, // Adjust this value to move the button up or down
marginLeft: -42.5, // Half of the button width (85/2)
width: 85,
height: 85,
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#FFB645',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 8,
borderRadius: 50,
backgroundColor: 'transparent',
zIndex: 10,
},
statusIndicator: {
position: 'absolute',
top: 15,
right: 15,
width: 10,
height: 10,
borderRadius: 5,
borderWidth: 1,
borderColor: '#FFF',
backgroundColor: statusColor,
zIndex: 11,
}
}), [width, statusColor]);
// 如果当前路径是ask页面则不渲染导航栏
if (currentRouteName !== 'memo-list' && currentRouteName !== 'owner') {
return null;
}
return (
<View style={styles.container}>
<Image source={require('@/assets/images/png/owner/ask.png')} style={{ width: width * 1.18, height: 100, resizeMode: 'cover', marginLeft: -width * 0.07 }} />
<View style={styles.navContainer}>
<TouchableOpacity
onPress={() => navigation.navigate('memo-list')}
style={[styles.navButton, { alignItems: "flex-start", paddingLeft: 16 }]}
>
<TabIcon
isActive={currentRouteName === "memo-list"}
ActiveIcon={ChatInSvg}
InactiveIcon={ChatNotInSvg}
/>
</TouchableOpacity>
<TouchableOpacity
onPress={() => router.push({ pathname: '/ask', params: { newSession: "true" } })}
style={styles.centerButton}
>
<View style={styles.statusIndicator} />
<Image source={require('@/assets/images/png/owner/askIP.png')} />
</TouchableOpacity>
<TouchableOpacity
onPress={() => navigation.navigate('owner')}
style={styles.navButton}
>
<TabIcon
isActive={currentRouteName === "owner"}
ActiveIcon={PersonInSvg}
InactiveIcon={PersonNotInSvg}
/>
</TouchableOpacity>
</View>
</View>
);
};
export default React.memo(AskNavbar);