1
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -133,6 +133,8 @@ const puzzlePublicEntry = {
|
||||
summaryText: '一张用于公开分享的拼图作品。',
|
||||
coverImageSrc: null,
|
||||
themeTags: ['奇幻'],
|
||||
playCount: 20,
|
||||
remixCount: 5,
|
||||
likeCount: 12,
|
||||
visibility: 'published',
|
||||
publishedAt: '1777110165.990127Z',
|
||||
@@ -553,22 +555,40 @@ test('public gallery cards hide work code until detail is opened', async () => {
|
||||
expect(onOpenGalleryDetail).toHaveBeenCalledWith(puzzlePublicEntry);
|
||||
});
|
||||
|
||||
test('mobile public work cards render cover, content and like count', () => {
|
||||
test('mobile public work cards render cover, author, kind and cover stats', () => {
|
||||
const { container } = renderLoggedOutHomeView(vi.fn(), {
|
||||
latestEntries: [puzzlePublicEntry],
|
||||
});
|
||||
|
||||
const card = screen.getByRole('button', {
|
||||
name: /奇幻拼图,12点赞/u,
|
||||
name: /奇幻拼图,拼图,20游玩,5改造,12点赞/u,
|
||||
});
|
||||
expect(
|
||||
card.querySelector('.platform-public-work-card__cover.aspect-video'),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
card.querySelector('.platform-public-work-card__cover-stats'),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
card.querySelectorAll('.platform-public-work-card__cover-stat'),
|
||||
).toHaveLength(3);
|
||||
expect(
|
||||
card.querySelector('.platform-public-work-card__kind')?.textContent,
|
||||
).toBe('拼图');
|
||||
expect(
|
||||
card.querySelector('.platform-public-work-card__author-avatar')
|
||||
?.textContent,
|
||||
).toBe('拼');
|
||||
expect(screen.getByText('奇幻拼图')).toBeTruthy();
|
||||
expect(screen.getByText('拼图玩家')).toBeTruthy();
|
||||
expect(screen.getByText('一张用于公开分享的拼图作品。')).toBeTruthy();
|
||||
expect(screen.getByText('奇幻')).toBeTruthy();
|
||||
expect(screen.getByText('20')).toBeTruthy();
|
||||
expect(screen.getByText('5')).toBeTruthy();
|
||||
expect(screen.getByText('12')).toBeTruthy();
|
||||
expect(screen.getByText('点赞')).toBeTruthy();
|
||||
expect(card.querySelector('.platform-pill--warm')?.textContent).not.toBe(
|
||||
'推荐',
|
||||
);
|
||||
expect(
|
||||
container.querySelector('.platform-mobile-home-channel--active')
|
||||
?.textContent,
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
Clock3,
|
||||
Coins,
|
||||
Copy,
|
||||
Gamepad2,
|
||||
Heart,
|
||||
House,
|
||||
LogIn,
|
||||
@@ -362,22 +363,40 @@ function SaveArchivePreview({
|
||||
|
||||
function WorldCard({
|
||||
entry,
|
||||
badge,
|
||||
metaLabel,
|
||||
onClick,
|
||||
className,
|
||||
}: {
|
||||
entry: PlatformWorldCardLike;
|
||||
badge: string;
|
||||
metaLabel: string;
|
||||
entry: PlatformPublicGalleryCard;
|
||||
onClick: () => void;
|
||||
className?: string;
|
||||
}) {
|
||||
const coverImage = resolvePlatformWorldCoverImage(entry);
|
||||
const displayName = formatPlatformWorkDisplayName(entry.worldName);
|
||||
const tags = buildPlatformWorldDisplayTags(entry, 3);
|
||||
const playCount = getPlatformWorldPlayCount(entry);
|
||||
const remixCount = getPlatformWorldRemixCount(entry);
|
||||
const likeCount = getPlatformWorldLikeCount(entry);
|
||||
const cardLabel = `${entry.worldName},${formatCompactCount(likeCount)}点赞`;
|
||||
const typeLabel = describePublicGalleryCardKind(entry);
|
||||
const authorName = entry.authorDisplayName.trim() || '玩家';
|
||||
const authorAvatarLabel = getPublicAuthorAvatarLabel(authorName);
|
||||
const cardLabel = `${entry.worldName},${typeLabel},${formatCompactCount(playCount)}游玩,${formatCompactCount(remixCount)}改造,${formatCompactCount(likeCount)}点赞`;
|
||||
const coverStats = [
|
||||
{
|
||||
label: '游玩',
|
||||
value: playCount,
|
||||
icon: Gamepad2,
|
||||
},
|
||||
{
|
||||
label: '改造',
|
||||
value: remixCount,
|
||||
icon: Pencil,
|
||||
},
|
||||
{
|
||||
label: '点赞',
|
||||
value: likeCount,
|
||||
icon: Heart,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<button
|
||||
@@ -397,13 +416,16 @@ function WorldCard({
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_18%_16%,rgba(255,255,255,0.28),transparent_30%),linear-gradient(135deg,rgba(255,118,117,0.42),rgba(89,164,255,0.34))]" />
|
||||
)}
|
||||
<div className="absolute inset-0 bg-[linear-gradient(180deg,rgba(0,0,0,0.02),rgba(0,0,0,0.18))]" />
|
||||
<div className="absolute left-3 top-3 flex min-w-0 max-w-[calc(100%-1.5rem)] flex-wrap gap-1.5">
|
||||
<span className="platform-pill platform-pill--warm max-w-[9rem] truncate px-2.5">
|
||||
{badge}
|
||||
</span>
|
||||
<span className="platform-pill platform-pill--neutral max-w-[9rem] truncate px-2.5">
|
||||
{metaLabel}
|
||||
</span>
|
||||
<div className="platform-public-work-card__cover-stats">
|
||||
{coverStats.map(({ label, value, icon: Icon }) => (
|
||||
<span key={label} className="platform-public-work-card__cover-stat">
|
||||
<Icon
|
||||
className={`h-3.5 w-3.5 ${label === '点赞' ? 'fill-current' : ''}`}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span>{formatCompactCount(value)}</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -413,21 +435,21 @@ function WorldCard({
|
||||
<div className="line-clamp-1 break-words text-base font-black leading-tight text-[var(--platform-text-strong)]">
|
||||
{displayName}
|
||||
</div>
|
||||
{entry.subtitle ? (
|
||||
<div className="mt-0.5 line-clamp-1 break-words text-[11px] font-medium text-[var(--platform-text-soft)]">
|
||||
{entry.subtitle}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="platform-public-work-card__likes shrink-0 text-right">
|
||||
<div className="flex items-center justify-end gap-1 text-xs font-black text-[var(--platform-warm-text)]">
|
||||
<Heart className="h-3.5 w-3.5 fill-current" />
|
||||
<span>{formatCompactCount(likeCount)}</span>
|
||||
</div>
|
||||
<div className="mt-0.5 text-[10px] font-semibold text-[var(--platform-text-soft)]">
|
||||
点赞
|
||||
<div className="mt-1 flex min-w-0 items-center gap-1.5">
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="platform-public-work-card__author-avatar"
|
||||
>
|
||||
{authorAvatarLabel}
|
||||
</span>
|
||||
<span className="line-clamp-1 break-words text-[11px] font-semibold text-[var(--platform-text-soft)]">
|
||||
{authorName}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span className="platform-public-work-card__kind shrink-0">
|
||||
{typeLabel}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="line-clamp-2 break-words text-xs leading-5 text-[color:color-mix(in_srgb,var(--platform-text-base)_88%,transparent)]">
|
||||
@@ -960,6 +982,10 @@ function describePublicGalleryCardKind(entry: PlatformPublicGalleryCard) {
|
||||
return formatPlatformWorkDisplayTag(kind);
|
||||
}
|
||||
|
||||
function getPublicAuthorAvatarLabel(authorDisplayName: string) {
|
||||
return Array.from(authorDisplayName.trim() || '玩')[0] ?? '玩';
|
||||
}
|
||||
|
||||
function getPlatformWorldLikeCount(entry: PlatformWorldCardLike) {
|
||||
return Math.max(0, Math.round(entry.likeCount ?? 0));
|
||||
}
|
||||
@@ -1404,9 +1430,16 @@ function ProfileNicknameModal({
|
||||
}) {
|
||||
return (
|
||||
<div className="platform-modal-backdrop fixed inset-0 z-[80] flex items-center justify-center px-4 py-6">
|
||||
<div className="platform-recharge-modal w-full max-w-sm overflow-hidden rounded-[1.4rem]">
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="profile-nickname-title"
|
||||
className="platform-modal-shell platform-remap-surface w-full max-w-sm overflow-hidden rounded-[1.4rem]"
|
||||
>
|
||||
<div className="flex items-center justify-between border-b border-white/10 px-5 py-4">
|
||||
<div className="text-base font-black">修改昵称</div>
|
||||
<div id="profile-nickname-title" className="text-base font-black">
|
||||
修改昵称
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="关闭昵称修改"
|
||||
@@ -1529,9 +1562,16 @@ function ProfileAvatarCropModal({
|
||||
|
||||
return (
|
||||
<div className="platform-modal-backdrop fixed inset-0 z-[80] flex items-center justify-center px-4 py-6">
|
||||
<div className="platform-recharge-modal w-full max-w-sm overflow-hidden rounded-[1.4rem]">
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="profile-avatar-crop-title"
|
||||
className="platform-modal-shell platform-remap-surface w-full max-w-sm overflow-hidden rounded-[1.4rem]"
|
||||
>
|
||||
<div className="flex items-center justify-between border-b border-white/10 px-5 py-4">
|
||||
<div className="text-base font-black">裁剪头像</div>
|
||||
<div id="profile-avatar-crop-title" className="text-base font-black">
|
||||
裁剪头像
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="关闭头像裁剪"
|
||||
@@ -2726,12 +2766,6 @@ export function RpgEntryHomeView({
|
||||
<WorldCard
|
||||
key={`${buildPublicGalleryCardKey(entry)}:mobile-feed:${mobileHomeChannel}`}
|
||||
entry={entry}
|
||||
badge={
|
||||
mobileHomeChannel === 'recommend'
|
||||
? '推荐'
|
||||
: describePublicGalleryCardKind(entry)
|
||||
}
|
||||
metaLabel={entry.authorDisplayName}
|
||||
onClick={() => onOpenGalleryDetail(entry)}
|
||||
className="w-full"
|
||||
/>
|
||||
@@ -3244,8 +3278,6 @@ export function RpgEntryHomeView({
|
||||
<WorldCard
|
||||
key={`${buildPublicGalleryCardKey(entry)}:desktop-featured`}
|
||||
entry={entry}
|
||||
badge="推荐"
|
||||
metaLabel={describePublicGalleryCardKind(entry)}
|
||||
onClick={() => onOpenGalleryDetail(entry)}
|
||||
className="w-full min-w-0"
|
||||
/>
|
||||
@@ -3375,8 +3407,6 @@ export function RpgEntryHomeView({
|
||||
<WorldCard
|
||||
key={`${buildPublicGalleryCardKey(entry)}:desktop-latest`}
|
||||
entry={entry}
|
||||
badge={describePublicGalleryCardKind(entry)}
|
||||
metaLabel={entry.authorDisplayName}
|
||||
onClick={() => onOpenGalleryDetail(entry)}
|
||||
className="w-full min-w-0"
|
||||
/>
|
||||
|
||||
@@ -96,9 +96,9 @@ export function mapPuzzleWorkToPlatformGalleryCard(
|
||||
publicWorkCode: buildPuzzlePublicWorkCode(work.profileId),
|
||||
ownerUserId: work.ownerUserId,
|
||||
authorDisplayName: work.authorDisplayName,
|
||||
worldName: work.levelName,
|
||||
worldName: work.workTitle || work.levelName,
|
||||
subtitle: '拼图关卡',
|
||||
summaryText: work.summary,
|
||||
summaryText: work.workDescription || work.summary,
|
||||
coverImageSrc: work.coverImageSrc,
|
||||
themeTags: work.themeTags,
|
||||
playCount: work.playCount ?? 0,
|
||||
@@ -120,7 +120,7 @@ export function mapBigFishWorkToPlatformGalleryCard(
|
||||
profileId: work.sourceSessionId,
|
||||
publicWorkCode: buildBigFishPublicWorkCode(work.sourceSessionId),
|
||||
ownerUserId: work.ownerUserId,
|
||||
authorDisplayName: '大鱼陶泥主',
|
||||
authorDisplayName: work.authorDisplayName,
|
||||
worldName: work.title,
|
||||
subtitle: work.subtitle || '大鱼吃小鱼',
|
||||
summaryText: work.summary,
|
||||
|
||||
Reference in New Issue
Block a user