fix: bug
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
|||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
|
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||||
|
|
||||||
import { TitleBar, HeroSlider, TabNavigation, TemplateCard } from '@/components/blocks/home'
|
import { TitleBar, HeroSlider, TabNavigation, TemplateCard, TemplateGrid } from '@/components/blocks/home'
|
||||||
import ErrorState from '@/components/ErrorState'
|
import ErrorState from '@/components/ErrorState'
|
||||||
import LoadingState from '@/components/LoadingState'
|
import LoadingState from '@/components/LoadingState'
|
||||||
import { useActivates } from '@/hooks/use-activates'
|
import { useActivates } from '@/hooks/use-activates'
|
||||||
@@ -23,6 +23,8 @@ import { useStickyTabs } from '@/hooks/use-sticky-tabs'
|
|||||||
import { useTabNavigation } from '@/hooks/use-tab-navigation'
|
import { useTabNavigation } from '@/hooks/use-tab-navigation'
|
||||||
import { useTemplateFilter } from '@/hooks/use-template-filter'
|
import { useTemplateFilter } from '@/hooks/use-template-filter'
|
||||||
import { useUserBalance } from '@/hooks/use-user-balance'
|
import { useUserBalance } from '@/hooks/use-user-balance'
|
||||||
|
import { useTemplateLike, useTemplateFavorite } from '@/hooks'
|
||||||
|
import { useTemplateSocialStore } from '@/stores/templateSocialStore'
|
||||||
|
|
||||||
const NUM_COLUMNS = 3
|
const NUM_COLUMNS = 3
|
||||||
const HORIZONTAL_PADDING = 16
|
const HORIZONTAL_PADDING = 16
|
||||||
@@ -151,6 +153,37 @@ export default function HomeScreen() {
|
|||||||
params: { id },
|
params: { id },
|
||||||
}), [router])
|
}), [router])
|
||||||
|
|
||||||
|
// 使用 Store 中的点赞/收藏状态
|
||||||
|
const { setLiked, setFavorited } = useTemplateSocialStore()
|
||||||
|
|
||||||
|
// 点赞处理
|
||||||
|
const handleLike = useCallback((id: string) => {
|
||||||
|
const currentState = setLiked(id, true) // 乐观更新
|
||||||
|
// TODO: 调用 API
|
||||||
|
console.log('Like template:', id)
|
||||||
|
}, [setLiked])
|
||||||
|
|
||||||
|
// 取消点赞处理
|
||||||
|
const handleUnlike = useCallback((id: string) => {
|
||||||
|
const currentState = setLiked(id, false) // 乐观更新
|
||||||
|
// TODO: 调用 API
|
||||||
|
console.log('Unlike template:', id)
|
||||||
|
}, [setLiked])
|
||||||
|
|
||||||
|
// 收藏处理
|
||||||
|
const handleFavorite = useCallback((id: string) => {
|
||||||
|
const currentState = setFavorited(id, true) // 乐观更新
|
||||||
|
// TODO: 调用 API
|
||||||
|
console.log('Favorite template:', id)
|
||||||
|
}, [setFavorited])
|
||||||
|
|
||||||
|
// 取消收藏处理
|
||||||
|
const handleUnfavorite = useCallback((id: string) => {
|
||||||
|
const currentState = setFavorited(id, false) // 乐观更新
|
||||||
|
// TODO: 调用 API
|
||||||
|
console.log('Unfavorite template:', id)
|
||||||
|
}, [setFavorited])
|
||||||
|
|
||||||
// 渲染模板卡片
|
// 渲染模板卡片
|
||||||
const renderTemplateItem = useCallback(({ item }: { item: typeof filteredTemplates[0] }) => {
|
const renderTemplateItem = useCallback(({ item }: { item: typeof filteredTemplates[0] }) => {
|
||||||
if (!item.id) return null
|
if (!item.id) return null
|
||||||
@@ -167,9 +200,13 @@ export default function HomeScreen() {
|
|||||||
onPress={handleTemplatePress}
|
onPress={handleTemplatePress}
|
||||||
liked={'isLiked' in item ? item.isLiked : undefined}
|
liked={'isLiked' in item ? item.isLiked : undefined}
|
||||||
favorited={'isFavorited' in item ? item.isFavorited : undefined}
|
favorited={'isFavorited' in item ? item.isFavorited : undefined}
|
||||||
|
onLike={handleLike}
|
||||||
|
onUnlike={handleUnlike}
|
||||||
|
onFavorite={handleFavorite}
|
||||||
|
onUnfavorite={handleUnfavorite}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}, [handleTemplatePress])
|
}, [handleTemplatePress, handleLike, handleUnlike, handleFavorite, handleUnfavorite])
|
||||||
|
|
||||||
// 提取 key
|
// 提取 key
|
||||||
const keyExtractor = useCallback((item: typeof filteredTemplates[0]) => item.id || '', [])
|
const keyExtractor = useCallback((item: typeof filteredTemplates[0]) => item.id || '', [])
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { TitleBar, TemplateCard } from '@/components/blocks/home'
|
|||||||
import ErrorState from '@/components/ErrorState'
|
import ErrorState from '@/components/ErrorState'
|
||||||
import LoadingState from '@/components/LoadingState'
|
import LoadingState from '@/components/LoadingState'
|
||||||
import { useUserFavorites } from '@/hooks/use-user-favorites'
|
import { useUserFavorites } from '@/hooks/use-user-favorites'
|
||||||
|
import { useTemplateSocialStore } from '@/stores/templateSocialStore'
|
||||||
|
|
||||||
const NUM_COLUMNS = 2
|
const NUM_COLUMNS = 2
|
||||||
const HORIZONTAL_PADDING = 16
|
const HORIZONTAL_PADDING = 16
|
||||||
@@ -34,6 +35,9 @@ export default function FavoritesScreen() {
|
|||||||
const insets = useSafeAreaInsets()
|
const insets = useSafeAreaInsets()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
// 使用 Store 中的点赞/收藏状态
|
||||||
|
const { setLiked, setFavorited } = useTemplateSocialStore()
|
||||||
|
|
||||||
// 获取收藏列表
|
// 获取收藏列表
|
||||||
const {
|
const {
|
||||||
favorites,
|
favorites,
|
||||||
@@ -82,6 +86,28 @@ export default function FavoritesScreen() {
|
|||||||
})
|
})
|
||||||
}, [router])
|
}, [router])
|
||||||
|
|
||||||
|
// 点赞/取消点赞处理
|
||||||
|
const handleLike = useCallback((id: string) => {
|
||||||
|
setLiked(id, true)
|
||||||
|
console.log('Like template:', id)
|
||||||
|
}, [setLiked])
|
||||||
|
|
||||||
|
const handleUnlike = useCallback((id: string) => {
|
||||||
|
setLiked(id, false)
|
||||||
|
console.log('Unlike template:', id)
|
||||||
|
}, [setLiked])
|
||||||
|
|
||||||
|
// 收藏/取消收藏处理
|
||||||
|
const handleFavorite = useCallback((id: string) => {
|
||||||
|
setFavorited(id, true)
|
||||||
|
console.log('Favorite template:', id)
|
||||||
|
}, [setFavorited])
|
||||||
|
|
||||||
|
const handleUnfavorite = useCallback((id: string) => {
|
||||||
|
setFavorited(id, false)
|
||||||
|
console.log('Unfavorite template:', id)
|
||||||
|
}, [setFavorited])
|
||||||
|
|
||||||
// 状态判断
|
// 状态判断
|
||||||
const isLoading = useMemo(() => loading, [loading])
|
const isLoading = useMemo(() => loading, [loading])
|
||||||
const showEmptyState = useMemo(() =>
|
const showEmptyState = useMemo(() =>
|
||||||
@@ -111,9 +137,13 @@ export default function FavoritesScreen() {
|
|||||||
onPress={handleTemplatePress}
|
onPress={handleTemplatePress}
|
||||||
liked={template.isLiked}
|
liked={template.isLiked}
|
||||||
favorited={template.isFavorited}
|
favorited={template.isFavorited}
|
||||||
|
onLike={handleLike}
|
||||||
|
onUnlike={handleUnlike}
|
||||||
|
onFavorite={handleFavorite}
|
||||||
|
onUnfavorite={handleUnfavorite}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}, [handleTemplatePress])
|
}, [handleTemplatePress, handleLike, handleUnlike, handleFavorite, handleUnfavorite])
|
||||||
|
|
||||||
// 提取 key
|
// 提取 key
|
||||||
const keyExtractor = useCallback((item: typeof favorites[0]) => item.id, [])
|
const keyExtractor = useCallback((item: typeof favorites[0]) => item.id, [])
|
||||||
@@ -17,6 +17,7 @@ import { TemplateCard } from '@/components/blocks/home'
|
|||||||
import ErrorState from '@/components/ErrorState'
|
import ErrorState from '@/components/ErrorState'
|
||||||
import LoadingState from '@/components/LoadingState'
|
import LoadingState from '@/components/LoadingState'
|
||||||
import { useUserLikes } from '@/hooks'
|
import { useUserLikes } from '@/hooks'
|
||||||
|
import { useTemplateSocialStore } from '@/stores/templateSocialStore'
|
||||||
|
|
||||||
const NUM_COLUMNS = 2
|
const NUM_COLUMNS = 2
|
||||||
const HORIZONTAL_PADDING = 16
|
const HORIZONTAL_PADDING = 16
|
||||||
@@ -35,6 +36,9 @@ export default function LikesScreen() {
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [refreshing, setRefreshing] = useState(false)
|
const [refreshing, setRefreshing] = useState(false)
|
||||||
|
|
||||||
|
// 使用 Store 中的点赞/收藏状态
|
||||||
|
const { setLiked, setFavorited } = useTemplateSocialStore()
|
||||||
|
|
||||||
// 获取用户点赞列表
|
// 获取用户点赞列表
|
||||||
const {
|
const {
|
||||||
likes,
|
likes,
|
||||||
@@ -74,6 +78,28 @@ export default function LikesScreen() {
|
|||||||
})
|
})
|
||||||
}, [router])
|
}, [router])
|
||||||
|
|
||||||
|
// 点赞/取消点赞处理
|
||||||
|
const handleLike = useCallback((id: string) => {
|
||||||
|
setLiked(id, true)
|
||||||
|
console.log('Like template:', id)
|
||||||
|
}, [setLiked])
|
||||||
|
|
||||||
|
const handleUnlike = useCallback((id: string) => {
|
||||||
|
setLiked(id, false)
|
||||||
|
console.log('Unlike template:', id)
|
||||||
|
}, [setLiked])
|
||||||
|
|
||||||
|
// 收藏/取消收藏处理
|
||||||
|
const handleFavorite = useCallback((id: string) => {
|
||||||
|
setFavorited(id, true)
|
||||||
|
console.log('Favorite template:', id)
|
||||||
|
}, [setFavorited])
|
||||||
|
|
||||||
|
const handleUnfavorite = useCallback((id: string) => {
|
||||||
|
setFavorited(id, false)
|
||||||
|
console.log('Unfavorite template:', id)
|
||||||
|
}, [setFavorited])
|
||||||
|
|
||||||
// 状态判断
|
// 状态判断
|
||||||
const showEmptyState = useMemo(() =>
|
const showEmptyState = useMemo(() =>
|
||||||
!loading && !error && likes.length === 0,
|
!loading && !error && likes.length === 0,
|
||||||
@@ -107,9 +133,13 @@ export default function LikesScreen() {
|
|||||||
onPress={handleTemplatePress}
|
onPress={handleTemplatePress}
|
||||||
liked={item.template.isLiked}
|
liked={item.template.isLiked}
|
||||||
favorited={item.template.isFavorited}
|
favorited={item.template.isFavorited}
|
||||||
|
onLike={handleLike}
|
||||||
|
onUnlike={handleUnlike}
|
||||||
|
onFavorite={handleFavorite}
|
||||||
|
onUnfavorite={handleUnfavorite}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}, [handleTemplatePress])
|
}, [handleTemplatePress, handleLike, handleUnlike, handleFavorite, handleUnfavorite])
|
||||||
|
|
||||||
// 提取 key
|
// 提取 key
|
||||||
const keyExtractor = useCallback((item: typeof likes[0]) => item.id || '', [])
|
const keyExtractor = useCallback((item: typeof likes[0]) => item.id || '', [])
|
||||||
@@ -4,6 +4,7 @@ import { Image } from 'expo-image'
|
|||||||
import { LinearGradient } from 'expo-linear-gradient'
|
import { LinearGradient } from 'expo-linear-gradient'
|
||||||
import { Ionicons } from '@expo/vector-icons'
|
import { Ionicons } from '@expo/vector-icons'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useTemplateSocialStore } from '@/stores/templateSocialStore'
|
||||||
|
|
||||||
export interface TemplateCardProps {
|
export interface TemplateCardProps {
|
||||||
id?: string
|
id?: string
|
||||||
@@ -65,8 +66,8 @@ const TemplateCardComponent: React.FC<TemplateCardProps> = ({
|
|||||||
aspectRatio: aspectRatioString,
|
aspectRatio: aspectRatioString,
|
||||||
cardWidth,
|
cardWidth,
|
||||||
onPress,
|
onPress,
|
||||||
liked,
|
liked: likedProp,
|
||||||
favorited,
|
favorited: favoritedProp,
|
||||||
onLike,
|
onLike,
|
||||||
onUnlike,
|
onUnlike,
|
||||||
onFavorite,
|
onFavorite,
|
||||||
@@ -74,6 +75,14 @@ const TemplateCardComponent: React.FC<TemplateCardProps> = ({
|
|||||||
testID,
|
testID,
|
||||||
}) => {
|
}) => {
|
||||||
const { i18n } = useTranslation()
|
const { i18n } = useTranslation()
|
||||||
|
|
||||||
|
// 获取 Store 中的状态(用于本地状态覆盖)
|
||||||
|
const { isLiked: isLikedInStore, isFavorited: isFavoritedInStore } = useTemplateSocialStore()
|
||||||
|
|
||||||
|
// 合并 props 状态和 store 状态:store 优先(乐观更新)
|
||||||
|
const liked = id !== undefined ? isLikedInStore(id) ?? likedProp : likedProp
|
||||||
|
const favorited = id !== undefined ? isFavoritedInStore(id) ?? favoritedProp : favoritedProp
|
||||||
|
|
||||||
const aspectRatio = useMemo(() => parseAspectRatio(aspectRatioString), [aspectRatioString])
|
const aspectRatio = useMemo(() => parseAspectRatio(aspectRatioString), [aspectRatioString])
|
||||||
const imageUri = useMemo(() => getImageUri(webpPreviewUrl, previewUrl, coverImageUrl), [webpPreviewUrl, previewUrl, coverImageUrl])
|
const imageUri = useMemo(() => getImageUri(webpPreviewUrl, previewUrl, coverImageUrl), [webpPreviewUrl, previewUrl, coverImageUrl])
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ export type Template = CategoryTemplate | TemplateDetail
|
|||||||
export interface TemplateGridProps {
|
export interface TemplateGridProps {
|
||||||
templates: Template[]
|
templates: Template[]
|
||||||
onTemplatePress: (id: string) => void
|
onTemplatePress: (id: string) => void
|
||||||
|
onLike?: (id: string) => void
|
||||||
|
onUnlike?: (id: string) => void
|
||||||
|
onFavorite?: (id: string) => void
|
||||||
|
onUnfavorite?: (id: string) => void
|
||||||
numColumns?: number
|
numColumns?: number
|
||||||
horizontalPadding?: number
|
horizontalPadding?: number
|
||||||
cardGap?: number
|
cardGap?: number
|
||||||
@@ -34,6 +38,10 @@ export function calculateCardWidth(
|
|||||||
const TemplateGridComponent: React.FC<TemplateGridProps> = ({
|
const TemplateGridComponent: React.FC<TemplateGridProps> = ({
|
||||||
templates,
|
templates,
|
||||||
onTemplatePress,
|
onTemplatePress,
|
||||||
|
onLike,
|
||||||
|
onUnlike,
|
||||||
|
onFavorite,
|
||||||
|
onUnfavorite,
|
||||||
numColumns = 3,
|
numColumns = 3,
|
||||||
horizontalPadding = 16,
|
horizontalPadding = 16,
|
||||||
cardGap = 5,
|
cardGap = 5,
|
||||||
@@ -75,6 +83,10 @@ const TemplateGridComponent: React.FC<TemplateGridProps> = ({
|
|||||||
onPress={onTemplatePress}
|
onPress={onTemplatePress}
|
||||||
liked={'isLiked' in template ? template.isLiked : undefined}
|
liked={'isLiked' in template ? template.isLiked : undefined}
|
||||||
favorited={'isFavorited' in template ? template.isFavorited : undefined}
|
favorited={'isFavorited' in template ? template.isFavorited : undefined}
|
||||||
|
onLike={onLike}
|
||||||
|
onUnlike={onUnlike}
|
||||||
|
onFavorite={onFavorite}
|
||||||
|
onUnfavorite={onUnfavorite}
|
||||||
testID={`template-card-${template.id}`}
|
testID={`template-card-${template.id}`}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ const FavoriteButtonComponent: React.FC<FavoriteButtonProps> = ({
|
|||||||
}
|
}
|
||||||
}, [loading, onPress])
|
}, [loading, onPress])
|
||||||
|
|
||||||
// 缓存样式计算
|
// 缓存样式计算 - 移除不必要的 iconStyle
|
||||||
const iconStyle = useMemo(() => [styles.icon, { fontSize: size }], [size])
|
|
||||||
const containerStyle = useMemo(
|
const containerStyle = useMemo(
|
||||||
() => [styles.container, loading && styles.disabled],
|
() => [styles.container, loading && styles.disabled],
|
||||||
[loading]
|
[loading]
|
||||||
@@ -45,7 +44,7 @@ const FavoriteButtonComponent: React.FC<FavoriteButtonProps> = ({
|
|||||||
style={containerStyle}
|
style={containerStyle}
|
||||||
>
|
>
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
<Ionicons name={iconName} size={size} color={iconColor} style={iconStyle} />
|
<Ionicons name={iconName} size={size} color={iconColor} />
|
||||||
{count !== undefined && (
|
{count !== undefined && (
|
||||||
<Text style={[styles.count, { fontSize: size * 0.7 }]}>{count}</Text>
|
<Text style={[styles.count, { fontSize: size * 0.7 }]}>{count}</Text>
|
||||||
)}
|
)}
|
||||||
@@ -70,9 +69,6 @@ const styles = StyleSheet.create({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: 4,
|
gap: 4,
|
||||||
},
|
},
|
||||||
icon: {
|
|
||||||
lineHeight: (size: number) => size,
|
|
||||||
},
|
|
||||||
count: {
|
count: {
|
||||||
color: '#8E8E93',
|
color: '#8E8E93',
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
|
|||||||
Reference in New Issue
Block a user