From c00b915e5fb0b0bea01155edbd440d702149033f Mon Sep 17 00:00:00 2001
From: lafay <2021211506@stu.hit.edu.cn>
Date: Tue, 14 Apr 2026 02:12:53 +0800
Subject: [PATCH] chore: remove IDE config files and improve web platform
compatibility
- Remove `.idea/` IntelliJ configuration files from version control
- Add web-specific touch handling for swipeable message bubbles and schedule screen
- Fix CSS touch-action rules for better web scrolling behavior
- Add nestedScrollEnabled to ScrollViews for proper gesture handling
- Improve null safety checks in profile screens
- Add horizontal ScrollView wrapper for notification filter tags
- Add hasHeader prop support for embedded profile screens
---
.idea/.gitignore | 10 -
.idea/frontend.iml | 9 -
.idea/gradle.xml | 448 ------------------
.idea/misc.xml | 7 -
.idea/modules.xml | 8 -
.idea/vcs.xml | 6 -
app/_layout.tsx | 35 +-
src/components/common/ImageGallery.tsx | 2 +-
src/screens/home/HomeScreen.tsx | 1 +
src/screens/home/PostDetailScreen.tsx | 6 +-
src/screens/message/ChatScreen.tsx | 2 +-
src/screens/message/NotificationsScreen.tsx | 72 +--
.../ChatScreen/SwipeableMessageBubble.tsx | 128 ++++-
src/screens/profile/EditProfileScreen.tsx | 8 +-
src/screens/profile/FollowListScreen.tsx | 12 +-
src/screens/profile/UserProfileScreen.tsx | 13 +-
src/screens/profile/UserScreen.tsx | 1 +
src/screens/schedule/ScheduleScreen.tsx | 77 ++-
18 files changed, 262 insertions(+), 583 deletions(-)
delete mode 100644 .idea/.gitignore
delete mode 100644 .idea/frontend.iml
delete mode 100644 .idea/gradle.xml
delete mode 100644 .idea/misc.xml
delete mode 100644 .idea/modules.xml
delete mode 100644 .idea/vcs.xml
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index f5fb94d..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-# 默认忽略的文件
-/shelf/
-/workspace.xml
-# 基于编辑器的 HTTP 客户端请求
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
-# Screenshots
-screenshots/
diff --git a/.idea/frontend.iml b/.idea/frontend.iml
deleted file mode 100644
index d6ebd48..0000000
--- a/.idea/frontend.iml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
deleted file mode 100644
index 6752ffb..0000000
--- a/.idea/gradle.xml
+++ /dev/null
@@ -1,448 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index ade1ecd..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index f3d93d7..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1dd..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/_layout.tsx b/app/_layout.tsx
index caa3c29..b1d55b9 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -53,24 +53,33 @@ if (Platform.OS === 'web' && typeof document !== 'undefined') {
}
*:focus { outline: none !important; }
*:focus-visible { outline: none !important; }
- /* 修复移动端 FlatList/ScrollView 滑动问题 */
+ /* 修复移动端滑动问题 - 仅对根容器限制 */
html, body {
overscroll-behavior: none;
- touch-action: pan-y;
+ touch-action: manipulation;
+ -webkit-overflow-scrolling: touch;
}
- /* React Native Web 生成的滚动容器 */
- [class*="css-"] {
- touch-action: pan-y !important;
- -webkit-overflow-scrolling: touch !important;
+ /* React Native Web 生成的滚动容器 - 允许双向滑动 */
+ [class*="css-view"] {
+ touch-action: auto;
+ -webkit-overflow-scrolling: touch;
}
- /* 确保所有可滚动元素都可以垂直滑动 */
- div[style*="overflow"] {
- touch-action: pan-y !important;
- -webkit-overflow-scrolling: touch !important;
+ /* 水平滚动容器 */
+ [data-horizontal-scroll="true"],
+ div[style*="overflow-x"],
+ div[style*="overflow: scroll"],
+ div[style*="overflow: auto"] {
+ touch-action: pan-x pan-y;
+ -webkit-overflow-scrolling: touch;
}
- /* 禁用水平滑动,只允许垂直滑动 */
- * {
- touch-action: pan-y pinch-zoom;
+ /* 手势区域 - 允许滑动手势 */
+ [data-gesture-area="true"] {
+ touch-action: pan-x pan-y;
+ }
+ /* 图片查看器手势区域 */
+ .react-native-image-viewer,
+ [data-image-viewer="true"] {
+ touch-action: pinch-zoom pan-x pan-y;
}
`;
document.head.appendChild(style);
diff --git a/src/components/common/ImageGallery.tsx b/src/components/common/ImageGallery.tsx
index 5bac549..18e06d8 100644
--- a/src/components/common/ImageGallery.tsx
+++ b/src/components/common/ImageGallery.tsx
@@ -361,7 +361,7 @@ export const ImageGallery: React.FC = ({
onRequestClose={requestClose}
statusBarTranslucent
>
-
+
{/* 顶部控制栏 */}
{showControls && (
diff --git a/src/screens/home/HomeScreen.tsx b/src/screens/home/HomeScreen.tsx
index 4d2e7a7..8af9968 100644
--- a/src/screens/home/HomeScreen.tsx
+++ b/src/screens/home/HomeScreen.tsx
@@ -676,6 +676,7 @@ export const HomeScreen: React.FC = () => {
ref={capsuleHScrollRef}
horizontal
showsHorizontalScrollIndicator={false}
+ nestedScrollEnabled
contentContainerStyle={styles.capsuleList}
onScroll={onCapsuleHorizontalScroll}
scrollEventThrottle={16}
diff --git a/src/screens/home/PostDetailScreen.tsx b/src/screens/home/PostDetailScreen.tsx
index 6438ba1..ded82b6 100644
--- a/src/screens/home/PostDetailScreen.tsx
+++ b/src/screens/home/PostDetailScreen.tsx
@@ -1182,7 +1182,7 @@ export const PostDetailScreen: React.FC = () => {
)}
- {currentUser?.id === post.author?.id && (
+ {!!currentUser && currentUser.id === post.author?.id && (
{
)}
- {/* 举报按钮 - 只对非帖子作者显示 */}
- {currentUser?.id !== post.author?.id && (
+ {/* 举报按钮 - 只对已登录且非帖子作者显示 */}
+ {!!currentUser && currentUser.id !== post.author?.id && (
{
diff --git a/src/screens/message/ChatScreen.tsx b/src/screens/message/ChatScreen.tsx
index ee7a732..cf7dfcb 100644
--- a/src/screens/message/ChatScreen.tsx
+++ b/src/screens/message/ChatScreen.tsx
@@ -415,7 +415,7 @@ export const ChatScreen: React.FC = (props) => {
return (
diff --git a/src/screens/message/NotificationsScreen.tsx b/src/screens/message/NotificationsScreen.tsx
index 7eb4944..d8c6175 100644
--- a/src/screens/message/NotificationsScreen.tsx
+++ b/src/screens/message/NotificationsScreen.tsx
@@ -23,6 +23,7 @@ import {
BackHandler,
Animated,
StatusBar,
+ ScrollView,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useIsFocused } from '@react-navigation/native';
@@ -444,6 +445,11 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
{renderHeader()}
{/* 分类筛选 */}
+
{MESSAGE_TYPES.map(type => {
const count = type.key === 'all'
? displayMessages.length
@@ -469,9 +475,7 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
activeOpacity={0.8}
>
{type.title}
@@ -479,7 +483,7 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
{count}
@@ -488,6 +492,7 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
);
})}
+
{/* 消息列表 */}
@@ -526,6 +531,11 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
{renderHeader()}
{/* 分类筛选 */}
+
{MESSAGE_TYPES.map(type => {
const count = type.key === 'all'
? displayMessages.length
@@ -550,9 +560,7 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
activeOpacity={0.8}
>
{type.title}
@@ -560,7 +568,7 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
{count}
@@ -569,6 +577,7 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
);
})}
+
{/* 消息列表 */}
@@ -677,52 +686,51 @@ function createNotificationsStyles(colors: AppColors) {
},
// 分类筛选 - 扁平化风格(分段式设计)
filterContainer: {
- flexDirection: 'row',
- paddingHorizontal: 16,
- paddingVertical: 12,
backgroundColor: colors.background.paper,
borderBottomWidth: 1,
borderBottomColor: colors.divider || '#E5E5EA',
},
filterContainerWideWeb: {
- paddingHorizontal: 32,
- paddingVertical: 16,
- justifyContent: 'center',
+ alignItems: 'center',
+ },
+ filterList: {
+ paddingVertical: spacing.md,
+ gap: spacing.xs,
+ paddingLeft: spacing.lg,
+ paddingRight: spacing.lg,
},
filterTag: {
flexDirection: 'row',
alignItems: 'center',
- paddingHorizontal: 14,
- paddingVertical: 8,
- marginRight: 8,
- borderRadius: 14,
- backgroundColor: colors.background.default,
- borderWidth: 1,
- borderColor: 'transparent',
+ paddingHorizontal: spacing.md,
+ paddingVertical: spacing.xs,
+ borderRadius: borderRadius.full,
},
filterTagWide: {
- paddingHorizontal: 18,
- paddingVertical: 10,
- marginRight: 10,
+ paddingHorizontal: spacing.lg,
+ paddingVertical: spacing.sm,
},
filterTagActive: {
- backgroundColor: colors.primary.main,
- borderColor: colors.primary.main,
+ },
+ filterTagText: {
+ fontSize: 13,
+ fontWeight: '600',
+ color: colors.text.secondary,
},
filterTagTextActive: {
- fontWeight: '600',
+ color: colors.primary.main,
},
filterCount: {
- marginLeft: 6,
- backgroundColor: colors.primary.light + '40',
- paddingHorizontal: 6,
+ marginLeft: spacing.xs,
+ backgroundColor: colors.background.default,
+ paddingHorizontal: spacing.xs,
paddingVertical: 1,
- borderRadius: 8,
+ borderRadius: borderRadius.full,
minWidth: 18,
alignItems: 'center',
},
filterCountActive: {
- backgroundColor: 'rgba(255, 255, 255, 0.25)',
+ backgroundColor: colors.primary.light + '30',
},
listContent: {
flexGrow: 1,
diff --git a/src/screens/message/components/ChatScreen/SwipeableMessageBubble.tsx b/src/screens/message/components/ChatScreen/SwipeableMessageBubble.tsx
index 6d47c97..137ead3 100644
--- a/src/screens/message/components/ChatScreen/SwipeableMessageBubble.tsx
+++ b/src/screens/message/components/ChatScreen/SwipeableMessageBubble.tsx
@@ -4,13 +4,12 @@
*/
import React, { useRef, useCallback } from 'react';
-import { View, StyleSheet, Animated } from 'react-native';
+import { View, StyleSheet, Animated, Platform } from 'react-native';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { PanGestureHandler, State } from 'react-native-gesture-handler';
import * as Haptics from 'expo-haptics';
import { useAppColors } from '../../../../theme';
-// 滑动阈值
const SWIPE_THRESHOLD = 30;
const MAX_SWIPE_DISTANCE = 50;
@@ -31,45 +30,39 @@ export const SwipeableMessageBubble: React.FC = ({
const translateX = useRef(new Animated.Value(0)).current;
const hasTriggeredRef = useRef(false);
- // 处理手势事件 - 使用原生驱动,不干预值
const onGestureEvent = Animated.event(
[{ nativeEvent: { translationX: translateX } }],
{ useNativeDriver: true }
);
- // 处理手势状态变化
const onHandlerStateChange = useCallback((event: any) => {
const { nativeEvent } = event;
if (nativeEvent.state === State.END) {
const translationX = nativeEvent.translationX;
- // 只允许特定方向的滑动
const shouldTrigger = isMe
- ? translationX < -SWIPE_THRESHOLD // 自己消息只能向左滑
- : translationX > SWIPE_THRESHOLD; // 对方消息只能向右滑
+ ? translationX < -SWIPE_THRESHOLD
+ : translationX > SWIPE_THRESHOLD;
if (shouldTrigger && !hasTriggeredRef.current) {
hasTriggeredRef.current = true;
- // 触觉反馈
- Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
+ if (Platform.OS !== 'web') {
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
+ }
- // 立即触发回复
onReply();
- // 重置标记
setTimeout(() => {
hasTriggeredRef.current = false;
}, 300);
}
- // 立即回到原位
translateX.setValue(0);
}
}, [isMe, onReply]);
- // 计算回复图标的透明度 - 只在允许的方向显示
const iconOpacity = translateX.interpolate({
inputRange: isMe
? [-MAX_SWIPE_DISTANCE, -SWIPE_THRESHOLD, 0, 10]
@@ -78,7 +71,6 @@ export const SwipeableMessageBubble: React.FC = ({
extrapolate: 'clamp',
});
- // 计算消息的位移 - 限制在允许的方向
const clampedTranslateX = translateX.interpolate({
inputRange: isMe
? [-MAX_SWIPE_DISTANCE - 10, -MAX_SWIPE_DISTANCE, 0, 10]
@@ -89,21 +81,117 @@ export const SwipeableMessageBubble: React.FC = ({
extrapolate: 'clamp',
});
- // 如果禁用手势,直接返回子元素
+ const webTouchStartRef = useRef<{ x: number; time: number } | null>(null);
+ const webTranslateXRef = useRef(new Animated.Value(0)).current;
+ const webIconOpacity = webTranslateXRef.interpolate({
+ inputRange: isMe
+ ? [-MAX_SWIPE_DISTANCE, -SWIPE_THRESHOLD, 0, 10]
+ : [-10, 0, SWIPE_THRESHOLD, MAX_SWIPE_DISTANCE],
+ outputRange: isMe ? [1, 0.8, 0, 0] : [0, 0, 0.8, 1],
+ extrapolate: 'clamp',
+ });
+ const webClampedTranslateX = webTranslateXRef.interpolate({
+ inputRange: isMe
+ ? [-MAX_SWIPE_DISTANCE - 10, -MAX_SWIPE_DISTANCE, 0, 10]
+ : [-10, 0, MAX_SWIPE_DISTANCE, MAX_SWIPE_DISTANCE + 10],
+ outputRange: isMe
+ ? [-MAX_SWIPE_DISTANCE, -MAX_SWIPE_DISTANCE, 0, 0]
+ : [0, 0, MAX_SWIPE_DISTANCE, MAX_SWIPE_DISTANCE],
+ extrapolate: 'clamp',
+ });
+ const webLastTouchRef = useRef<{ x: number } | null>(null);
+
+ const handleWebTouchStart = useCallback((e: any) => {
+ const touch = e.nativeEvent?.touches?.[0] || e.nativeEvent;
+ if (touch && touch.pageX !== undefined) {
+ webTouchStartRef.current = { x: touch.pageX, time: Date.now() };
+ webLastTouchRef.current = { x: touch.pageX };
+ }
+ }, []);
+
+ const handleWebTouchMove = useCallback((e: any) => {
+ const start = webTouchStartRef.current;
+ if (!start) return;
+ const touch = e.nativeEvent?.touches?.[0] || e.nativeEvent;
+ if (!touch || touch.pageX === undefined) return;
+ const dx = touch.pageX - start.x;
+ const allowed = isMe ? dx < 5 : dx > -5;
+ if (allowed) {
+ webTranslateXRef.setValue(dx);
+ webLastTouchRef.current = { x: touch.pageX };
+ }
+ }, [isMe]);
+
+ const handleWebTouchEnd = useCallback((e: any) => {
+ const start = webTouchStartRef.current;
+ if (!start) return;
+ webTouchStartRef.current = null;
+ const touch = e.nativeEvent?.changedTouches?.[0] || e.nativeEvent;
+ if (!touch || touch.pageX === undefined) {
+ Animated.spring(webTranslateXRef, { toValue: 0, useNativeDriver: true }).start();
+ return;
+ }
+ const dx = touch.pageX - start.x;
+ const shouldTrigger = isMe
+ ? dx < -SWIPE_THRESHOLD
+ : dx > SWIPE_THRESHOLD;
+
+ if (shouldTrigger && !hasTriggeredRef.current) {
+ hasTriggeredRef.current = true;
+ onReply();
+ setTimeout(() => {
+ hasTriggeredRef.current = false;
+ }, 300);
+ }
+
+ Animated.spring(webTranslateXRef, { toValue: 0, useNativeDriver: true }).start();
+ }, [isMe, onReply]);
+
if (!enabled) {
return <>{children}>;
}
+ if (Platform.OS === 'web') {
+ return (
+
+
+
+
+
+
+ {children}
+
+
+
+ );
+ }
+
return (
- {/* 回复图标 - 在消息后面 */}
= ({
color={colors.primary.main}
/>
-
- {/* 可滑动的消息内容 */}
{
{/* 用户名和简介 */}
- {nickname || currentUser?.nickname}
+ {nickname || currentUser?.nickname || ''}
- @{currentUser?.username}
+ @{currentUser?.username || ''}
{bio ? (
@@ -624,7 +624,7 @@ export const EditProfileScreen: React.FC = () => {
- {website.replace(/^https?:\/\//, '')}
+ {typeof website === 'string' ? website.replace(/^https?:\/\//, '') : ''}
) : (
@@ -638,7 +638,7 @@ export const EditProfileScreen: React.FC = () => {
- 加入于 {new Date(currentUser?.created_at || Date.now()).getFullYear()}年
+ {`加入于 ${new Date(currentUser?.created_at || Date.now()).getFullYear()}年`}
diff --git a/src/screens/profile/FollowListScreen.tsx b/src/screens/profile/FollowListScreen.tsx
index 17c2ce2..72091ab 100644
--- a/src/screens/profile/FollowListScreen.tsx
+++ b/src/screens/profile/FollowListScreen.tsx
@@ -186,7 +186,7 @@ const FollowListScreen: React.FC = () => {
) : null}
- {!isSelf && (
+ {isCurrentUser && !isSelf && (