refactor(core): distinguish network errors from auth failures and refactor real-time messaging pipeline
- Add isNetworkError() helper and FetchCurrentUserResult discriminated union to prevent logout on network failures
- Refactor authService to return { kind: 'user' } | { kind: 'auth_failed' } | { kind: 'network_error' }
- Update authStore to preserve login state on network errors, only logout on auth failures
- Replace WSMessageHandler with RealtimeIngestionPipeline for improved real-time message handling
- Remove MessageDeduplication service; add store-level idempotent addOrReplaceMessage for message dedup
- Add atomic systemUnreadCount operations to prevent race conditions
- Add SearchHeader component for consistent search UI across screens
- Add 'home' TabBar variant with underline style matching HomeScreen
- Add Jest test infrastructure and exclude tests from tsconfig
- Fix ChatScreen history lock being stuck when scrolling to latest messages
- Remove unused call_participant_joined/left WS event types
This commit is contained in:
@@ -10,7 +10,7 @@ import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { spacing, fontSizes, borderRadius, useAppColors, type AppColors } from '../../theme';
|
||||
import Text from '../common/Text';
|
||||
|
||||
type TabBarVariant = 'default' | 'pill' | 'segmented' | 'modern';
|
||||
type TabBarVariant = 'default' | 'pill' | 'segmented' | 'modern' | 'home';
|
||||
|
||||
interface TabBarProps {
|
||||
tabs: string[];
|
||||
@@ -20,6 +20,8 @@ interface TabBarProps {
|
||||
rightContent?: ReactNode;
|
||||
variant?: TabBarVariant;
|
||||
icons?: string[];
|
||||
/** home 变体的字号,默认 18(HomeScreen 可传 20) */
|
||||
fontSize?: number;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
@@ -176,6 +178,33 @@ function createTabBarStyles(colors: AppColors) {
|
||||
borderTopLeftRadius: borderRadius.sm,
|
||||
borderTopRightRadius: borderRadius.sm,
|
||||
},
|
||||
// home 变体:与 HomeScreen/SearchScreen 一致的文字下划线风格
|
||||
homeContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
homeTab: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingVertical: spacing.sm,
|
||||
marginRight: 20,
|
||||
borderBottomWidth: 2,
|
||||
borderBottomColor: 'transparent',
|
||||
},
|
||||
homeTabActive: {
|
||||
borderBottomColor: colors.text.primary,
|
||||
},
|
||||
homeTabText: {
|
||||
fontSize: fontSizes.xl,
|
||||
fontWeight: '400',
|
||||
color: colors.text.hint,
|
||||
},
|
||||
homeTabTextActive: {
|
||||
fontSize: fontSizes.xl,
|
||||
fontWeight: '700',
|
||||
color: colors.text.primary,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -187,6 +216,7 @@ const TabBar: React.FC<TabBarProps> = ({
|
||||
rightContent,
|
||||
variant = 'default',
|
||||
icons,
|
||||
fontSize,
|
||||
style,
|
||||
}) => {
|
||||
const colors = useAppColors();
|
||||
@@ -278,6 +308,29 @@ const TabBar: React.FC<TabBarProps> = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (variant === 'home') {
|
||||
const homeTextStyle = [
|
||||
isActive ? styles.homeTabTextActive : styles.homeTabText,
|
||||
fontSize != null ? { fontSize } : undefined,
|
||||
];
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={index}
|
||||
style={[styles.homeTab, isActive && styles.homeTabActive]}
|
||||
onPress={() => onTabChange(index)}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text
|
||||
variant="body"
|
||||
color={isActive ? colors.text.primary : colors.text.hint}
|
||||
style={homeTextStyle}
|
||||
>
|
||||
{tab}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={index}
|
||||
@@ -306,6 +359,8 @@ const TabBar: React.FC<TabBarProps> = ({
|
||||
return styles.segmentedContainer;
|
||||
case 'modern':
|
||||
return styles.modernContainer;
|
||||
case 'home':
|
||||
return styles.homeContainer;
|
||||
default:
|
||||
return styles.container;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user