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

View File

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

View File

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

View File

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

View File

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

View File

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