feat(auth): enhance login error handling with detailed error code mapping
- Add backend error_code support (INVALID_CREDENTIALS, USER_BANNED, etc.) - Improve network error detection with code=0 fallback - Add granular HTTP status code handling (400, 401, 403, 429, 5xx) - Update LoginScreen to display authStore error or generic message - Include notification service improvements: - Keep JPush connection alive in background via setKeepLongConnInBackground - Add manual sound/vibration for local notifications via preferences - Fix SearchScreen layout by accounting for floating tab bar and safe area
This commit is contained in:
@@ -107,12 +107,11 @@ export const LoginScreen: React.FC = () => {
|
||||
try {
|
||||
const success = await login({ username, password });
|
||||
if (!success) {
|
||||
if (!useAuthStore.getState().error) {
|
||||
showPrompt({ title: '登录失败', message: '请检查用户名和密码', type: 'error' });
|
||||
}
|
||||
const errorMsg = useAuthStore.getState().error || '登录失败,请稍后重试';
|
||||
showPrompt({ title: '登录失败', message: errorMsg, type: 'error' });
|
||||
}
|
||||
} catch (error: any) {
|
||||
showPrompt({ title: '登录失败', message: error.message || '请检查用户名和密码', type: 'error' });
|
||||
showPrompt({ title: '登录失败', message: error.message || '登录失败,请稍后重试', type: 'error' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
ScrollView,
|
||||
RefreshControl,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { spacing, fontSizes, borderRadius, useAppColors, type AppColors } from '../../theme';
|
||||
@@ -30,6 +30,8 @@ import { useCursorPagination } from '../../hooks/useCursorPagination';
|
||||
|
||||
const TABS = ['帖子', '用户'];
|
||||
const DEFAULT_PAGE_SIZE = 20;
|
||||
// 与 TabsLayout 中的常量保持一致:tabBarHeight 56 + 上下浮动间距 12*2
|
||||
const FLOATING_TAB_BAR_HEIGHT = 56 + 12 * 2;
|
||||
|
||||
type SearchType = 'posts' | 'users';
|
||||
|
||||
@@ -41,6 +43,7 @@ export const SearchScreen: React.FC<SearchScreenProps> = ({ onBack }) => {
|
||||
const colors = useAppColors();
|
||||
const styles = useMemo(() => createSearchScreenStyles(colors), [colors]);
|
||||
const router = useRouter();
|
||||
const insets = useSafeAreaInsets();
|
||||
const { searchHistory: history, addSearchHistory, clearSearchHistory } = useUserStore();
|
||||
|
||||
// 使用响应式 hook
|
||||
@@ -73,6 +76,9 @@ export const SearchScreen: React.FC<SearchScreenProps> = ({ onBack }) => {
|
||||
'4xl': 1200,
|
||||
});
|
||||
|
||||
// 底部预留空间:悬浮 TabBar 高度 + 底部安全区 + 间距
|
||||
const listBottomInset = FLOATING_TAB_BAR_HEIGHT + insets.bottom + spacing.md;
|
||||
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const [hasSearched, setHasSearched] = useState(false);
|
||||
@@ -238,7 +244,7 @@ export const SearchScreen: React.FC<SearchScreenProps> = ({ onBack }) => {
|
||||
return (
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={{ paddingBottom: responsivePadding }}
|
||||
contentContainerStyle={{ paddingBottom: listBottomInset }}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={isRefreshing}
|
||||
@@ -287,7 +293,7 @@ export const SearchScreen: React.FC<SearchScreenProps> = ({ onBack }) => {
|
||||
/>
|
||||
)}
|
||||
keyExtractor={item => item.id}
|
||||
contentContainerStyle={{ paddingBottom: responsivePadding }}
|
||||
contentContainerStyle={{ paddingBottom: listBottomInset }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
@@ -323,7 +329,7 @@ export const SearchScreen: React.FC<SearchScreenProps> = ({ onBack }) => {
|
||||
return (
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={{ paddingBottom: responsivePadding }}
|
||||
contentContainerStyle={{ paddingBottom: listBottomInset }}
|
||||
>
|
||||
<ResponsiveGrid
|
||||
columns={{ xs: 1, sm: 2, md: 2, lg: 3, xl: 3, '2xl': 4, '3xl': 4, '4xl': 5 }}
|
||||
@@ -412,7 +418,7 @@ export const SearchScreen: React.FC<SearchScreenProps> = ({ onBack }) => {
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
keyExtractor={item => item.id}
|
||||
contentContainerStyle={{ paddingVertical: responsiveGap }}
|
||||
contentContainerStyle={{ paddingVertical: responsiveGap, paddingBottom: listBottomInset }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
ListFooterComponent={userLoading ? <Loading size="sm" /> : null}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user