fix public author display

This commit is contained in:
kdletters
2026-05-28 20:45:27 +08:00
parent 31afb2b18a
commit d2f838582f
6 changed files with 82 additions and 56 deletions

View File

@@ -15294,9 +15294,7 @@ export function PlatformEntryFlowShellImpl({
> >
<PlatformWorkDetailView <PlatformWorkDetailView
entry={selectedPublicWorkDetail} entry={selectedPublicWorkDetail}
authorAvatarUrl={selectedPublicWorkAuthor?.avatarUrl ?? null} authorSummary={selectedPublicWorkAuthor}
authorUsername={selectedPublicWorkAuthor?.username ?? null}
authorDisplayName={selectedPublicWorkAuthor?.displayName ?? null}
isBusy={ isBusy={
isPublicWorkDetailBusy || isPublicWorkDetailBusy ||
isPuzzleBusy || isPuzzleBusy ||
@@ -15342,11 +15340,7 @@ export function PlatformEntryFlowShellImpl({
) : selectedDetailEntry.visibility !== 'draft' ? ( ) : selectedDetailEntry.visibility !== 'draft' ? (
<PlatformWorkDetailView <PlatformWorkDetailView
entry={mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry)} entry={mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry)}
authorAvatarUrl={selectedPublicWorkAuthor?.avatarUrl ?? null} authorSummary={selectedPublicWorkAuthor}
authorUsername={selectedPublicWorkAuthor?.username ?? null}
authorDisplayName={
selectedPublicWorkAuthor?.displayName ?? null
}
isBusy={detailNavigation.isMutatingDetail} isBusy={detailNavigation.isMutatingDetail}
error={detailNavigation.detailError} error={detailNavigation.detailError}
actionMode={ actionMode={

View File

@@ -143,7 +143,13 @@ test('PlatformWorkDetailView prefers resolved public user display name', () => {
render( render(
<PlatformWorkDetailView <PlatformWorkDetailView
entry={createPuzzleEntry()} entry={createPuzzleEntry()}
authorDisplayName="新的作者昵称" authorSummary={{
id: 'user-2',
publicUserCode: '',
username: 'phone_00000002',
displayName: '新的作者昵称',
avatarUrl: null,
}}
isBusy={false} isBusy={false}
error={null} error={null}
onBack={vi.fn()} onBack={vi.fn()}
@@ -157,12 +163,17 @@ test('PlatformWorkDetailView prefers resolved public user display name', () => {
expect(screen.queryByText('137****6613')).toBeNull(); 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( render(
<PlatformWorkDetailView <PlatformWorkDetailView
entry={createWoodenFishEntry()} entry={createWoodenFishEntry()}
authorUsername="lotus_user" authorSummary={{
authorDisplayName="公开昵称" id: 'user-4',
publicUserCode: 'SY-00000004',
username: 'phone_00000004',
displayName: '公开昵称',
avatarUrl: null,
}}
isBusy={false} isBusy={false}
error={null} error={null}
onBack={vi.fn()} onBack={vi.fn()}
@@ -172,7 +183,8 @@ test('PlatformWorkDetailView prefers resolved username for wooden fish works', (
/>, />,
); );
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();
expect(screen.queryByText('公开昵称')).toBeNull(); expect(screen.queryByText('公开昵称')).toBeNull();
}); });

View File

@@ -14,6 +14,7 @@ import {
} from 'lucide-react'; } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import type { PublicUserSummary } from '../../../packages/shared/src/contracts/auth';
import { buildPublicWorkDetailUrl } from '../../routing/appPageRoutes'; import { buildPublicWorkDetailUrl } from '../../routing/appPageRoutes';
import { copyTextToClipboard } from '../../services/clipboard'; import { copyTextToClipboard } from '../../services/clipboard';
import { ResolvedAssetImage } from '../ResolvedAssetImage'; import { ResolvedAssetImage } from '../ResolvedAssetImage';
@@ -33,9 +34,7 @@ import {
export interface PlatformWorkDetailViewProps { export interface PlatformWorkDetailViewProps {
entry: PlatformPublicGalleryCard; entry: PlatformPublicGalleryCard;
authorAvatarUrl?: string | null; authorSummary?: PublicUserSummary | null;
authorUsername?: string | null;
authorDisplayName?: string | null;
isBusy: boolean; isBusy: boolean;
error?: string | null; error?: string | null;
visibleCoverCount?: number; visibleCoverCount?: number;
@@ -87,9 +86,7 @@ const PLATFORM_WORK_COVER_CAROUSEL_INTERVAL_MS = 4200;
export function PlatformWorkDetailView({ export function PlatformWorkDetailView({
entry, entry,
authorAvatarUrl, authorSummary,
authorUsername,
authorDisplayName,
isBusy, isBusy,
visibleCoverCount = 1, visibleCoverCount = 1,
onBack, onBack,
@@ -111,10 +108,10 @@ export function PlatformWorkDetailView({
const appIconImage = coverSlides[0]?.imageSrc ?? ''; const appIconImage = coverSlides[0]?.imageSrc ?? '';
const hasCoverCarousel = coverSlides.length > 1; const hasCoverCarousel = coverSlides.length > 1;
const publicWorkCode = resolvePlatformPublicWorkCode(entry); const publicWorkCode = resolvePlatformPublicWorkCode(entry);
const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? ''; const normalizedAuthorAvatarUrl = authorSummary?.avatarUrl?.trim() ?? '';
const resolvedAuthorDisplayName = resolvePlatformWorkAuthorDisplayName( const resolvedAuthorDisplayName = resolvePlatformWorkAuthorDisplayName(
entry, entry,
authorUsername?.trim() || authorDisplayName?.trim() || null, authorSummary,
); );
const [copyState, setCopyState] = useState<'idle' | 'copied' | 'failed'>( const [copyState, setCopyState] = useState<'idle' | 'copied' | 'failed'>(
'idle', 'idle',

View File

@@ -612,7 +612,7 @@ function WorldCard({
onClick, onClick,
className, className,
authorAvatarUrl, authorAvatarUrl,
authorUsername, authorSummary,
feedCardKey, feedCardKey,
enableCoverCarousel = false, enableCoverCarousel = false,
isCoverCarouselActive = false, isCoverCarouselActive = false,
@@ -622,7 +622,7 @@ function WorldCard({
onClick: () => void; onClick: () => void;
className?: string; className?: string;
authorAvatarUrl?: string | null; authorAvatarUrl?: string | null;
authorUsername?: string | null; authorSummary?: PublicUserSummary | null;
feedCardKey?: string; feedCardKey?: string;
enableCoverCarousel?: boolean; enableCoverCarousel?: boolean;
isCoverCarouselActive?: boolean; isCoverCarouselActive?: boolean;
@@ -658,7 +658,7 @@ function WorldCard({
const typeLabel = describePublicGalleryCardKind(entry); const typeLabel = describePublicGalleryCardKind(entry);
const authorName = resolvePlatformWorkAuthorDisplayName( const authorName = resolvePlatformWorkAuthorDisplayName(
entry, entry,
authorUsername, authorSummary,
); );
const authorAvatarLabel = getPublicAuthorAvatarLabel(authorName); const authorAvatarLabel = getPublicAuthorAvatarLabel(authorName);
const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? ''; const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? '';
@@ -941,7 +941,7 @@ function RecommendRuntimePreviewCard({
function RecommendSwipeCard({ function RecommendSwipeCard({
entry, entry,
authorAvatarUrl, authorAvatarUrl,
authorUsername, authorSummary,
isActive, isActive,
visual, visual,
shareState, shareState,
@@ -955,7 +955,7 @@ function RecommendSwipeCard({
}: { }: {
entry: PlatformPublicGalleryCard; entry: PlatformPublicGalleryCard;
authorAvatarUrl?: string | null; authorAvatarUrl?: string | null;
authorUsername?: string | null; authorSummary?: PublicUserSummary | null;
isActive: boolean; isActive: boolean;
visual: ReactNode; visual: ReactNode;
shareState?: 'idle' | 'copied' | 'failed'; shareState?: 'idle' | 'copied' | 'failed';
@@ -980,7 +980,7 @@ function RecommendSwipeCard({
<RecommendRuntimeMeta <RecommendRuntimeMeta
entry={entry} entry={entry}
authorAvatarUrl={authorAvatarUrl} authorAvatarUrl={authorAvatarUrl}
authorUsername={authorUsername} authorSummary={authorSummary}
isActive={isActive} isActive={isActive}
shareState={shareState} shareState={shareState}
onDragPointerDown={onDragPointerDown} onDragPointerDown={onDragPointerDown}
@@ -999,7 +999,7 @@ function RecommendSwipeCard({
function RecommendRuntimeMeta({ function RecommendRuntimeMeta({
entry, entry,
authorAvatarUrl, authorAvatarUrl,
authorUsername, authorSummary,
onDragPointerDown, onDragPointerDown,
onDragPointerMove, onDragPointerMove,
onDragPointerUp, onDragPointerUp,
@@ -1012,7 +1012,7 @@ function RecommendRuntimeMeta({
}: { }: {
entry: PlatformPublicGalleryCard; entry: PlatformPublicGalleryCard;
authorAvatarUrl?: string | null; authorAvatarUrl?: string | null;
authorUsername?: string | null; authorSummary?: PublicUserSummary | null;
onDragPointerDown?: (event: PointerEvent<HTMLElement>) => void; onDragPointerDown?: (event: PointerEvent<HTMLElement>) => void;
onDragPointerMove?: (event: PointerEvent<HTMLElement>) => void; onDragPointerMove?: (event: PointerEvent<HTMLElement>) => void;
onDragPointerUp?: (event: PointerEvent<HTMLElement>) => void; onDragPointerUp?: (event: PointerEvent<HTMLElement>) => void;
@@ -1027,7 +1027,7 @@ function RecommendRuntimeMeta({
const remixCount = getPlatformWorldRemixCount(entry); const remixCount = getPlatformWorldRemixCount(entry);
const authorName = resolvePlatformWorkAuthorDisplayName( const authorName = resolvePlatformWorkAuthorDisplayName(
entry, entry,
authorUsername, authorSummary,
); );
const authorAvatarLabel = getPublicAuthorAvatarLabel(authorName); const authorAvatarLabel = getPublicAuthorAvatarLabel(authorName);
const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? ''; const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? '';
@@ -4192,14 +4192,14 @@ export function RpgEntryHomeView({
}, },
[publicAuthorSummariesByKey], [publicAuthorSummariesByKey],
); );
const getPublicEntryAuthorUsername = useCallback( const getPublicEntryAuthorSummary = useCallback(
(entry: PlatformPublicGalleryCard) => { (entry: PlatformPublicGalleryCard) => {
const authorLookupKey = buildPublicWorkAuthorLookupKey(entry); const authorLookupKey = buildPublicWorkAuthorLookupKey(entry);
if (!authorLookupKey) { if (!authorLookupKey) {
return null; return null;
} }
return publicAuthorSummariesByKey[authorLookupKey]?.username?.trim() || null; return publicAuthorSummariesByKey[authorLookupKey] ?? null;
}, },
[publicAuthorSummariesByKey], [publicAuthorSummariesByKey],
); );
@@ -5548,7 +5548,7 @@ export function RpgEntryHomeView({
authorAvatarUrl={getPublicEntryAuthorAvatarUrl( authorAvatarUrl={getPublicEntryAuthorAvatarUrl(
previousRecommendEntry, previousRecommendEntry,
)} )}
authorUsername={getPublicEntryAuthorUsername( authorSummary={getPublicEntryAuthorSummary(
previousRecommendEntry, previousRecommendEntry,
)} )}
isActive={false} isActive={false}
@@ -5568,7 +5568,7 @@ export function RpgEntryHomeView({
authorAvatarUrl={getPublicEntryAuthorAvatarUrl( authorAvatarUrl={getPublicEntryAuthorAvatarUrl(
activeRecommendEntry, activeRecommendEntry,
)} )}
authorUsername={getPublicEntryAuthorUsername( authorSummary={getPublicEntryAuthorSummary(
activeRecommendEntry, activeRecommendEntry,
)} )}
isActive isActive
@@ -5595,7 +5595,7 @@ export function RpgEntryHomeView({
authorAvatarUrl={getPublicEntryAuthorAvatarUrl( authorAvatarUrl={getPublicEntryAuthorAvatarUrl(
nextRecommendEntry, nextRecommendEntry,
)} )}
authorUsername={getPublicEntryAuthorUsername( authorSummary={getPublicEntryAuthorSummary(
nextRecommendEntry, nextRecommendEntry,
)} )}
isActive={false} isActive={false}
@@ -5751,7 +5751,7 @@ export function RpgEntryHomeView({
onClick={() => onOpenGalleryDetail(entry)} onClick={() => onOpenGalleryDetail(entry)}
className="w-full" className="w-full"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorUsername={getPublicEntryAuthorUsername(entry)} authorSummary={getPublicEntryAuthorSummary(entry)}
feedCardKey={cardKey} feedCardKey={cardKey}
/> />
); );
@@ -5813,14 +5813,14 @@ export function RpgEntryHomeView({
return ( return (
<WorldCard <WorldCard
key={`${cardKey}:mobile-feed:${discoverChannel}`} key={`${cardKey}:mobile-feed:${discoverChannel}`}
entry={entry} entry={entry}
onClick={() => onOpenGalleryDetail(entry)} onClick={() => onOpenGalleryDetail(entry)}
className="w-full" className="w-full"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorUsername={getPublicEntryAuthorUsername(entry)} authorSummary={getPublicEntryAuthorSummary(entry)}
feedCardKey={cardKey} feedCardKey={cardKey}
enableCoverCarousel={mobileFeedCarouselEnabled} enableCoverCarousel={mobileFeedCarouselEnabled}
isCoverCarouselActive={ isCoverCarouselActive={
mobileCenteredCardKey === cardKey mobileCenteredCardKey === cardKey
} }
/> />
@@ -5924,7 +5924,7 @@ export function RpgEntryHomeView({
onClick={() => openRecommendGalleryDetail(entry)} onClick={() => openRecommendGalleryDetail(entry)}
className="w-full min-w-0" className="w-full min-w-0"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorUsername={getPublicEntryAuthorUsername(entry)} authorSummary={getPublicEntryAuthorSummary(entry)}
/> />
))} ))}
</div> </div>
@@ -5952,7 +5952,7 @@ export function RpgEntryHomeView({
onClick={() => openRecommendGalleryDetail(entry)} onClick={() => openRecommendGalleryDetail(entry)}
className="w-full min-w-0" className="w-full min-w-0"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorUsername={getPublicEntryAuthorUsername(entry)} authorSummary={getPublicEntryAuthorSummary(entry)}
/> />
))} ))}
{onOpenChildMotionDemo ? ( {onOpenChildMotionDemo ? (
@@ -6013,7 +6013,7 @@ export function RpgEntryHomeView({
onClick={() => openRecommendGalleryDetail(entry)} onClick={() => openRecommendGalleryDetail(entry)}
className="w-full min-w-0" className="w-full min-w-0"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorUsername={getPublicEntryAuthorUsername(entry)} authorSummary={getPublicEntryAuthorSummary(entry)}
/> />
))} ))}
</div> </div>
@@ -6529,7 +6529,7 @@ export function RpgEntryHomeView({
onClick={() => openRecommendGalleryDetail(entry)} onClick={() => openRecommendGalleryDetail(entry)}
className="w-full min-w-0" className="w-full min-w-0"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorUsername={getPublicEntryAuthorUsername(entry)} authorSummary={getPublicEntryAuthorSummary(entry)}
/> />
))} ))}
</div> </div>
@@ -6700,7 +6700,7 @@ export function RpgEntryHomeView({
onClick={() => openRecommendGalleryDetail(entry)} onClick={() => openRecommendGalleryDetail(entry)}
className="w-full min-w-0" className="w-full min-w-0"
authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)} authorAvatarUrl={getPublicEntryAuthorAvatarUrl(entry)}
authorUsername={getPublicEntryAuthorUsername(entry)} authorSummary={getPublicEntryAuthorSummary(entry)}
/> />
))} ))}
</div> </div>

View File

@@ -198,7 +198,7 @@ test('maps wooden fish work to platform gallery card with WF public code', () =>
expect(buildPlatformWorldDisplayTags(card, 2)).toEqual(['敲木鱼']); 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({ const card = mapWoodenFishWorkToPlatformGalleryCard({
publicWorkCode: 'WF-AUTHOR1', publicWorkCode: 'WF-AUTHOR1',
workId: 'wooden-fish-work-author', workId: 'wooden-fish-work-author',
@@ -216,10 +216,25 @@ test('resolves public work author from live username before stored author name',
generationStatus: 'ready', generationStatus: 'ready',
}); });
expect(resolvePlatformWorkAuthorDisplayName(card, 'lotus_user')).toBe( expect(
'lotus_user', resolvePlatformWorkAuthorDisplayName(card, {
); id: 'user_00000004',
expect(resolvePlatformWorkAuthorDisplayName(card, ' ')).toBe('敲木鱼玩家'); 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', () => { test('keeps baby object match public card code and template label intact', () => {

View File

@@ -1,4 +1,5 @@
import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle'; 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 { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject'; import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject';
import { BABY_OBJECT_MATCH_EDUTAINMENT_TAG } 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( export function resolvePlatformWorkAuthorDisplayName(
entry: PlatformPublicGalleryCard, 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( export function buildPlatformWorldDisplayTags(