diff --git a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx index ecab6ae3..32c45498 100644 --- a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx +++ b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx @@ -15294,9 +15294,7 @@ export function PlatformEntryFlowShellImpl({ > { render( { expect(screen.queryByText('137****6613')).toBeNull(); }); -test('PlatformWorkDetailView prefers resolved username for wooden fish works', () => { +test('PlatformWorkDetailView prefers display name then public user code for wooden fish works', () => { render( , ); - expect(screen.getByText('lotus_user')).toBeTruthy(); + expect(screen.getByText('公开昵称 · SY-00000004')).toBeTruthy(); + expect(screen.queryByText('phone_00000004')).toBeNull(); expect(screen.queryByText('敲木鱼玩家')).toBeNull(); expect(screen.queryByText('公开昵称')).toBeNull(); }); diff --git a/src/components/platform-entry/PlatformWorkDetailView.tsx b/src/components/platform-entry/PlatformWorkDetailView.tsx index a6ec1c88..5390415a 100644 --- a/src/components/platform-entry/PlatformWorkDetailView.tsx +++ b/src/components/platform-entry/PlatformWorkDetailView.tsx @@ -14,6 +14,7 @@ import { } from 'lucide-react'; import { useEffect, useMemo, useState } from 'react'; +import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth'; import { buildPublicWorkDetailUrl } from '../../routing/appPageRoutes'; import { copyTextToClipboard } from '../../services/clipboard'; import { ResolvedAssetImage } from '../ResolvedAssetImage'; @@ -33,9 +34,7 @@ import { export interface PlatformWorkDetailViewProps { entry: PlatformPublicGalleryCard; - authorAvatarUrl?: string | null; - authorUsername?: string | null; - authorDisplayName?: string | null; + authorSummary?: PublicUserSummary | null; isBusy: boolean; error?: string | null; visibleCoverCount?: number; @@ -87,9 +86,7 @@ const PLATFORM_WORK_COVER_CAROUSEL_INTERVAL_MS = 4200; export function PlatformWorkDetailView({ entry, - authorAvatarUrl, - authorUsername, - authorDisplayName, + authorSummary, isBusy, visibleCoverCount = 1, onBack, @@ -111,10 +108,10 @@ export function PlatformWorkDetailView({ const appIconImage = coverSlides[0]?.imageSrc ?? ''; const hasCoverCarousel = coverSlides.length > 1; const publicWorkCode = resolvePlatformPublicWorkCode(entry); - const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? ''; + const normalizedAuthorAvatarUrl = authorSummary?.avatarUrl?.trim() ?? ''; const resolvedAuthorDisplayName = resolvePlatformWorkAuthorDisplayName( entry, - authorUsername?.trim() || authorDisplayName?.trim() || null, + authorSummary, ); const [copyState, setCopyState] = useState<'idle' | 'copied' | 'failed'>( 'idle', diff --git a/src/components/rpg-entry/RpgEntryHomeView.tsx b/src/components/rpg-entry/RpgEntryHomeView.tsx index 9cd49445..b9657636 100644 --- a/src/components/rpg-entry/RpgEntryHomeView.tsx +++ b/src/components/rpg-entry/RpgEntryHomeView.tsx @@ -612,7 +612,7 @@ function WorldCard({ onClick, className, authorAvatarUrl, - authorUsername, + authorSummary, feedCardKey, enableCoverCarousel = false, isCoverCarouselActive = false, @@ -622,7 +622,7 @@ function WorldCard({ onClick: () => void; className?: string; authorAvatarUrl?: string | null; - authorUsername?: string | null; + authorSummary?: PublicUserSummary | null; feedCardKey?: string; enableCoverCarousel?: boolean; isCoverCarouselActive?: boolean; @@ -658,7 +658,7 @@ function WorldCard({ const typeLabel = describePublicGalleryCardKind(entry); const authorName = resolvePlatformWorkAuthorDisplayName( entry, - authorUsername, + authorSummary, ); const authorAvatarLabel = getPublicAuthorAvatarLabel(authorName); const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? ''; @@ -941,7 +941,7 @@ function RecommendRuntimePreviewCard({ function RecommendSwipeCard({ entry, authorAvatarUrl, - authorUsername, + authorSummary, isActive, visual, shareState, @@ -955,7 +955,7 @@ function RecommendSwipeCard({ }: { entry: PlatformPublicGalleryCard; authorAvatarUrl?: string | null; - authorUsername?: string | null; + authorSummary?: PublicUserSummary | null; isActive: boolean; visual: ReactNode; shareState?: 'idle' | 'copied' | 'failed'; @@ -980,7 +980,7 @@ function RecommendSwipeCard({ ) => void; onDragPointerMove?: (event: PointerEvent) => void; onDragPointerUp?: (event: PointerEvent) => void; @@ -1027,7 +1027,7 @@ function RecommendRuntimeMeta({ const remixCount = getPlatformWorldRemixCount(entry); const authorName = resolvePlatformWorkAuthorDisplayName( entry, - authorUsername, + authorSummary, ); const authorAvatarLabel = getPublicAuthorAvatarLabel(authorName); const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? ''; @@ -4192,14 +4192,14 @@ export function RpgEntryHomeView({ }, [publicAuthorSummariesByKey], ); - const getPublicEntryAuthorUsername = useCallback( + const getPublicEntryAuthorSummary = useCallback( (entry: PlatformPublicGalleryCard) => { const authorLookupKey = buildPublicWorkAuthorLookupKey(entry); if (!authorLookupKey) { return null; } - return publicAuthorSummariesByKey[authorLookupKey]?.username?.trim() || null; + return publicAuthorSummariesByKey[authorLookupKey] ?? null; }, [publicAuthorSummariesByKey], ); @@ -5548,7 +5548,7 @@ export function RpgEntryHomeView({ authorAvatarUrl={getPublicEntryAuthorAvatarUrl( previousRecommendEntry, )} - authorUsername={getPublicEntryAuthorUsername( + authorSummary={getPublicEntryAuthorSummary( previousRecommendEntry, )} isActive={false} @@ -5568,7 +5568,7 @@ export function RpgEntryHomeView({ authorAvatarUrl={getPublicEntryAuthorAvatarUrl( activeRecommendEntry, )} - authorUsername={getPublicEntryAuthorUsername( + authorSummary={getPublicEntryAuthorSummary( activeRecommendEntry, )} isActive @@ -5595,7 +5595,7 @@ export function RpgEntryHomeView({ authorAvatarUrl={getPublicEntryAuthorAvatarUrl( nextRecommendEntry, )} - authorUsername={getPublicEntryAuthorUsername( + authorSummary={getPublicEntryAuthorSummary( nextRecommendEntry, )} isActive={false} @@ -5751,7 +5751,7 @@ export function RpgEntryHomeView({ onClick={() => onOpenGalleryDetail(entry)} className="w-full" authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} - authorUsername={getPublicEntryAuthorUsername(entry)} + authorSummary={getPublicEntryAuthorSummary(entry)} feedCardKey={cardKey} /> ); @@ -5813,14 +5813,14 @@ export function RpgEntryHomeView({ return ( onOpenGalleryDetail(entry)} - className="w-full" - authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} - authorUsername={getPublicEntryAuthorUsername(entry)} - feedCardKey={cardKey} - enableCoverCarousel={mobileFeedCarouselEnabled} - isCoverCarouselActive={ + entry={entry} + onClick={() => onOpenGalleryDetail(entry)} + className="w-full" + authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} + authorSummary={getPublicEntryAuthorSummary(entry)} + feedCardKey={cardKey} + enableCoverCarousel={mobileFeedCarouselEnabled} + isCoverCarouselActive={ mobileCenteredCardKey === cardKey } /> @@ -5924,7 +5924,7 @@ export function RpgEntryHomeView({ onClick={() => openRecommendGalleryDetail(entry)} className="w-full min-w-0" authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} - authorUsername={getPublicEntryAuthorUsername(entry)} + authorSummary={getPublicEntryAuthorSummary(entry)} /> ))} @@ -5952,7 +5952,7 @@ export function RpgEntryHomeView({ onClick={() => openRecommendGalleryDetail(entry)} className="w-full min-w-0" authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} - authorUsername={getPublicEntryAuthorUsername(entry)} + authorSummary={getPublicEntryAuthorSummary(entry)} /> ))} {onOpenChildMotionDemo ? ( @@ -6013,7 +6013,7 @@ export function RpgEntryHomeView({ onClick={() => openRecommendGalleryDetail(entry)} className="w-full min-w-0" authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} - authorUsername={getPublicEntryAuthorUsername(entry)} + authorSummary={getPublicEntryAuthorSummary(entry)} /> ))} @@ -6529,7 +6529,7 @@ export function RpgEntryHomeView({ onClick={() => openRecommendGalleryDetail(entry)} className="w-full min-w-0" authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} - authorUsername={getPublicEntryAuthorUsername(entry)} + authorSummary={getPublicEntryAuthorSummary(entry)} /> ))} @@ -6700,7 +6700,7 @@ export function RpgEntryHomeView({ onClick={() => openRecommendGalleryDetail(entry)} className="w-full min-w-0" authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} - authorUsername={getPublicEntryAuthorUsername(entry)} + authorSummary={getPublicEntryAuthorSummary(entry)} /> ))} diff --git a/src/components/rpg-entry/rpgEntryWorldPresentation.test.ts b/src/components/rpg-entry/rpgEntryWorldPresentation.test.ts index 8628fae3..33918436 100644 --- a/src/components/rpg-entry/rpgEntryWorldPresentation.test.ts +++ b/src/components/rpg-entry/rpgEntryWorldPresentation.test.ts @@ -198,7 +198,7 @@ test('maps wooden fish work to platform gallery card with WF public code', () => expect(buildPlatformWorldDisplayTags(card, 2)).toEqual(['敲木鱼']); }); -test('resolves public work author from live username before stored author name', () => { +test('resolves public work author from display name and public user code before stored author name', () => { const card = mapWoodenFishWorkToPlatformGalleryCard({ publicWorkCode: 'WF-AUTHOR1', workId: 'wooden-fish-work-author', @@ -216,10 +216,25 @@ test('resolves public work author from live username before stored author name', generationStatus: 'ready', }); - expect(resolvePlatformWorkAuthorDisplayName(card, 'lotus_user')).toBe( - 'lotus_user', - ); - expect(resolvePlatformWorkAuthorDisplayName(card, ' ')).toBe('敲木鱼玩家'); + expect( + resolvePlatformWorkAuthorDisplayName(card, { + id: 'user_00000004', + publicUserCode: 'SY-00000004', + username: 'phone_00000004', + displayName: '公开昵称', + avatarUrl: null, + }), + ).toBe('公开昵称 · SY-00000004'); + expect( + resolvePlatformWorkAuthorDisplayName(card, { + id: 'user_00000004', + publicUserCode: '', + username: 'phone_00000004', + displayName: '公开昵称', + avatarUrl: null, + }), + ).toBe('公开昵称'); + expect(resolvePlatformWorkAuthorDisplayName(card, null)).toBe('敲木鱼玩家'); }); test('keeps baby object match public card code and template label intact', () => { diff --git a/src/components/rpg-entry/rpgEntryWorldPresentation.ts b/src/components/rpg-entry/rpgEntryWorldPresentation.ts index b81747e6..98e23f93 100644 --- a/src/components/rpg-entry/rpgEntryWorldPresentation.ts +++ b/src/components/rpg-entry/rpgEntryWorldPresentation.ts @@ -1,4 +1,5 @@ import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle'; +import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth'; import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary'; import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject'; import { BABY_OBJECT_MATCH_EDUTAINMENT_TAG } from '../../../packages/shared/src/contracts/edutainmentBabyObject'; @@ -864,9 +865,16 @@ export function formatPlatformWorkDisplayTags( export function resolvePlatformWorkAuthorDisplayName( entry: PlatformPublicGalleryCard, - authorUsername?: string | null, + authorSummary?: PublicUserSummary | null, ) { - return authorUsername?.trim() || entry.authorDisplayName.trim() || '玩家'; + const displayName = authorSummary?.displayName?.trim() ?? ''; + const publicUserCode = authorSummary?.publicUserCode?.trim() ?? ''; + + if (displayName && publicUserCode) { + return `${displayName} · ${publicUserCode}`; + } + + return displayName || publicUserCode || entry.authorDisplayName.trim() || '玩家'; } export function buildPlatformWorldDisplayTags(