fix: bug
This commit is contained in:
@@ -25,6 +25,9 @@ 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 { useTemplateLike, useTemplateFavorite } from '@/hooks'
|
||||||
import { useTemplateSocialStore } from '@/stores/templateSocialStore'
|
import { useTemplateSocialStore } from '@/stores/templateSocialStore'
|
||||||
|
import { root } from '@repo/core'
|
||||||
|
import { TemplateSocialController } from '@repo/sdk'
|
||||||
|
import { handleError } from '@/hooks/use-error'
|
||||||
|
|
||||||
const NUM_COLUMNS = 3
|
const NUM_COLUMNS = 3
|
||||||
const HORIZONTAL_PADDING = 16
|
const HORIZONTAL_PADDING = 16
|
||||||
@@ -108,6 +111,21 @@ export default function HomeScreen() {
|
|||||||
}
|
}
|
||||||
}, [params.categoryId])
|
}, [params.categoryId])
|
||||||
|
|
||||||
|
// 同步模板的点赞数量到 store(用于初始化 store 中的数据)
|
||||||
|
useEffect(() => {
|
||||||
|
if (filteredTemplates.length > 0) {
|
||||||
|
const likeCountMap: Record<string, number> = {}
|
||||||
|
filteredTemplates.forEach(template => {
|
||||||
|
if (template.id && 'likeCount' in template && template.likeCount !== undefined) {
|
||||||
|
likeCountMap[template.id] = template.likeCount
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (Object.keys(likeCountMap).length > 0) {
|
||||||
|
setLikeCountStates(likeCountMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [filteredTemplates, setLikeCountStates])
|
||||||
|
|
||||||
// 状态判断 - 使用 useMemo 缓存
|
// 状态判断 - 使用 useMemo 缓存
|
||||||
// 统一 loading 状态:只要有任何一个在加载,就显示 loading
|
// 统一 loading 状态:只要有任何一个在加载,就显示 loading
|
||||||
const isLoading = useMemo(() =>
|
const isLoading = useMemo(() =>
|
||||||
@@ -154,35 +172,72 @@ export default function HomeScreen() {
|
|||||||
}), [router])
|
}), [router])
|
||||||
|
|
||||||
// 使用 Store 中的点赞/收藏状态
|
// 使用 Store 中的点赞/收藏状态
|
||||||
const { setLiked, setFavorited } = useTemplateSocialStore()
|
const { setLiked, setFavorited, incrementLikeCount, decrementLikeCount, setLikeCountStates } = useTemplateSocialStore()
|
||||||
|
|
||||||
|
// 获取 social controller
|
||||||
|
const getSocialController = useCallback(() => {
|
||||||
|
return root.get(TemplateSocialController)
|
||||||
|
}, [])
|
||||||
|
|
||||||
// 点赞处理
|
// 点赞处理
|
||||||
const handleLike = useCallback((id: string) => {
|
const handleLike = useCallback(async (id: string) => {
|
||||||
const currentState = setLiked(id, true) // 乐观更新
|
setLiked(id, true) // 乐观更新状态
|
||||||
// TODO: 调用 API
|
incrementLikeCount(id) // 乐观更新数量
|
||||||
console.log('Like template:', id)
|
try {
|
||||||
}, [setLiked])
|
const social = getSocialController()
|
||||||
|
await handleError(() => social.like({ templateId: id }))
|
||||||
|
console.log('Liked template:', id)
|
||||||
|
} catch (e) {
|
||||||
|
// 失败时回滚状态
|
||||||
|
setLiked(id, false)
|
||||||
|
decrementLikeCount(id)
|
||||||
|
console.error('Like failed:', e)
|
||||||
|
}
|
||||||
|
}, [setLiked, incrementLikeCount, decrementLikeCount, getSocialController])
|
||||||
|
|
||||||
// 取消点赞处理
|
// 取消点赞处理
|
||||||
const handleUnlike = useCallback((id: string) => {
|
const handleUnlike = useCallback(async (id: string) => {
|
||||||
const currentState = setLiked(id, false) // 乐观更新
|
setLiked(id, false) // 乐观更新状态
|
||||||
// TODO: 调用 API
|
decrementLikeCount(id) // 乐观更新数量
|
||||||
console.log('Unlike template:', id)
|
try {
|
||||||
}, [setLiked])
|
const social = getSocialController()
|
||||||
|
await handleError(() => social.unlike({ templateId: id }))
|
||||||
|
console.log('Unliked template:', id)
|
||||||
|
} catch (e) {
|
||||||
|
// 失败时回滚状态
|
||||||
|
setLiked(id, true)
|
||||||
|
incrementLikeCount(id)
|
||||||
|
console.error('Unlike failed:', e)
|
||||||
|
}
|
||||||
|
}, [setLiked, incrementLikeCount, decrementLikeCount, getSocialController])
|
||||||
|
|
||||||
// 收藏处理
|
// 收藏处理
|
||||||
const handleFavorite = useCallback((id: string) => {
|
const handleFavorite = useCallback(async (id: string) => {
|
||||||
const currentState = setFavorited(id, true) // 乐观更新
|
setFavorited(id, true) // 乐观更新
|
||||||
// TODO: 调用 API
|
try {
|
||||||
console.log('Favorite template:', id)
|
const social = getSocialController()
|
||||||
}, [setFavorited])
|
await handleError(() => social.favorite({ templateId: id }))
|
||||||
|
console.log('Favorited template:', id)
|
||||||
|
} catch (e) {
|
||||||
|
// 失败时回滚状态
|
||||||
|
setFavorited(id, false)
|
||||||
|
console.error('Favorite failed:', e)
|
||||||
|
}
|
||||||
|
}, [setFavorited, getSocialController])
|
||||||
|
|
||||||
// 取消收藏处理
|
// 取消收藏处理
|
||||||
const handleUnfavorite = useCallback((id: string) => {
|
const handleUnfavorite = useCallback(async (id: string) => {
|
||||||
const currentState = setFavorited(id, false) // 乐观更新
|
setFavorited(id, false) // 乐观更新
|
||||||
// TODO: 调用 API
|
try {
|
||||||
console.log('Unfavorite template:', id)
|
const social = getSocialController()
|
||||||
}, [setFavorited])
|
await handleError(() => social.unfavorite({ templateId: id }))
|
||||||
|
console.log('Unfavorited template:', id)
|
||||||
|
} catch (e) {
|
||||||
|
// 失败时回滚状态
|
||||||
|
setFavorited(id, true)
|
||||||
|
console.error('Unfavorite failed:', e)
|
||||||
|
}
|
||||||
|
}, [setFavorited, getSocialController])
|
||||||
|
|
||||||
// 渲染模板卡片
|
// 渲染模板卡片
|
||||||
const renderTemplateItem = useCallback(({ item }: { item: typeof filteredTemplates[0] }) => {
|
const renderTemplateItem = useCallback(({ item }: { item: typeof filteredTemplates[0] }) => {
|
||||||
@@ -200,6 +255,7 @@ 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}
|
||||||
|
likeCount={'likeCount' in item ? item.likeCount : undefined}
|
||||||
onLike={handleLike}
|
onLike={handleLike}
|
||||||
onUnlike={handleUnlike}
|
onUnlike={handleUnlike}
|
||||||
onFavorite={handleFavorite}
|
onFavorite={handleFavorite}
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ 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'
|
import { useTemplateSocialStore } from '@/stores/templateSocialStore'
|
||||||
|
import { root } from '@repo/core'
|
||||||
|
import { TemplateSocialController } from '@repo/sdk'
|
||||||
|
import { handleError } from '@/hooks/use-error'
|
||||||
|
|
||||||
const NUM_COLUMNS = 2
|
const NUM_COLUMNS = 2
|
||||||
const HORIZONTAL_PADDING = 16
|
const HORIZONTAL_PADDING = 16
|
||||||
@@ -36,7 +39,7 @@ export default function FavoritesScreen() {
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
// 使用 Store 中的点赞/收藏状态
|
// 使用 Store 中的点赞/收藏状态
|
||||||
const { setLiked, setFavorited } = useTemplateSocialStore()
|
const { setLiked, setFavorited, incrementLikeCount, decrementLikeCount } = useTemplateSocialStore()
|
||||||
|
|
||||||
// 获取收藏列表
|
// 获取收藏列表
|
||||||
const {
|
const {
|
||||||
@@ -86,27 +89,56 @@ export default function FavoritesScreen() {
|
|||||||
})
|
})
|
||||||
}, [router])
|
}, [router])
|
||||||
|
|
||||||
// 点赞/取消点赞处理
|
// 获取 social controller
|
||||||
const handleLike = useCallback((id: string) => {
|
const getSocialController = useCallback(() => {
|
||||||
setLiked(id, true)
|
return root.get(TemplateSocialController)
|
||||||
console.log('Like template:', id)
|
}, [])
|
||||||
}, [setLiked])
|
|
||||||
|
|
||||||
const handleUnlike = useCallback((id: string) => {
|
// 点赞/取消点赞处理
|
||||||
|
const handleLike = useCallback(async (id: string) => {
|
||||||
|
setLiked(id, true)
|
||||||
|
incrementLikeCount(id)
|
||||||
|
try {
|
||||||
|
const social = getSocialController()
|
||||||
|
await handleError(() => social.like({ templateId: id }))
|
||||||
|
} catch (e) {
|
||||||
|
setLiked(id, false)
|
||||||
|
decrementLikeCount(id)
|
||||||
|
}
|
||||||
|
}, [setLiked, incrementLikeCount, decrementLikeCount, getSocialController])
|
||||||
|
|
||||||
|
const handleUnlike = useCallback(async (id: string) => {
|
||||||
setLiked(id, false)
|
setLiked(id, false)
|
||||||
console.log('Unlike template:', id)
|
decrementLikeCount(id)
|
||||||
}, [setLiked])
|
try {
|
||||||
|
const social = getSocialController()
|
||||||
|
await handleError(() => social.unlike({ templateId: id }))
|
||||||
|
} catch (e) {
|
||||||
|
setLiked(id, true)
|
||||||
|
incrementLikeCount(id)
|
||||||
|
}
|
||||||
|
}, [setLiked, incrementLikeCount, decrementLikeCount, getSocialController])
|
||||||
|
|
||||||
// 收藏/取消收藏处理
|
// 收藏/取消收藏处理
|
||||||
const handleFavorite = useCallback((id: string) => {
|
const handleFavorite = useCallback(async (id: string) => {
|
||||||
setFavorited(id, true)
|
setFavorited(id, true)
|
||||||
console.log('Favorite template:', id)
|
try {
|
||||||
}, [setFavorited])
|
const social = getSocialController()
|
||||||
|
await handleError(() => social.favorite({ templateId: id }))
|
||||||
|
} catch (e) {
|
||||||
|
setFavorited(id, false)
|
||||||
|
}
|
||||||
|
}, [setFavorited, getSocialController])
|
||||||
|
|
||||||
const handleUnfavorite = useCallback((id: string) => {
|
const handleUnfavorite = useCallback(async (id: string) => {
|
||||||
setFavorited(id, false)
|
setFavorited(id, false)
|
||||||
console.log('Unfavorite template:', id)
|
try {
|
||||||
}, [setFavorited])
|
const social = getSocialController()
|
||||||
|
await handleError(() => social.unfavorite({ templateId: id }))
|
||||||
|
} catch (e) {
|
||||||
|
setFavorited(id, true)
|
||||||
|
}
|
||||||
|
}, [setFavorited, getSocialController])
|
||||||
|
|
||||||
// 状态判断
|
// 状态判断
|
||||||
const isLoading = useMemo(() => loading, [loading])
|
const isLoading = useMemo(() => loading, [loading])
|
||||||
@@ -137,6 +169,7 @@ export default function FavoritesScreen() {
|
|||||||
onPress={handleTemplatePress}
|
onPress={handleTemplatePress}
|
||||||
liked={template.isLiked}
|
liked={template.isLiked}
|
||||||
favorited={template.isFavorited}
|
favorited={template.isFavorited}
|
||||||
|
likeCount={template.likeCount}
|
||||||
onLike={handleLike}
|
onLike={handleLike}
|
||||||
onUnlike={handleUnlike}
|
onUnlike={handleUnlike}
|
||||||
onFavorite={handleFavorite}
|
onFavorite={handleFavorite}
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ 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'
|
import { useTemplateSocialStore } from '@/stores/templateSocialStore'
|
||||||
|
import { root } from '@repo/core'
|
||||||
|
import { TemplateSocialController } from '@repo/sdk'
|
||||||
|
import { handleError } from '@/hooks/use-error'
|
||||||
|
|
||||||
const NUM_COLUMNS = 2
|
const NUM_COLUMNS = 2
|
||||||
const HORIZONTAL_PADDING = 16
|
const HORIZONTAL_PADDING = 16
|
||||||
@@ -37,7 +40,7 @@ export default function LikesScreen() {
|
|||||||
const [refreshing, setRefreshing] = useState(false)
|
const [refreshing, setRefreshing] = useState(false)
|
||||||
|
|
||||||
// 使用 Store 中的点赞/收藏状态
|
// 使用 Store 中的点赞/收藏状态
|
||||||
const { setLiked, setFavorited } = useTemplateSocialStore()
|
const { setLiked, setFavorited, incrementLikeCount, decrementLikeCount } = useTemplateSocialStore()
|
||||||
|
|
||||||
// 获取用户点赞列表
|
// 获取用户点赞列表
|
||||||
const {
|
const {
|
||||||
@@ -78,27 +81,56 @@ export default function LikesScreen() {
|
|||||||
})
|
})
|
||||||
}, [router])
|
}, [router])
|
||||||
|
|
||||||
// 点赞/取消点赞处理
|
// 获取 social controller
|
||||||
const handleLike = useCallback((id: string) => {
|
const getSocialController = useCallback(() => {
|
||||||
setLiked(id, true)
|
return root.get(TemplateSocialController)
|
||||||
console.log('Like template:', id)
|
}, [])
|
||||||
}, [setLiked])
|
|
||||||
|
|
||||||
const handleUnlike = useCallback((id: string) => {
|
// 点赞/取消点赞处理
|
||||||
|
const handleLike = useCallback(async (id: string) => {
|
||||||
|
setLiked(id, true)
|
||||||
|
incrementLikeCount(id)
|
||||||
|
try {
|
||||||
|
const social = getSocialController()
|
||||||
|
await handleError(() => social.like({ templateId: id }))
|
||||||
|
} catch (e) {
|
||||||
|
setLiked(id, false)
|
||||||
|
decrementLikeCount(id)
|
||||||
|
}
|
||||||
|
}, [setLiked, incrementLikeCount, decrementLikeCount, getSocialController])
|
||||||
|
|
||||||
|
const handleUnlike = useCallback(async (id: string) => {
|
||||||
setLiked(id, false)
|
setLiked(id, false)
|
||||||
console.log('Unlike template:', id)
|
decrementLikeCount(id)
|
||||||
}, [setLiked])
|
try {
|
||||||
|
const social = getSocialController()
|
||||||
|
await handleError(() => social.unlike({ templateId: id }))
|
||||||
|
} catch (e) {
|
||||||
|
setLiked(id, true)
|
||||||
|
incrementLikeCount(id)
|
||||||
|
}
|
||||||
|
}, [setLiked, incrementLikeCount, decrementLikeCount, getSocialController])
|
||||||
|
|
||||||
// 收藏/取消收藏处理
|
// 收藏/取消收藏处理
|
||||||
const handleFavorite = useCallback((id: string) => {
|
const handleFavorite = useCallback(async (id: string) => {
|
||||||
setFavorited(id, true)
|
setFavorited(id, true)
|
||||||
console.log('Favorite template:', id)
|
try {
|
||||||
}, [setFavorited])
|
const social = getSocialController()
|
||||||
|
await handleError(() => social.favorite({ templateId: id }))
|
||||||
|
} catch (e) {
|
||||||
|
setFavorited(id, false)
|
||||||
|
}
|
||||||
|
}, [setFavorited, getSocialController])
|
||||||
|
|
||||||
const handleUnfavorite = useCallback((id: string) => {
|
const handleUnfavorite = useCallback(async (id: string) => {
|
||||||
setFavorited(id, false)
|
setFavorited(id, false)
|
||||||
console.log('Unfavorite template:', id)
|
try {
|
||||||
}, [setFavorited])
|
const social = getSocialController()
|
||||||
|
await handleError(() => social.unfavorite({ templateId: id }))
|
||||||
|
} catch (e) {
|
||||||
|
setFavorited(id, true)
|
||||||
|
}
|
||||||
|
}, [setFavorited, getSocialController])
|
||||||
|
|
||||||
// 状态判断
|
// 状态判断
|
||||||
const showEmptyState = useMemo(() =>
|
const showEmptyState = useMemo(() =>
|
||||||
@@ -133,6 +165,7 @@ export default function LikesScreen() {
|
|||||||
onPress={handleTemplatePress}
|
onPress={handleTemplatePress}
|
||||||
liked={item.template.isLiked}
|
liked={item.template.isLiked}
|
||||||
favorited={item.template.isFavorited}
|
favorited={item.template.isFavorited}
|
||||||
|
likeCount={item.template.likeCount}
|
||||||
onLike={handleLike}
|
onLike={handleLike}
|
||||||
onUnlike={handleUnlike}
|
onUnlike={handleUnlike}
|
||||||
onFavorite={handleFavorite}
|
onFavorite={handleFavorite}
|
||||||
|
|||||||
@@ -4,7 +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'
|
import { useTemplateSocialStore, useTemplateLiked, useTemplateFavorited, useTemplateLikeCount } from '@/stores/templateSocialStore'
|
||||||
|
|
||||||
export interface TemplateCardProps {
|
export interface TemplateCardProps {
|
||||||
id?: string
|
id?: string
|
||||||
@@ -18,6 +18,7 @@ export interface TemplateCardProps {
|
|||||||
onPress: (id: string) => void
|
onPress: (id: string) => void
|
||||||
liked?: boolean
|
liked?: boolean
|
||||||
favorited?: boolean
|
favorited?: boolean
|
||||||
|
likeCount?: number
|
||||||
onLike?: (id: string) => void
|
onLike?: (id: string) => void
|
||||||
onUnlike?: (id: string) => void
|
onUnlike?: (id: string) => void
|
||||||
onFavorite?: (id: string) => void
|
onFavorite?: (id: string) => void
|
||||||
@@ -51,6 +52,23 @@ export function getImageUri(
|
|||||||
return webpPreviewUrl || previewUrl || coverImageUrl
|
return webpPreviewUrl || previewUrl || coverImageUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化数字显示
|
||||||
|
* 1000以下: 显示原数字
|
||||||
|
* 1000-9999: 1.2k
|
||||||
|
* 10000-99999: 1w
|
||||||
|
* 100000以上: 10w
|
||||||
|
*/
|
||||||
|
export function formatCount(count: number): string {
|
||||||
|
if (count < 1000) {
|
||||||
|
return count.toString()
|
||||||
|
}
|
||||||
|
if (count < 10000) {
|
||||||
|
return `${(count / 1000).toFixed(1).replace(/\.0$/, '')}k`
|
||||||
|
}
|
||||||
|
return `${(count / 10000).toFixed(1).replace(/\.0$/, '')}w`
|
||||||
|
}
|
||||||
|
|
||||||
// 渐变颜色常量,避免每次渲染创建新数组
|
// 渐变颜色常量,避免每次渲染创建新数组
|
||||||
const GRADIENT_COLORS = ['rgba(17, 17, 17, 0)', 'rgba(17, 17, 17, 0.9)'] as const
|
const GRADIENT_COLORS = ['rgba(17, 17, 17, 0)', 'rgba(17, 17, 17, 0.9)'] as const
|
||||||
const GRADIENT_START = { x: 0, y: 0 }
|
const GRADIENT_START = { x: 0, y: 0 }
|
||||||
@@ -68,6 +86,7 @@ const TemplateCardComponent: React.FC<TemplateCardProps> = ({
|
|||||||
onPress,
|
onPress,
|
||||||
liked: likedProp,
|
liked: likedProp,
|
||||||
favorited: favoritedProp,
|
favorited: favoritedProp,
|
||||||
|
likeCount: likeCountProp,
|
||||||
onLike,
|
onLike,
|
||||||
onUnlike,
|
onUnlike,
|
||||||
onFavorite,
|
onFavorite,
|
||||||
@@ -76,12 +95,16 @@ const TemplateCardComponent: React.FC<TemplateCardProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { i18n } = useTranslation()
|
const { i18n } = useTranslation()
|
||||||
|
|
||||||
// 获取 Store 中的状态(用于本地状态覆盖)
|
// 订阅特定模板的点赞/收藏状态和点赞数量
|
||||||
const { isLiked: isLikedInStore, isFavorited: isFavoritedInStore } = useTemplateSocialStore()
|
// 当该模板的状态变化时才会触发重新渲染,不影响其他卡片
|
||||||
|
const storeLiked = useTemplateLiked(id)
|
||||||
|
const storeFavorited = useTemplateFavorited(id)
|
||||||
|
const storeLikeCount = useTemplateLikeCount(id)
|
||||||
|
|
||||||
// 合并 props 状态和 store 状态:store 优先(乐观更新)
|
// 合并 props 状态和 store 状态:store 优先(乐观更新)
|
||||||
const liked = id !== undefined ? isLikedInStore(id) ?? likedProp : likedProp
|
const liked = storeLiked ?? likedProp
|
||||||
const favorited = id !== undefined ? isFavoritedInStore(id) ?? favoritedProp : favoritedProp
|
const favorited = storeFavorited ?? favoritedProp
|
||||||
|
const likeCount = storeLikeCount ?? likeCountProp
|
||||||
|
|
||||||
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])
|
||||||
@@ -179,7 +202,12 @@ const TemplateCardComponent: React.FC<TemplateCardProps> = ({
|
|||||||
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
||||||
testID={`${testID}-like-button`}
|
testID={`${testID}-like-button`}
|
||||||
>
|
>
|
||||||
<Ionicons name={likeIconName} size={16} color={likeIconColor} style={styles.icon} />
|
<View style={styles.likeButtonContainer}>
|
||||||
|
<Ionicons name={likeIconName} size={16} color={likeIconColor} style={styles.icon} />
|
||||||
|
{likeCount !== undefined && likeCount > 0 && (
|
||||||
|
<Text style={styles.likeCount}>{formatCount(likeCount)}</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={(e) => {
|
onPress={(e) => {
|
||||||
@@ -196,7 +224,12 @@ const TemplateCardComponent: React.FC<TemplateCardProps> = ({
|
|||||||
) : (
|
) : (
|
||||||
// 没有回调时显示为静态图标
|
// 没有回调时显示为静态图标
|
||||||
<View style={styles.iconsContainer}>
|
<View style={styles.iconsContainer}>
|
||||||
<Ionicons name={likeIconName} size={16} color={likeIconColor} style={styles.icon} />
|
<View style={styles.likeButtonContainer}>
|
||||||
|
<Ionicons name={likeIconName} size={16} color={likeIconColor} style={styles.icon} />
|
||||||
|
{likeCount !== undefined && likeCount > 0 && (
|
||||||
|
<Text style={styles.likeCount}>{formatCount(likeCount)}</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
<Ionicons name={favoriteIconName} size={16} color={favoriteIconColor} style={styles.icon} />
|
<Ionicons name={favoriteIconName} size={16} color={favoriteIconColor} style={styles.icon} />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
@@ -238,6 +271,11 @@ const styles = StyleSheet.create({
|
|||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
gap: 4,
|
gap: 4,
|
||||||
},
|
},
|
||||||
|
likeButtonContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 2,
|
||||||
|
},
|
||||||
iconButton: {
|
iconButton: {
|
||||||
padding: 4,
|
padding: 4,
|
||||||
},
|
},
|
||||||
@@ -248,6 +286,15 @@ const styles = StyleSheet.create({
|
|||||||
shadowRadius: 2,
|
shadowRadius: 2,
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
},
|
},
|
||||||
|
likeCount: {
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: '#F5F5F5',
|
||||||
|
textShadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
textShadowOffset: { width: 0, height: 1 },
|
||||||
|
textShadowRadius: 2,
|
||||||
|
minWidth: 12,
|
||||||
|
},
|
||||||
cardTitle: {
|
cardTitle: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: 8,
|
bottom: 8,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { create } from 'zustand'
|
import { create } from 'zustand'
|
||||||
|
import { devtools } from 'zustand/middleware'
|
||||||
|
|
||||||
interface TemplateSocialState {
|
interface TemplateSocialState {
|
||||||
// 点赞状态缓存: templateId -> boolean
|
// 点赞状态缓存: templateId -> boolean
|
||||||
@@ -7,70 +8,166 @@ interface TemplateSocialState {
|
|||||||
// 收藏状态缓存: templateId -> boolean
|
// 收藏状态缓存: templateId -> boolean
|
||||||
favoritedMap: Record<string, boolean>
|
favoritedMap: Record<string, boolean>
|
||||||
|
|
||||||
|
// 点赞数量缓存: templateId -> number
|
||||||
|
likeCountMap: Record<string, number>
|
||||||
|
|
||||||
// 批量更新点赞状态
|
// 批量更新点赞状态
|
||||||
setLikedStates: (states: Record<string, boolean>) => void
|
setLikedStates: (states: Record<string, boolean>) => void
|
||||||
|
|
||||||
// 批量更新收藏状态
|
// 批量更新收藏状态
|
||||||
setFavoritedStates: (states: Record<string, boolean>) => void
|
setFavoritedStates: (states: Record<string, boolean>) => void
|
||||||
|
|
||||||
|
// 批量更新点赞数量
|
||||||
|
setLikeCountStates: (states: Record<string, number>) => void
|
||||||
|
|
||||||
// 更新单个点赞状态
|
// 更新单个点赞状态
|
||||||
setLiked: (templateId: string, liked: boolean) => void
|
setLiked: (templateId: string, liked: boolean) => void
|
||||||
|
|
||||||
// 更新单个收藏状态
|
// 更新单个收藏状态
|
||||||
setFavorited: (templateId: string, favorited: boolean) => void
|
setFavorited: (templateId: string, favorited: boolean) => void
|
||||||
|
|
||||||
// 获取点赞状态
|
// 更新单个点赞数量
|
||||||
|
setLikeCount: (templateId: string, count: number) => void
|
||||||
|
|
||||||
|
// 增加点赞数量(乐观更新)
|
||||||
|
incrementLikeCount: (templateId: string) => void
|
||||||
|
|
||||||
|
// 减少点赞数量(乐观更新)
|
||||||
|
decrementLikeCount: (templateId: string) => void
|
||||||
|
|
||||||
|
// 获取点赞状态(如果不存在返回 undefined)
|
||||||
|
getLiked: (templateId: string) => boolean | undefined
|
||||||
|
|
||||||
|
// 获取收藏状态(如果不存在返回 undefined)
|
||||||
|
getFavorited: (templateId: string) => boolean | undefined
|
||||||
|
|
||||||
|
// 获取点赞数量(如果不存在返回 undefined)
|
||||||
|
getLikeCount: (templateId: string) => number | undefined
|
||||||
|
|
||||||
|
// 获取点赞状态(如果不存在返回 false)
|
||||||
isLiked: (templateId: string) => boolean
|
isLiked: (templateId: string) => boolean
|
||||||
|
|
||||||
// 获取收藏状态
|
// 获取收藏状态(如果不存在返回 false)
|
||||||
isFavorited: (templateId: string) => boolean
|
isFavorited: (templateId: string) => boolean
|
||||||
|
|
||||||
// 清空所有缓存
|
// 清空所有缓存
|
||||||
clear: () => void
|
clear: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useTemplateSocialStore = create<TemplateSocialState>((set, get) => ({
|
export const useTemplateSocialStore = create<TemplateSocialState>()(
|
||||||
// State
|
devtools(
|
||||||
likedMap: {},
|
(set, get) => ({
|
||||||
favoritedMap: {},
|
// State
|
||||||
|
|
||||||
// Actions
|
|
||||||
setLikedStates: (states) => {
|
|
||||||
set((state) => ({
|
|
||||||
likedMap: { ...state.likedMap, ...states },
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
|
|
||||||
setFavoritedStates: (states) => {
|
|
||||||
set((state) => ({
|
|
||||||
favoritedMap: { ...state.favoritedMap, ...states },
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
|
|
||||||
setLiked: (templateId, liked) => {
|
|
||||||
set((state) => ({
|
|
||||||
likedMap: { ...state.likedMap, [templateId]: liked },
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
|
|
||||||
setFavorited: (templateId, favorited) => {
|
|
||||||
set((state) => ({
|
|
||||||
favoritedMap: { ...state.favoritedMap, [templateId]: favorited },
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
|
|
||||||
isLiked: (templateId) => {
|
|
||||||
return get().likedMap[templateId] || false
|
|
||||||
},
|
|
||||||
|
|
||||||
isFavorited: (templateId) => {
|
|
||||||
return get().favoritedMap[templateId] || false
|
|
||||||
},
|
|
||||||
|
|
||||||
clear: () => {
|
|
||||||
set({
|
|
||||||
likedMap: {},
|
likedMap: {},
|
||||||
favoritedMap: {},
|
favoritedMap: {},
|
||||||
})
|
likeCountMap: {},
|
||||||
},
|
|
||||||
}))
|
// Actions
|
||||||
|
setLikedStates: (states) => {
|
||||||
|
set((state) => ({
|
||||||
|
likedMap: { ...state.likedMap, ...states },
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
|
||||||
|
setFavoritedStates: (states) => {
|
||||||
|
set((state) => ({
|
||||||
|
favoritedMap: { ...state.favoritedMap, ...states },
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
|
||||||
|
setLikeCountStates: (states) => {
|
||||||
|
set((state) => ({
|
||||||
|
likeCountMap: { ...state.likeCountMap, ...states },
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
|
||||||
|
setLiked: (templateId, liked) => {
|
||||||
|
set((state) => ({
|
||||||
|
likedMap: { ...state.likedMap, [templateId]: liked },
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
|
||||||
|
setFavorited: (templateId, favorited) => {
|
||||||
|
set((state) => ({
|
||||||
|
favoritedMap: { ...state.favoritedMap, [templateId]: favorited },
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
|
||||||
|
setLikeCount: (templateId, count) => {
|
||||||
|
set((state) => ({
|
||||||
|
likeCountMap: { ...state.likeCountMap, [templateId]: count },
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
|
||||||
|
incrementLikeCount: (templateId) => {
|
||||||
|
set((state) => ({
|
||||||
|
likeCountMap: {
|
||||||
|
...state.likeCountMap,
|
||||||
|
[templateId]: (state.likeCountMap[templateId] || 0) + 1,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
|
||||||
|
decrementLikeCount: (templateId) => {
|
||||||
|
set((state) => {
|
||||||
|
const currentCount = state.likeCountMap[templateId] || 0
|
||||||
|
return {
|
||||||
|
likeCountMap: {
|
||||||
|
...state.likeCountMap,
|
||||||
|
[templateId]: Math.max(0, currentCount - 1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getLiked: (templateId) => {
|
||||||
|
return get().likedMap[templateId]
|
||||||
|
},
|
||||||
|
|
||||||
|
getFavorited: (templateId) => {
|
||||||
|
return get().favoritedMap[templateId]
|
||||||
|
},
|
||||||
|
|
||||||
|
getLikeCount: (templateId) => {
|
||||||
|
return get().likeCountMap[templateId]
|
||||||
|
},
|
||||||
|
|
||||||
|
isLiked: (templateId) => {
|
||||||
|
return get().likedMap[templateId] || false
|
||||||
|
},
|
||||||
|
|
||||||
|
isFavorited: (templateId) => {
|
||||||
|
return get().favoritedMap[templateId] || false
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: () => {
|
||||||
|
set({
|
||||||
|
likedMap: {},
|
||||||
|
favoritedMap: {},
|
||||||
|
likeCountMap: {},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{ name: 'TemplateSocialStore' }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 选择器 hook:只订阅特定模板的状态
|
||||||
|
// 当该模板的状态变化时才会触发重新渲染
|
||||||
|
export function useTemplateLiked(templateId: string | undefined) {
|
||||||
|
return useTemplateSocialStore((state) =>
|
||||||
|
templateId ? state.likedMap[templateId] : undefined
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTemplateFavorited(templateId: string | undefined) {
|
||||||
|
return useTemplateSocialStore((state) =>
|
||||||
|
templateId ? state.favoritedMap[templateId] : undefined
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTemplateLikeCount(templateId: string | undefined) {
|
||||||
|
return useTemplateSocialStore((state) =>
|
||||||
|
templateId ? state.likeCountMap[templateId] : undefined
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user