crush-level-web/src/app/(main)/user/[userId]/components/AlbumItem.tsx

154 lines
4.7 KiB
TypeScript

import { Tag } from '@/components/ui/tag'
import { cn, formatNumberToKMB } from '@/lib/utils'
import { IAlbumItem, LikedStatus, LockStatus } from '@/services/user'
import Image from 'next/image'
import { useState } from 'react'
import { useAIUser } from '../context/aiUser'
import { IconButton } from '@/components/ui/button'
import AlbumItemAction from './AlbumItemAction'
import { formatFromCents } from '@/utils/number'
interface AlbumItemProps {
item: IAlbumItem
onLike: (albumId: number, isLiked: boolean) => void
onImageClick: () => void
}
const AlbumItem = ({ item, onLike, onImageClick }: AlbumItemProps) => {
const [imageLoading, setImageLoading] = useState(true)
const { isOwner } = useAIUser()
const handleLike = () => {
onLike(item.albumId, item.likedStatus === LikedStatus.Liked)
}
const renderTag = () => {
if (item.isDefault) {
return null
}
if (isOwner) {
if (item.lockStatus) {
return (
<Tag variant="dark" className="absolute top-2 right-2 p-[6px]" size="small">
<i className="iconfont icon-private !text-[12px] leading-none" />
</Tag>
)
}
}
if (item.lockStatus === LockStatus.Unlock) {
return (
<Tag
variant="default"
className="bg-primary-gradient-normal absolute top-2 right-2 p-[6px]"
size="small"
>
<i className="iconfont icon-public !text-[12px] leading-none" />
</Tag>
)
}
return null
}
const renderOverlay = () => {
// 如果是自己的相册,则不显示解锁按钮
if (isOwner) {
return null
}
if (item.lockStatus === LockStatus.Lock) {
return (
<div
className="absolute inset-0 flex cursor-pointer flex-col items-center justify-center gap-3"
onClick={(e) => {
// e.stopPropagation();
// handleUnlock();
}}
>
<i className="iconfont icon-private-border !text-[24px] leading-none" />
<div className="flex items-center gap-2">
<div className="flex items-center gap-1">
<div className="relative h-4 w-4">
<Image src="/icons/diamond.svg" alt="diamond" fill className="object-contain" />
</div>
<span className="text-sm font-semibold text-white">
{formatFromCents(item.unlockPrice || 0)}
</span>
</div>
<div className="txt-label-m text-txt-primary-normal">Unlock</div>
</div>
</div>
)
}
return null
}
const renderDefaultTag = () => {
if (item.isDefault) {
return (
<Tag variant="dark" className="absolute top-2 left-2" size="small">
Default
</Tag>
)
}
return null
}
return (
<div className="group relative cursor-pointer overflow-hidden rounded-2xl pb-[134%]">
<div className="absolute inset-0" onClick={onImageClick}>
{/* 背景图片 */}
<div className="relative h-full w-full">
<Image
src={item.imgUrl || item.img1}
alt="Album image"
fill
className={cn(
'object-cover object-top transition-opacity duration-300',
imageLoading ? 'opacity-0' : 'opacity-100'
)}
onLoadingComplete={() => setImageLoading(false)}
sizes="(max-width: 768px) 50vw, 176px"
/>
{imageLoading && (
<div className="bg-surface-nest-normal absolute inset-0 animate-pulse" />
)}
</div>
{renderDefaultTag()}
{/* 标签 */}
{renderTag()}
{/* 付费内容遮罩 */}
{renderOverlay()}
{/* 底部操作区 */}
<div
className="absolute right-2 bottom-2 left-2 flex items-center justify-between"
onClick={(e) => e.stopPropagation()}
>
{/* 点赞按钮 */}
{(item.lockStatus !== LockStatus.Lock || isOwner) && (
<div className="bg-surface-element-dark-normal flex items-center gap-[2px] rounded-full pr-1 backdrop-blur-lg">
<IconButton variant="ghost" size="xs" onClick={handleLike}>
{item.likedStatus === LikedStatus.Liked ? (
<i className="iconfont icon-Like-fill !text-important-normal !text-[16px] leading-none" />
) : (
<i className="iconfont icon-Like !text-[16px] leading-none" />
)}
</IconButton>
<span className="txt-numMonotype-xs text-white">
{formatNumberToKMB(item.likedCount ?? 0)}
</span>
</div>
)}
<AlbumItemAction data={item} />
</div>
</div>
</div>
)
}
export default AlbumItem