fix public work author display

This commit is contained in:
kdletters
2026-05-28 17:47:31 +08:00
parent f1fb92aa29
commit 31afb2b18a
6 changed files with 88 additions and 15 deletions

View File

@@ -15295,6 +15295,7 @@ export function PlatformEntryFlowShellImpl({
<PlatformWorkDetailView <PlatformWorkDetailView
entry={selectedPublicWorkDetail} entry={selectedPublicWorkDetail}
authorAvatarUrl={selectedPublicWorkAuthor?.avatarUrl ?? null} authorAvatarUrl={selectedPublicWorkAuthor?.avatarUrl ?? null}
authorUsername={selectedPublicWorkAuthor?.username ?? null}
authorDisplayName={selectedPublicWorkAuthor?.displayName ?? null} authorDisplayName={selectedPublicWorkAuthor?.displayName ?? null}
isBusy={ isBusy={
isPublicWorkDetailBusy || isPublicWorkDetailBusy ||
@@ -15342,6 +15343,7 @@ export function PlatformEntryFlowShellImpl({
<PlatformWorkDetailView <PlatformWorkDetailView
entry={mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry)} entry={mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry)}
authorAvatarUrl={selectedPublicWorkAuthor?.avatarUrl ?? null} authorAvatarUrl={selectedPublicWorkAuthor?.avatarUrl ?? null}
authorUsername={selectedPublicWorkAuthor?.username ?? null}
authorDisplayName={ authorDisplayName={
selectedPublicWorkAuthor?.displayName ?? null selectedPublicWorkAuthor?.displayName ?? null
} }

View File

@@ -9,6 +9,7 @@ import {
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME, EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
type PlatformEdutainmentGalleryCard, type PlatformEdutainmentGalleryCard,
type PlatformPuzzleGalleryCard, type PlatformPuzzleGalleryCard,
type PlatformWoodenFishGalleryCard,
} from '../rpg-entry/rpgEntryWorldPresentation'; } from '../rpg-entry/rpgEntryWorldPresentation';
import { PlatformWorkDetailView } from './PlatformWorkDetailView'; import { PlatformWorkDetailView } from './PlatformWorkDetailView';
@@ -82,6 +83,29 @@ function createBabyObjectMatchEntry(): PlatformEdutainmentGalleryCard {
}; };
} }
function createWoodenFishEntry(): PlatformWoodenFishGalleryCard {
return {
sourceType: 'wooden-fish',
workId: 'wooden-fish-work-1',
profileId: 'wooden-fish-profile-1',
publicWorkCode: 'WF-001',
ownerUserId: 'user-wooden-fish',
authorDisplayName: '敲木鱼玩家',
worldName: '莲花木鱼',
subtitle: '敲木鱼',
summaryText: '莲花主题敲木鱼。',
coverImageSrc: null,
themeTags: ['敲木鱼'],
playCount: 12,
remixCount: 0,
likeCount: 4,
recentPlayCount7d: 0,
visibility: 'published',
publishedAt: '2026-05-20T10:00:00.000Z',
updatedAt: '2026-05-20T12:00:00.000Z',
};
}
afterEach(() => { afterEach(() => {
vi.useRealTimers(); vi.useRealTimers();
}); });
@@ -133,6 +157,26 @@ 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', () => {
render(
<PlatformWorkDetailView
entry={createWoodenFishEntry()}
authorUsername="lotus_user"
authorDisplayName="公开昵称"
isBusy={false}
error={null}
onBack={vi.fn()}
onLike={vi.fn()}
onStart={vi.fn()}
onRemix={vi.fn()}
/>,
);
expect(screen.getByText('lotus_user')).toBeTruthy();
expect(screen.queryByText('敲木鱼玩家')).toBeNull();
expect(screen.queryByText('公开昵称')).toBeNull();
});
test('PlatformWorkDetailView calls like handler', () => { test('PlatformWorkDetailView calls like handler', () => {
const onLike = vi.fn(); const onLike = vi.fn();
render( render(

View File

@@ -25,6 +25,7 @@ import {
isBarkBattleGalleryEntry, isBarkBattleGalleryEntry,
isEdutainmentGalleryEntry, isEdutainmentGalleryEntry,
type PlatformPublicGalleryCard, type PlatformPublicGalleryCard,
resolvePlatformWorkAuthorDisplayName,
resolvePlatformPublicWorkCode, resolvePlatformPublicWorkCode,
resolvePlatformWorldCoverSlides, resolvePlatformWorldCoverSlides,
resolvePlatformWorldStats, resolvePlatformWorldStats,
@@ -33,6 +34,7 @@ import {
export interface PlatformWorkDetailViewProps { export interface PlatformWorkDetailViewProps {
entry: PlatformPublicGalleryCard; entry: PlatformPublicGalleryCard;
authorAvatarUrl?: string | null; authorAvatarUrl?: string | null;
authorUsername?: string | null;
authorDisplayName?: string | null; authorDisplayName?: string | null;
isBusy: boolean; isBusy: boolean;
error?: string | null; error?: string | null;
@@ -86,6 +88,7 @@ const PLATFORM_WORK_COVER_CAROUSEL_INTERVAL_MS = 4200;
export function PlatformWorkDetailView({ export function PlatformWorkDetailView({
entry, entry,
authorAvatarUrl, authorAvatarUrl,
authorUsername,
authorDisplayName, authorDisplayName,
isBusy, isBusy,
visibleCoverCount = 1, visibleCoverCount = 1,
@@ -109,8 +112,10 @@ export function PlatformWorkDetailView({
const hasCoverCarousel = coverSlides.length > 1; const hasCoverCarousel = coverSlides.length > 1;
const publicWorkCode = resolvePlatformPublicWorkCode(entry); const publicWorkCode = resolvePlatformPublicWorkCode(entry);
const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? ''; const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? '';
const resolvedAuthorDisplayName = const resolvedAuthorDisplayName = resolvePlatformWorkAuthorDisplayName(
authorDisplayName?.trim() || entry.authorDisplayName; entry,
authorUsername?.trim() || authorDisplayName?.trim() || null,
);
const [copyState, setCopyState] = useState<'idle' | 'copied' | 'failed'>( const [copyState, setCopyState] = useState<'idle' | 'copied' | 'failed'>(
'idle', 'idle',
); );

View File

@@ -149,6 +149,7 @@ import {
isWoodenFishGalleryEntry, isWoodenFishGalleryEntry,
type PlatformPublicGalleryCard, type PlatformPublicGalleryCard,
type PlatformWorldCardLike, type PlatformWorldCardLike,
resolvePlatformWorkAuthorDisplayName,
resolvePlatformPublicWorkCode, resolvePlatformPublicWorkCode,
resolvePlatformWorldCoverImage, resolvePlatformWorldCoverImage,
resolvePlatformWorldCoverSlides, resolvePlatformWorldCoverSlides,
@@ -655,7 +656,7 @@ function WorldCard({
const remixCount = getPlatformWorldRemixCount(entry); const remixCount = getPlatformWorldRemixCount(entry);
const likeCount = getPlatformWorldLikeCount(entry); const likeCount = getPlatformWorldLikeCount(entry);
const typeLabel = describePublicGalleryCardKind(entry); const typeLabel = describePublicGalleryCardKind(entry);
const authorName = resolvePublicEntryAuthorDisplayText( const authorName = resolvePlatformWorkAuthorDisplayName(
entry, entry,
authorUsername, authorUsername,
); );
@@ -1024,7 +1025,7 @@ function RecommendRuntimeMeta({
}) { }) {
const likeCount = getPlatformWorldLikeCount(entry); const likeCount = getPlatformWorldLikeCount(entry);
const remixCount = getPlatformWorldRemixCount(entry); const remixCount = getPlatformWorldRemixCount(entry);
const authorName = resolvePublicEntryAuthorDisplayText( const authorName = resolvePlatformWorkAuthorDisplayName(
entry, entry,
authorUsername, authorUsername,
); );
@@ -1935,17 +1936,6 @@ function getPublicAuthorAvatarLabel(authorDisplayName: string) {
return Array.from(authorDisplayName.trim() || '玩')[0] ?? '玩'; return Array.from(authorDisplayName.trim() || '玩')[0] ?? '玩';
} }
function resolvePublicEntryAuthorDisplayText(
entry: PlatformPublicGalleryCard,
authorUsername?: string | null,
) {
if (isWoodenFishGalleryEntry(entry)) {
return authorUsername?.trim() || entry.authorDisplayName.trim() || '玩家';
}
return entry.authorDisplayName.trim() || '玩家';
}
function getPlatformWorldLikeCount(entry: PlatformWorldCardLike) { function getPlatformWorldLikeCount(entry: PlatformWorldCardLike) {
return Math.max(0, Math.round(entry.likeCount ?? 0)); return Math.max(0, Math.round(entry.likeCount ?? 0));
} }

View File

@@ -18,6 +18,7 @@ import {
mapWoodenFishWorkToPlatformGalleryCard, mapWoodenFishWorkToPlatformGalleryCard,
type PlatformEdutainmentGalleryCard, type PlatformEdutainmentGalleryCard,
type PlatformPuzzleGalleryCard, type PlatformPuzzleGalleryCard,
resolvePlatformWorkAuthorDisplayName,
resolvePlatformPublicWorkCode, resolvePlatformPublicWorkCode,
resolvePlatformWorldFallbackCoverImage, resolvePlatformWorldFallbackCoverImage,
} from './rpgEntryWorldPresentation'; } from './rpgEntryWorldPresentation';
@@ -197,6 +198,30 @@ 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', () => {
const card = mapWoodenFishWorkToPlatformGalleryCard({
publicWorkCode: 'WF-AUTHOR1',
workId: 'wooden-fish-work-author',
profileId: 'wooden-fish-profile-author',
ownerUserId: 'user-author',
authorDisplayName: '敲木鱼玩家',
workTitle: '莲花木鱼',
workDescription: '莲花主题敲木鱼。',
coverImageSrc: null,
themeTags: ['敲木鱼'],
publicationStatus: 'published',
playCount: 0,
updatedAt: '2026-05-20T00:00:00.000Z',
publishedAt: '2026-05-20T00:00:00.000Z',
generationStatus: 'ready',
});
expect(resolvePlatformWorkAuthorDisplayName(card, 'lotus_user')).toBe(
'lotus_user',
);
expect(resolvePlatformWorkAuthorDisplayName(card, ' ')).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', () => {
const card: PlatformEdutainmentGalleryCard = { const card: PlatformEdutainmentGalleryCard = {
sourceType: 'edutainment', sourceType: 'edutainment',

View File

@@ -862,6 +862,13 @@ export function formatPlatformWorkDisplayTags(
].slice(0, limit); ].slice(0, limit);
} }
export function resolvePlatformWorkAuthorDisplayName(
entry: PlatformPublicGalleryCard,
authorUsername?: string | null,
) {
return authorUsername?.trim() || entry.authorDisplayName.trim() || '玩家';
}
export function buildPlatformWorldDisplayTags( export function buildPlatformWorldDisplayTags(
entry: PlatformWorldCardLike, entry: PlatformWorldCardLike,
limit = 3, limit = 3,