feat: 同步dev分支并保留当前修改
Some checks failed
Frontend CI / build-and-push-web (push) Failing after 1m35s
Frontend CI / ota-android (push) Successful in 13m3s
Frontend CI / build-android-apk (push) Successful in 39m23s

This commit is contained in:
lafay
2026-03-21 03:22:28 +08:00
parent 2c65330837
commit a8c78f0c3f
16 changed files with 461 additions and 128 deletions

View File

@@ -7,7 +7,7 @@ import {
ActivityIndicator,
ScrollView,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { colors, spacing, fontSizes, borderRadius } from '../../theme';
import { Text, ResponsiveContainer } from '../../components/common';
@@ -17,9 +17,13 @@ import { showPrompt } from '../../services/promptService';
import { useAuthStore } from '../../stores';
export const AccountSecurityScreen: React.FC = () => {
const { isWideScreen } = useResponsive();
const { isWideScreen, isMobile } = useResponsive();
const insets = useSafeAreaInsets();
const currentUser = useAuthStore((state) => state.currentUser);
const fetchCurrentUser = useAuthStore((state) => state.fetchCurrentUser);
// 底部间距,避免被 TabBar 遮挡
const scrollBottomInset = isMobile ? 64 + 24 + insets.bottom + spacing.md : spacing.md;
const [email, setEmail] = useState(currentUser?.email || '');
const [verificationCode, setVerificationCode] = useState('');
@@ -330,10 +334,10 @@ export const AccountSecurityScreen: React.FC = () => {
<SafeAreaView style={styles.container} edges={['bottom']}>
{isWideScreen ? (
<ResponsiveContainer maxWidth={800}>
<ScrollView contentContainerStyle={styles.scrollContent}>{content}</ScrollView>
<ScrollView contentContainerStyle={[styles.scrollContent, { paddingBottom: scrollBottomInset }]}>{content}</ScrollView>
</ResponsiveContainer>
) : (
<ScrollView contentContainerStyle={styles.scrollContent}>{content}</ScrollView>
<ScrollView contentContainerStyle={[styles.scrollContent, { paddingBottom: scrollBottomInset }]}>{content}</ScrollView>
)}
</SafeAreaView>
);

View File

@@ -8,7 +8,7 @@ import {
View,
Alert,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { Avatar, Button, EmptyState, ResponsiveContainer, Text } from '../../components/common';
@@ -16,15 +16,21 @@ import { authService } from '../../services';
import { colors, spacing, borderRadius, shadows } from '../../theme';
import { User } from '../../types';
import { RootStackParamList } from '../../navigation/types';
import { useResponsive } from '../../hooks';
type NavigationProp = NativeStackNavigationProp<RootStackParamList>;
export const BlockedUsersScreen: React.FC = () => {
const navigation = useNavigation<NavigationProp>();
const { isMobile } = useResponsive();
const insets = useSafeAreaInsets();
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [processingUserId, setProcessingUserId] = useState<string | null>(null);
// 底部间距,避免被 TabBar 遮挡
const listBottomInset = isMobile ? 64 + 24 + insets.bottom + spacing.md : spacing.md;
const loadBlockedUsers = useCallback(async () => {
try {
@@ -115,7 +121,7 @@ export const BlockedUsersScreen: React.FC = () => {
data={users}
keyExtractor={item => item.id}
renderItem={renderItem}
contentContainerStyle={[styles.listContent, users.length === 0 && styles.emptyContent]}
contentContainerStyle={[styles.listContent, users.length === 0 && styles.emptyContent, { paddingBottom: listBottomInset }]}
refreshControl={
<RefreshControl
refreshing={refreshing}

View File

@@ -102,7 +102,8 @@ export const EditProfileScreen: React.FC = () => {
const [uploadingAvatar, setUploadingAvatar] = useState(false);
const [uploadingCover, setUploadingCover] = useState(false);
const [saving, setSaving] = useState(false);
const bottomSafeDistance = isMobile ? insets.bottom + 92 : spacing.xl;
// 底部间距,避免被 TabBar 遮挡
const scrollBottomInset = isMobile ? 64 + 24 + insets.bottom + spacing.md : spacing.lg;
// 根据屏幕宽度计算封面高度
const coverHeight = isWideScreen ? Math.min((width * 9) / 16, 300) : (width * 9) / 16;
@@ -471,7 +472,7 @@ export const EditProfileScreen: React.FC = () => {
</View>
{/* 保存按钮 */}
<View style={[styles.buttonContainer, { marginBottom: bottomSafeDistance }]}>
<View style={[styles.buttonContainer, { marginBottom: scrollBottomInset }]}>
<TouchableOpacity
style={[
styles.saveButton,

View File

@@ -11,7 +11,7 @@ import {
Switch,
ScrollView,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { colors, spacing, fontSizes, borderRadius } from '../../theme';
@@ -34,7 +34,11 @@ export const NotificationSettingsScreen: React.FC = () => {
const [vibrationEnabled, setVibrationEnabledState] = useState(true);
const [pushEnabled, setPushEnabled] = useState(true);
const [soundEnabled, setSoundEnabled] = useState(true);
const { isWideScreen } = useResponsive();
const { isWideScreen, isMobile } = useResponsive();
const insets = useSafeAreaInsets();
// 底部间距,避免被 TabBar 遮挡
const scrollBottomInset = isMobile ? 64 + 24 + insets.bottom + spacing.md : spacing.md;
// 加载设置
useEffect(() => {
@@ -150,12 +154,12 @@ export const NotificationSettingsScreen: React.FC = () => {
<SafeAreaView style={styles.container} edges={['bottom']}>
{isWideScreen ? (
<ResponsiveContainer maxWidth={800}>
<ScrollView contentContainerStyle={styles.scrollContent}>
<ScrollView contentContainerStyle={[styles.scrollContent, { paddingBottom: scrollBottomInset }]}>
{renderContent()}
</ScrollView>
</ResponsiveContainer>
) : (
<ScrollView contentContainerStyle={styles.scrollContent}>
<ScrollView contentContainerStyle={[styles.scrollContent, { paddingBottom: scrollBottomInset }]}>
{renderContent()}
</ScrollView>
)}

View File

@@ -55,10 +55,12 @@ export const ProfileScreen: React.FC = () => {
const { isDesktop, isTablet, width } = useResponsive();
// 页面滚动底部安全间距,避免内容被底部 TabBar 遮挡
// TabBar 悬浮:高度 64 + 浮动间距 12*2 = 88
const scrollBottomInset = useMemo(() => {
if (isDesktop) return spacing.lg;
return tabBarHeight + insets.bottom + spacing.md;
}, [isDesktop, tabBarHeight, insets.bottom]);
const FLOATING_TAB_BAR_HEIGHT = 64 + 24; // TabBar高度 + 上下浮动间距
return FLOATING_TAB_BAR_HEIGHT + insets.bottom + spacing.md;
}, [isDesktop, insets.bottom]);
const [activeTab, setActiveTab] = useState(0);
const [posts, setPosts] = useState<Post[]>([]);

View File

@@ -20,6 +20,7 @@ import { colors, spacing, fontSizes, borderRadius } from '../../theme';
import { useAuthStore } from '../../stores';
import { Text, ResponsiveContainer } from '../../components/common';
import { useResponsive } from '../../hooks';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
interface SettingsItem {
key: string;
@@ -67,6 +68,11 @@ export const SettingsScreen: React.FC = () => {
const navigation = useNavigation();
const { logout } = useAuthStore();
const { isWideScreen } = useResponsive();
const insets = useSafeAreaInsets();
const { isMobile } = useResponsive();
// 底部间距,避免被 TabBar 遮挡
const scrollBottomInset = isMobile ? 64 + 24 + insets.bottom + spacing.md : spacing.md;
// 处理设置项点击
const handleItemPress = (key: string) => {
@@ -200,12 +206,12 @@ export const SettingsScreen: React.FC = () => {
<SafeAreaView style={styles.container} edges={['bottom']}>
{isWideScreen ? (
<ResponsiveContainer maxWidth={800}>
<ScrollView contentContainerStyle={styles.scrollContent}>
<ScrollView contentContainerStyle={[styles.scrollContent, { paddingBottom: scrollBottomInset }]}>
{renderContent()}
</ScrollView>
</ResponsiveContainer>
) : (
<ScrollView contentContainerStyle={styles.scrollContent}>
<ScrollView contentContainerStyle={[styles.scrollContent, { paddingBottom: scrollBottomInset }]}>
{renderContent()}
</ScrollView>
)}