fix: bug
This commit is contained in:
@@ -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>
|
||||
<MessageCard
|
||||
message={item}
|
||||
onPress={handleMessagePress}
|
||||
/>
|
||||
), [handleDeleteMessage, handleMessagePress])
|
||||
|
||||
// Render list footer
|
||||
@@ -288,6 +286,7 @@ const styles = StyleSheet.create({
|
||||
listContent: {
|
||||
paddingHorizontal: 4,
|
||||
paddingTop: 12,
|
||||
paddingBottom: 100,
|
||||
},
|
||||
emptyListContent: {
|
||||
flex: 1,
|
||||
|
||||
@@ -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,81 +288,169 @@ 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" />
|
||||
|
||||
<StatusBar style="light" />
|
||||
<RNStatusBar barStyle="light-content" />
|
||||
|
||||
{/* 顶部积分与设置 */}
|
||||
<View style={styles.topBar}>
|
||||
<Pressable
|
||||
style={styles.pointsPill}
|
||||
onPress={() => router.push('/membership' as any)}
|
||||
>
|
||||
<PointsIcon />
|
||||
<Text style={styles.pointsPillText}>{balance}</Text>
|
||||
</Pressable>
|
||||
<Dropdown
|
||||
options={settingsOptions}
|
||||
onSelect={(value) => handleSettingsSelect(value)}
|
||||
renderTrigger={(selectedOption, isOpen, toggle) => (
|
||||
<Pressable onPress={toggle}>
|
||||
<SettingsIcon />
|
||||
</Pressable>
|
||||
)}
|
||||
dropdownStyle={{
|
||||
minWidth: 160,
|
||||
right: 10,
|
||||
backgroundColor: '#2A2A2A80',
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* 个人信息区 */}
|
||||
<View style={styles.profileSection}>
|
||||
<Image
|
||||
source={session?.user?.image ? { uri: session.user.image } : require('@/assets/images/icon.png')}
|
||||
style={styles.avatar}
|
||||
contentFit="cover"
|
||||
/>
|
||||
<View style={styles.profileInfo}>
|
||||
<Text style={styles.profileName}>{profileName}</Text>
|
||||
<Text style={styles.profileSubTitle}>ID {session?.user?.id?.slice(0, 8) || '--------'}</Text>
|
||||
</View>
|
||||
<Pressable
|
||||
style={styles.editButton}
|
||||
onPress={() => {
|
||||
console.log('[my.tsx] Edit Profile button pressed')
|
||||
setEditDrawerVisible(true)
|
||||
}}
|
||||
>
|
||||
<Text style={styles.editButtonText}>{t('my.editProfile')}</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
{/* Tab 导航 */}
|
||||
<TabNavigation
|
||||
tabs={tabs}
|
||||
activeIndex={activeTabIndex}
|
||||
onTabPress={handleTabPress}
|
||||
{/* 顶部积分与设置 */}
|
||||
<View style={styles.topBar}>
|
||||
<Pressable
|
||||
style={styles.pointsPill}
|
||||
onPress={() => router.push('/membership' as any)}
|
||||
>
|
||||
<PointsIcon />
|
||||
<Text style={styles.pointsPillText}>{balance}</Text>
|
||||
</Pressable>
|
||||
<Dropdown
|
||||
options={settingsOptions}
|
||||
onSelect={(value) => handleSettingsSelect(value)}
|
||||
renderTrigger={(selectedOption, isOpen, toggle) => (
|
||||
<Pressable onPress={toggle}>
|
||||
<SettingsIcon />
|
||||
</Pressable>
|
||||
)}
|
||||
dropdownStyle={{
|
||||
minWidth: 160,
|
||||
right: 10,
|
||||
backgroundColor: '#2A2A2A80',
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* 作品九宫格 */}
|
||||
{activeTab === 'works' && loading ? (
|
||||
{/* 个人信息区 */}
|
||||
<View style={styles.profileSection}>
|
||||
<Image
|
||||
source={session?.user?.image ? { uri: session.user.image } : require('@/assets/images/icon.png')}
|
||||
style={styles.avatar}
|
||||
contentFit="cover"
|
||||
/>
|
||||
<View style={styles.profileInfo}>
|
||||
<Text style={styles.profileName}>{profileName}</Text>
|
||||
<Text style={styles.profileSubTitle}>ID {session?.user?.id?.slice(0, 8) || '--------'}</Text>
|
||||
</View>
|
||||
<Pressable
|
||||
style={styles.editButton}
|
||||
onPress={() => {
|
||||
console.log('[my.tsx] Edit Profile button pressed')
|
||||
setEditDrawerVisible(true)
|
||||
}}
|
||||
>
|
||||
<Text style={styles.editButtonText}>{t('my.editProfile')}</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
{/* Tab 导航 */}
|
||||
<TabNavigation
|
||||
tabs={tabs}
|
||||
activeIndex={activeTabIndex}
|
||||
onTabPress={handleTabPress}
|
||||
/>
|
||||
|
||||
{/* 作品九宫格 */}
|
||||
{activeTab === 'works' && loading ? (
|
||||
<MySkeleton />
|
||||
) : activeTab === 'works' ? (
|
||||
<ScrollView
|
||||
testID="my-scroll-view"
|
||||
style={styles.scrollView}
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
showsVerticalScrollIndicator={false}
|
||||
onScroll={handleScroll}
|
||||
scrollEventThrottle={16}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
tintColor="#9966FF"
|
||||
colors={['#9966FF', '#FF6699', '#FF9966']}
|
||||
progressBackgroundColor="#1C1E22"
|
||||
progressViewOffset={10}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<View style={styles.galleryGrid}>
|
||||
{generations.map((item, index) => (
|
||||
<Pressable
|
||||
key={item.id}
|
||||
style={[
|
||||
styles.galleryItem,
|
||||
index % 3 !== 2 && styles.galleryItemMarginRight,
|
||||
styles.galleryItemMarginBottom,
|
||||
]}
|
||||
onPress={() => {
|
||||
if (item.status === 'completed') {
|
||||
router.push({
|
||||
pathname: '/generationRecord' as any,
|
||||
params: { id: item.id },
|
||||
})
|
||||
}
|
||||
}}
|
||||
disabled={item.status !== 'completed'}
|
||||
>
|
||||
<Image
|
||||
source={getCoverUrl(item) ? { uri: getCoverUrl(item) } : require('@/assets/images/membership.png')}
|
||||
style={styles.galleryImage}
|
||||
contentFit="cover"
|
||||
/>
|
||||
|
||||
{/* 遮罩:非完成状态 */}
|
||||
{item.status !== 'completed' && (
|
||||
<View style={styles.generatingOverlay} />
|
||||
)}
|
||||
|
||||
{/* 数量角标:已完成且有结果 */}
|
||||
{item.status === 'completed' && (item.resultUrl?.length || 0) > 0 && (
|
||||
<View style={styles.counterBadge}>
|
||||
<Text style={styles.counterText}>
|
||||
{item.resultUrl?.length || 1}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 状态角标 */}
|
||||
{item.status === 'running' && (
|
||||
<View style={styles.generatingBadge}>
|
||||
<Text style={styles.generatingBadgeText}>{t('my.generating')}</Text>
|
||||
</View>
|
||||
)}
|
||||
{item.status === 'pending' && (
|
||||
<View style={styles.generatingBadge}>
|
||||
<Text style={styles.generatingBadgeText}>{t('my.queuing')}</Text>
|
||||
</View>
|
||||
)}
|
||||
</Pressable>
|
||||
))}
|
||||
|
||||
{/* 加载更多指示器 */}
|
||||
{loadingMore && (
|
||||
<View style={styles.loadingMoreContainer}>
|
||||
<ActivityIndicator size="small" color="#9966FF" />
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 空状态提示 */}
|
||||
{!loading && generations.length === 0 && (
|
||||
<View style={styles.emptyState}>
|
||||
<Text style={styles.emptyStateText}>
|
||||
{t('my.noWorks')}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
) : activeTab === 'favorites' && favoritesLoading ? (
|
||||
<MySkeleton />
|
||||
) : activeTab === 'works' ? (
|
||||
) : activeTab === 'favorites' ? (
|
||||
<ScrollView
|
||||
testID="my-scroll-view"
|
||||
style={styles.scrollView}
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
showsVerticalScrollIndicator={false}
|
||||
onScroll={handleScroll}
|
||||
onScroll={handleFavoritesScroll}
|
||||
scrollEventThrottle={16}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
refreshing={favoritesRefreshing}
|
||||
onRefresh={onFavoritesRefresh}
|
||||
tintColor="#9966FF"
|
||||
colors={['#9966FF', '#FF6699', '#FF9966']}
|
||||
progressBackgroundColor="#1C1E22"
|
||||
@@ -314,107 +459,37 @@ export default function My() {
|
||||
}
|
||||
>
|
||||
<View style={styles.galleryGrid}>
|
||||
{generations.map((item, index) => (
|
||||
<Pressable
|
||||
key={item.id}
|
||||
style={[
|
||||
styles.galleryItem,
|
||||
index % 3 !== 2 && styles.galleryItemMarginRight,
|
||||
styles.galleryItemMarginBottom,
|
||||
]}
|
||||
onPress={() => {
|
||||
if (item.status === 'completed') {
|
||||
router.push({
|
||||
pathname: '/generationRecord' as any,
|
||||
params: { id: item.id },
|
||||
})
|
||||
}
|
||||
}}
|
||||
disabled={item.status !== 'completed'}
|
||||
>
|
||||
<Image
|
||||
source={getCoverUrl(item) ? { uri: getCoverUrl(item) } : require('@/assets/images/membership.png')}
|
||||
style={styles.galleryImage}
|
||||
contentFit="cover"
|
||||
/>
|
||||
|
||||
{/* 遮罩:非完成状态 */}
|
||||
{item.status !== 'completed' && (
|
||||
<View style={styles.generatingOverlay} />
|
||||
)}
|
||||
|
||||
{/* 数量角标:已完成且有结果 */}
|
||||
{item.status === 'completed' && (item.resultUrl?.length || 0) > 0 && (
|
||||
<View style={styles.counterBadge}>
|
||||
<Text style={styles.counterText}>
|
||||
{item.resultUrl?.length || 1}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 状态角标 */}
|
||||
{item.status === 'running' && (
|
||||
<View style={styles.generatingBadge}>
|
||||
<Text style={styles.generatingBadgeText}>{t('my.generating')}</Text>
|
||||
</View>
|
||||
)}
|
||||
{item.status === 'pending' && (
|
||||
<View style={styles.generatingBadge}>
|
||||
<Text style={styles.generatingBadgeText}>{t('my.queuing')}</Text>
|
||||
</View>
|
||||
)}
|
||||
</Pressable>
|
||||
))}
|
||||
|
||||
{/* 加载更多指示器 */}
|
||||
{loadingMore && (
|
||||
<View style={styles.loadingMoreContainer}>
|
||||
<ActivityIndicator size="small" color="#9966FF" />
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 空状态提示 */}
|
||||
{!loading && generations.length === 0 && (
|
||||
<View style={styles.emptyState}>
|
||||
<Text style={styles.emptyStateText}>
|
||||
{t('my.noWorks')}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
) : activeTab === 'favorites' && favoritesLoading ? (
|
||||
<MySkeleton />
|
||||
) : activeTab === 'favorites' ? (
|
||||
<ScrollView
|
||||
style={styles.scrollView}
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
<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) => (
|
||||
<Pressable
|
||||
key={item.id}
|
||||
style={[
|
||||
styles.galleryItem,
|
||||
index % 3 !== 2 && styles.galleryItemMarginRight,
|
||||
styles.galleryItemMarginBottom,
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
source={item.template?.coverImageUrl ? { uri: item.template.coverImageUrl } : require('@/assets/images/membership.png')}
|
||||
style={styles.galleryImage}
|
||||
contentFit="cover"
|
||||
/>
|
||||
</Pressable>
|
||||
))
|
||||
<>
|
||||
{favorites.map((item, index) => (
|
||||
<Pressable
|
||||
key={item.id}
|
||||
style={[
|
||||
styles.galleryItem,
|
||||
index % 3 !== 2 && styles.galleryItemMarginRight,
|
||||
styles.galleryItemMarginBottom,
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
source={item.template?.coverImageUrl ? { uri: item.template.coverImageUrl } : require('@/assets/images/membership.png')}
|
||||
style={styles.galleryImage}
|
||||
contentFit="cover"
|
||||
/>
|
||||
</Pressable>
|
||||
))}
|
||||
{/* 加载更多指示器 */}
|
||||
{favoritesLoadingMore && (
|
||||
<View style={styles.loadingMoreContainer}>
|
||||
<ActivityIndicator size="small" color="#9966FF" />
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
@@ -425,48 +500,66 @@ 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) => (
|
||||
<Pressable
|
||||
key={item.id}
|
||||
style={[
|
||||
styles.galleryItem,
|
||||
index % 3 !== 2 && styles.galleryItemMarginRight,
|
||||
styles.galleryItemMarginBottom,
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
source={item.template?.coverImageUrl ? { uri: item.template.coverImageUrl } : require('@/assets/images/membership.png')}
|
||||
style={styles.galleryImage}
|
||||
contentFit="cover"
|
||||
/>
|
||||
</Pressable>
|
||||
))
|
||||
<>
|
||||
{likes.map((item, index) => (
|
||||
<Pressable
|
||||
key={item.id}
|
||||
style={[
|
||||
styles.galleryItem,
|
||||
index % 3 !== 2 && styles.galleryItemMarginRight,
|
||||
styles.galleryItemMarginBottom,
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
source={item.template?.coverImageUrl ? { uri: item.template.coverImageUrl } : require('@/assets/images/membership.png')}
|
||||
style={styles.galleryImage}
|
||||
contentFit="cover"
|
||||
/>
|
||||
</Pressable>
|
||||
))}
|
||||
{/* 加载更多指示器 */}
|
||||
{likesLoadingMore && (
|
||||
<View style={styles.loadingMoreContainer}>
|
||||
<ActivityIndicator size="small" color="#9966FF" />
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
) : null}
|
||||
|
||||
{/* 编辑资料抽屉 */}
|
||||
|
||||
<EditProfileDrawer
|
||||
visible={editDrawerVisible}
|
||||
onClose={() => setEditDrawerVisible(false)}
|
||||
initialName={profileName}
|
||||
initialAvatar={session?.user?.image}
|
||||
onSave={(data) => {
|
||||
setProfileName(data.name)
|
||||
}}
|
||||
/>
|
||||
</KeyboardAwareScrollView>
|
||||
{/* 编辑资料抽屉 */}
|
||||
<EditProfileDrawer
|
||||
visible={editDrawerVisible}
|
||||
onClose={() => setEditDrawerVisible(false)}
|
||||
initialName={profileName}
|
||||
initialAvatar={session?.user?.image}
|
||||
onSave={(data) => {
|
||||
setProfileName(data.name)
|
||||
}}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
@@ -506,7 +599,7 @@ const styles = StyleSheet.create({
|
||||
scrollContent: {
|
||||
backgroundColor: '#090A0B',
|
||||
paddingHorizontal: GALLERY_HORIZONTAL_PADDING,
|
||||
paddingBottom: 20,
|
||||
paddingBottom: 100,
|
||||
},
|
||||
profileSection: {
|
||||
flexDirection: 'row',
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user