This commit is contained in:
imeepos
2026-01-29 16:20:01 +08:00
parent 7c3b3c066c
commit 1ccdc23355
6 changed files with 315 additions and 229 deletions

View File

@@ -155,12 +155,10 @@ export default function MessageScreen() {
// Render message item
const renderMessageItem = useCallback(({ item }: { item: Message }) => (
<SwipeToDelete onDelete={() => handleDeleteMessage(item.id)}>
<MessageCard
message={item}
onPress={handleMessagePress}
/>
</SwipeToDelete>
), [handleDeleteMessage, handleMessagePress])
// Render list footer
@@ -288,6 +286,7 @@ const styles = StyleSheet.create({
listContent: {
paddingHorizontal: 4,
paddingTop: 12,
paddingBottom: 100,
},
emptyListContent: {
flex: 1,

View File

@@ -32,7 +32,6 @@ import { MySkeleton } from '@/components/skeleton/MySkeleton'
import { TabNavigation } from '@/components/blocks/home/TabNavigation'
import { useUserBalance } from '@/hooks/use-user-balance'
import { KeyboardAwareScrollView } from 'react-native-keyboard-controller'
const { width: screenWidth } = Dimensions.get('window')
const GALLERY_GAP = 2
@@ -136,9 +135,13 @@ export default function My() {
// 下拉刷新状态
const [refreshing, setRefreshing] = useState(false)
const [favoritesRefreshing, setFavoritesRefreshing] = useState(false)
const [likesRefreshing, setLikesRefreshing] = useState(false)
// 防止重复触发加载更多
const isLoadingMoreRef = useRef(false)
const isFavoritesLoadingMoreRef = useRef(false)
const isLikesLoadingMoreRef = useRef(false)
// 下拉刷新处理
const onRefresh = useCallback(async () => {
@@ -150,6 +153,26 @@ export default function My() {
}
}, [refetch])
// 收藏Tab下拉刷新处理
const onFavoritesRefresh = useCallback(async () => {
setFavoritesRefreshing(true)
try {
await favoritesRefetch()
} finally {
setFavoritesRefreshing(false)
}
}, [favoritesRefetch])
// 点赞Tab下拉刷新处理
const onLikesRefresh = useCallback(async () => {
setLikesRefreshing(true)
try {
await likesRefetch()
} finally {
setLikesRefreshing(false)
}
}, [likesRefetch])
// 加载更多处理
const handleScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent
@@ -173,6 +196,40 @@ export default function My() {
}
}, [loadingMore, hasMore, loadMore, loading])
// 收藏Tab加载更多处理
const handleFavoritesScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent
const paddingToBottom = 200
const isCloseToBottom =
layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom
if (isCloseToBottom && !favoritesLoadingMore && favoritesHasMore && !favoritesLoading && !isFavoritesLoadingMoreRef.current) {
console.log('🔄 收藏Tab触发加载更多')
isFavoritesLoadingMoreRef.current = true
favoritesLoadMore().finally(() => {
isFavoritesLoadingMoreRef.current = false
})
}
}, [favoritesLoadingMore, favoritesHasMore, favoritesLoadMore, favoritesLoading])
// 点赞Tab加载更多处理
const handleLikesScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent
const paddingToBottom = 200
const isCloseToBottom =
layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom
if (isCloseToBottom && !likesLoadingMore && likesHasMore && !likesLoading && !isLikesLoadingMoreRef.current) {
console.log('🔄 点赞Tab触发加载更多')
isLikesLoadingMoreRef.current = true
likesLoadMore().finally(() => {
isLikesLoadingMoreRef.current = false
})
}
}, [likesLoadingMore, likesHasMore, likesLoadMore, likesLoading])
// 处理设置菜单选择
const handleSettingsSelect = async (value: string) => {
if (value === 'changePassword') {
@@ -231,9 +288,7 @@ export default function My() {
]
return (
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
<KeyboardAwareScrollView bottomOffset={50}>
<SafeAreaView style={styles.container} edges={['top']}>
<StatusBar style="light" />
<RNStatusBar barStyle="light-content" />
@@ -390,16 +445,29 @@ export default function My() {
style={styles.scrollView}
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
onScroll={handleFavoritesScroll}
scrollEventThrottle={16}
refreshControl={
<RefreshControl
refreshing={favoritesRefreshing}
onRefresh={onFavoritesRefresh}
tintColor="#9966FF"
colors={['#9966FF', '#FF6699', '#FF9966']}
progressBackgroundColor="#1C1E22"
progressViewOffset={10}
/>
}
>
<View style={styles.galleryGrid}>
{favorites.length === 0 ? (
{favorites.length === 0 && !favoritesLoading ? (
<View style={styles.emptyState}>
<Text style={styles.emptyStateText}>
{t('my.noFavorites')}
</Text>
</View>
) : (
favorites.map((item, index) => (
<>
{favorites.map((item, index) => (
<Pressable
key={item.id}
style={[
@@ -414,7 +482,14 @@ export default function My() {
contentFit="cover"
/>
</Pressable>
))
))}
{/* 加载更多指示器 */}
{favoritesLoadingMore && (
<View style={styles.loadingMoreContainer}>
<ActivityIndicator size="small" color="#9966FF" />
</View>
)}
</>
)}
</View>
</ScrollView>
@@ -425,16 +500,29 @@ export default function My() {
style={styles.scrollView}
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
onScroll={handleLikesScroll}
scrollEventThrottle={16}
refreshControl={
<RefreshControl
refreshing={likesRefreshing}
onRefresh={onLikesRefresh}
tintColor="#9966FF"
colors={['#9966FF', '#FF6699', '#FF9966']}
progressBackgroundColor="#1C1E22"
progressViewOffset={10}
/>
}
>
<View style={styles.galleryGrid}>
{likes.length === 0 ? (
{likes.length === 0 && !likesLoading ? (
<View style={styles.emptyState}>
<Text style={styles.emptyStateText}>
{t('my.noLikes')}
</Text>
</View>
) : (
likes.map((item, index) => (
<>
{likes.map((item, index) => (
<Pressable
key={item.id}
style={[
@@ -449,14 +537,20 @@ export default function My() {
contentFit="cover"
/>
</Pressable>
))
))}
{/* 加载更多指示器 */}
{likesLoadingMore && (
<View style={styles.loadingMoreContainer}>
<ActivityIndicator size="small" color="#9966FF" />
</View>
)}
</>
)}
</View>
</ScrollView>
) : null}
{/* 编辑资料抽屉 */}
<EditProfileDrawer
visible={editDrawerVisible}
onClose={() => setEditDrawerVisible(false)}
@@ -466,7 +560,6 @@ export default function My() {
setProfileName(data.name)
}}
/>
</KeyboardAwareScrollView>
</SafeAreaView>
)
}
@@ -506,7 +599,7 @@ const styles = StyleSheet.create({
scrollContent: {
backgroundColor: '#090A0B',
paddingHorizontal: GALLERY_HORIZONTAL_PADDING,
paddingBottom: 20,
paddingBottom: 100,
},
profileSection: {
flexDirection: 'row',

View File

@@ -112,9 +112,6 @@ export const MessageCard: React.FC<MessageCardProps> = ({
onDelete,
}) => {
const config = getMessageConfig(message.data?.subType)
const icon = getMessageIcon(message.data?.subType)
const showAvatar = config.showAvatar && message.data?.likerAvatar
const showPreview = config.showPreview && message.data?.webpPreviewUrl
const showQuote = config.showQuote && message.data?.originalComment
const handlePress = () => {
@@ -148,15 +145,6 @@ export const MessageCard: React.FC<MessageCardProps> = ({
{message.content}
</Text>
{/* Preview Image */}
{showPreview && (
<Image
testID="preview-image"
source={{ uri: message.data?.webpPreviewUrl }}
style={styles.previewImage}
/>
)}
{/* Quote */}
{showQuote && (
<View testID="quote-container" style={styles.quoteContainer}>

View File

@@ -25,8 +25,8 @@ export const useMessages = (initialParams?: ListMessagesParams) => {
const [refreshing, setRefreshing] = useState(false)
const [error, setError] = useState<ApiError | null>(null)
const [data, setData] = useState<ListMessagesResult>()
const [hasMore, setHasMore] = useState(true)
const currentPageRef = useRef(1)
const hasMoreRef = useRef(true)
const execute = useCallback(async (params?: ListMessagesParams) => {
setLoading(true)
@@ -53,14 +53,15 @@ export const useMessages = (initialParams?: ListMessagesParams) => {
const currentPage = requestParams.page || 1
const totalPages = data?.totalPages || 1
hasMoreRef.current = currentPage < totalPages
const hasMoreValue = currentPage < totalPages
setHasMore(hasMoreValue)
setData(data)
setLoading(false)
return { data, error: null }
}, [initialParams])
const loadMore = useCallback(async () => {
if (loadingMore || loading || !hasMoreRef.current) return { data: undefined, error: null }
if (loadingMore || loading || !hasMore) return { data: undefined, error: null }
setLoadingMore(true)
const nextPage = currentPageRef.current + 1
@@ -83,7 +84,8 @@ export const useMessages = (initialParams?: ListMessagesParams) => {
const newMessages = newData?.messages || []
const totalPages = newData?.totalPages || 1
hasMoreRef.current = nextPage < totalPages
const hasMoreValue = nextPage < totalPages
setHasMore(hasMoreValue)
currentPageRef.current = nextPage
setData((prev) => ({
@@ -92,10 +94,10 @@ export const useMessages = (initialParams?: ListMessagesParams) => {
}))
setLoadingMore(false)
return { data: newData, error: null }
}, [loading, loadingMore, initialParams])
}, [loading, loadingMore, hasMore, initialParams])
const refetch = useCallback(async () => {
hasMoreRef.current = true
setHasMore(true)
setRefreshing(true)
const result = await execute()
setRefreshing(false)
@@ -108,11 +110,11 @@ export const useMessages = (initialParams?: ListMessagesParams) => {
loading,
loadingMore,
refreshing,
hasMore,
error,
execute,
refetch,
loadMore,
hasMore: hasMoreRef.current,
}
}

View File

@@ -24,8 +24,8 @@ export const useUserFavorites = (initialParams?: GetUserFavoritesParams) => {
const [loadingMore, setLoadingMore] = useState(false)
const [error, setError] = useState<ApiError | null>(null)
const [data, setData] = useState<GetUserFavoritesResponse>()
const [hasMore, setHasMore] = useState(true)
const currentPageRef = useRef(1)
const hasMoreRef = useRef(true)
const execute = useCallback(async (params?: GetUserFavoritesParams) => {
setLoading(true)
@@ -55,14 +55,15 @@ export const useUserFavorites = (initialParams?: GetUserFavoritesParams) => {
const total = data?.total || 0
const limit = requestParams.limit || 20
const totalPages = Math.ceil(total / limit)
hasMoreRef.current = currentPage < totalPages
const hasMoreValue = currentPage < totalPages
setHasMore(hasMoreValue)
setData(data)
setLoading(false)
return { data, error: null }
}, [initialParams])
const loadMore = useCallback(async () => {
if (loadingMore || loading || !hasMoreRef.current) return { data: undefined, error: null }
if (loadingMore || loading || !hasMore) return { data: undefined, error: null }
setLoadingMore(true)
const nextPage = currentPageRef.current + 1
@@ -87,7 +88,8 @@ export const useUserFavorites = (initialParams?: GetUserFavoritesParams) => {
const total = newData?.total || 0
const limit = requestParams.limit || 20
const totalPages = Math.ceil(total / limit)
hasMoreRef.current = nextPage < totalPages
const hasMoreValue = nextPage < totalPages
setHasMore(hasMoreValue)
currentPageRef.current = nextPage
setData((prev) => ({
@@ -98,10 +100,10 @@ export const useUserFavorites = (initialParams?: GetUserFavoritesParams) => {
}))
setLoadingMore(false)
return { data: newData, error: null }
}, [loading, loadingMore, initialParams])
}, [loading, loadingMore, hasMore, initialParams])
const refetch = useCallback(() => {
hasMoreRef.current = true
setHasMore(true)
return execute()
}, [execute])
@@ -110,11 +112,11 @@ export const useUserFavorites = (initialParams?: GetUserFavoritesParams) => {
favorites: data?.favorites || [],
loading,
loadingMore,
hasMore,
error,
execute,
refetch,
loadMore,
hasMore: hasMoreRef.current,
}
}

View File

@@ -21,8 +21,8 @@ export const useUserLikes = (initialParams?: GetUserLikesParams) => {
const [loadingMore, setLoadingMore] = useState(false)
const [error, setError] = useState<ApiError | null>(null)
const [data, setData] = useState<GetUserLikesResponse>()
const [hasMore, setHasMore] = useState(true)
const currentPageRef = useRef(1)
const hasMoreRef = useRef(true)
const execute = useCallback(async (params?: GetUserLikesParams) => {
setLoading(true)
@@ -52,14 +52,15 @@ export const useUserLikes = (initialParams?: GetUserLikesParams) => {
const total = data?.total || 0
const limit = requestParams.limit || 20
const totalPages = Math.ceil(total / limit)
hasMoreRef.current = currentPage < totalPages
const hasMoreValue = currentPage < totalPages
setHasMore(hasMoreValue)
setData(data)
setLoading(false)
return { data, error: null }
}, [initialParams])
const loadMore = useCallback(async () => {
if (loadingMore || loading || !hasMoreRef.current) return { data: undefined, error: null }
if (loadingMore || loading || !hasMore) return { data: undefined, error: null }
setLoadingMore(true)
const nextPage = currentPageRef.current + 1
@@ -84,7 +85,8 @@ export const useUserLikes = (initialParams?: GetUserLikesParams) => {
const total = newData?.total || 0
const limit = requestParams.limit || 20
const totalPages = Math.ceil(total / limit)
hasMoreRef.current = nextPage < totalPages
const hasMoreValue = nextPage < totalPages
setHasMore(hasMoreValue)
currentPageRef.current = nextPage
setData((prev) => ({
@@ -93,10 +95,10 @@ export const useUserLikes = (initialParams?: GetUserLikesParams) => {
}))
setLoadingMore(false)
return { data: newData, error: null }
}, [loading, loadingMore, initialParams])
}, [loading, loadingMore, hasMore, initialParams])
const refetch = useCallback(() => {
hasMoreRef.current = true
setHasMore(true)
return execute()
}, [execute])
@@ -105,10 +107,10 @@ export const useUserLikes = (initialParams?: GetUserLikesParams) => {
likes: data?.likes || [],
loading,
loadingMore,
hasMore,
error,
execute,
refetch,
loadMore,
hasMore: hasMoreRef.current,
}
}