refactor: 收口公开作者展示模型
This commit is contained in:
@@ -194,6 +194,7 @@ import {
|
||||
describePlatformPublicWorkKind,
|
||||
describePlatformThemeLabel,
|
||||
formatPlatformCompactCount,
|
||||
formatPlatformPublicAuthorAvatarLabel,
|
||||
formatPlatformWorkDisplayName,
|
||||
formatPlatformWorkDisplayTag,
|
||||
formatPlatformWorldTime,
|
||||
@@ -203,6 +204,8 @@ import {
|
||||
isPuzzleGalleryEntry,
|
||||
isVisualNovelGalleryEntry,
|
||||
type PlatformPublicGalleryCard,
|
||||
type PlatformPublicWorkAuthorLookup,
|
||||
resolvePlatformPublicWorkAuthorLookup,
|
||||
resolvePlatformPublicWorkCode,
|
||||
resolvePlatformWorkAuthorDisplayName,
|
||||
resolvePlatformWorldCoverImage,
|
||||
@@ -597,7 +600,7 @@ function WorldCard({
|
||||
entry,
|
||||
authorSummary,
|
||||
);
|
||||
const authorAvatarLabel = getPublicAuthorAvatarLabel(authorName);
|
||||
const authorAvatarLabel = formatPlatformPublicAuthorAvatarLabel(authorName);
|
||||
const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? '';
|
||||
const cardLabel = `${entry.worldName},${typeLabel},${formatPlatformCompactCount(playCount)}游玩,${formatPlatformCompactCount(remixCount)}改造,${formatPlatformCompactCount(likeCount)}点赞`;
|
||||
const coverStats = [
|
||||
@@ -966,7 +969,7 @@ function RecommendRuntimeMeta({
|
||||
entry,
|
||||
authorSummary,
|
||||
);
|
||||
const authorAvatarLabel = getPublicAuthorAvatarLabel(authorName);
|
||||
const authorAvatarLabel = formatPlatformPublicAuthorAvatarLabel(authorName);
|
||||
const normalizedAuthorAvatarUrl = authorAvatarUrl?.trim() ?? '';
|
||||
const displayName = formatPlatformWorkDisplayName(entry.worldName);
|
||||
const stopActionPointer = (event: PointerEvent<HTMLButtonElement>) => {
|
||||
@@ -1637,36 +1640,20 @@ function PlatformWorkSearchResults({
|
||||
);
|
||||
}
|
||||
|
||||
function buildPublicWorkAuthorLookupKey(entry: PlatformPublicGalleryCard) {
|
||||
if ('authorPublicUserCode' in entry) {
|
||||
const authorPublicUserCode = entry.authorPublicUserCode?.trim();
|
||||
if (authorPublicUserCode) {
|
||||
return `code:${authorPublicUserCode}`;
|
||||
}
|
||||
}
|
||||
|
||||
const ownerUserId = entry.ownerUserId.trim();
|
||||
return ownerUserId ? `id:${ownerUserId}` : null;
|
||||
}
|
||||
|
||||
async function getPublicWorkAuthorSummary(
|
||||
authorLookupKey: string,
|
||||
authorLookup: PlatformPublicWorkAuthorLookup,
|
||||
): Promise<PublicUserSummary | null> {
|
||||
if (authorLookupKey.startsWith('code:')) {
|
||||
return getPublicAuthUserByCode(authorLookupKey.slice('code:'.length));
|
||||
if (authorLookup.source === 'publicUserCode') {
|
||||
return getPublicAuthUserByCode(authorLookup.value);
|
||||
}
|
||||
|
||||
if (authorLookupKey.startsWith('id:')) {
|
||||
return getPublicAuthUserById(authorLookupKey.slice('id:'.length));
|
||||
if (authorLookup.source === 'ownerUserId') {
|
||||
return getPublicAuthUserById(authorLookup.value);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getPublicAuthorAvatarLabel(authorDisplayName: string) {
|
||||
return Array.from(authorDisplayName.trim() || '玩')[0] ?? '玩';
|
||||
}
|
||||
|
||||
function formatSnapshotTime(value: string | null | undefined) {
|
||||
if (!value) {
|
||||
return '刚刚保存';
|
||||
@@ -3619,25 +3606,25 @@ export function RpgEntryHomeView({
|
||||
);
|
||||
const getPublicEntryAuthorAvatarUrl = useCallback(
|
||||
(entry: PlatformPublicGalleryCard) => {
|
||||
const authorLookupKey = buildPublicWorkAuthorLookupKey(entry);
|
||||
if (!authorLookupKey) {
|
||||
const authorLookup = resolvePlatformPublicWorkAuthorLookup(entry);
|
||||
if (!authorLookup) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
publicAuthorSummariesByKey[authorLookupKey]?.avatarUrl?.trim() || null
|
||||
publicAuthorSummariesByKey[authorLookup.key]?.avatarUrl?.trim() || null
|
||||
);
|
||||
},
|
||||
[publicAuthorSummariesByKey],
|
||||
);
|
||||
const getPublicEntryAuthorSummary = useCallback(
|
||||
(entry: PlatformPublicGalleryCard) => {
|
||||
const authorLookupKey = buildPublicWorkAuthorLookupKey(entry);
|
||||
if (!authorLookupKey) {
|
||||
const authorLookup = resolvePlatformPublicWorkAuthorLookup(entry);
|
||||
if (!authorLookup) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return publicAuthorSummariesByKey[authorLookupKey] ?? null;
|
||||
return publicAuthorSummariesByKey[authorLookup.key] ?? null;
|
||||
},
|
||||
[publicAuthorSummariesByKey],
|
||||
);
|
||||
@@ -3775,37 +3762,38 @@ export function RpgEntryHomeView({
|
||||
}, [categoryGroups, selectedCategoryTag]);
|
||||
|
||||
useEffect(() => {
|
||||
const missingAuthorKeys = [
|
||||
...new Set(
|
||||
publicEntries
|
||||
.map(buildPublicWorkAuthorLookupKey)
|
||||
.filter((key): key is string => Boolean(key)),
|
||||
),
|
||||
].filter(
|
||||
(key) =>
|
||||
!(key in publicAuthorSummariesByKey) &&
|
||||
!pendingPublicAuthorKeysRef.current.has(key),
|
||||
const authorLookupsByKey = new Map<string, PlatformPublicWorkAuthorLookup>();
|
||||
publicEntries.forEach((entry) => {
|
||||
const authorLookup = resolvePlatformPublicWorkAuthorLookup(entry);
|
||||
if (authorLookup) {
|
||||
authorLookupsByKey.set(authorLookup.key, authorLookup);
|
||||
}
|
||||
});
|
||||
const missingAuthorLookups = Array.from(authorLookupsByKey.values()).filter(
|
||||
(authorLookup) =>
|
||||
!(authorLookup.key in publicAuthorSummariesByKey) &&
|
||||
!pendingPublicAuthorKeysRef.current.has(authorLookup.key),
|
||||
);
|
||||
|
||||
if (missingAuthorKeys.length === 0) {
|
||||
if (missingAuthorLookups.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
missingAuthorKeys.forEach((key) => {
|
||||
pendingPublicAuthorKeysRef.current.add(key);
|
||||
missingAuthorLookups.forEach((authorLookup) => {
|
||||
pendingPublicAuthorKeysRef.current.add(authorLookup.key);
|
||||
});
|
||||
|
||||
// 中文注释:头像来自公开用户摘要,失败时缓存空值,避免首页滚动时反复打公开用户接口。
|
||||
void Promise.all(
|
||||
missingAuthorKeys.map(async (authorLookupKey) => {
|
||||
missingAuthorLookups.map(async (authorLookup) => {
|
||||
try {
|
||||
const author = await getPublicWorkAuthorSummary(authorLookupKey);
|
||||
return [authorLookupKey, author] as const;
|
||||
const author = await getPublicWorkAuthorSummary(authorLookup);
|
||||
return [authorLookup.key, author] as const;
|
||||
} catch {
|
||||
return [authorLookupKey, null] as const;
|
||||
return [authorLookup.key, null] as const;
|
||||
} finally {
|
||||
pendingPublicAuthorKeysRef.current.delete(authorLookupKey);
|
||||
pendingPublicAuthorKeysRef.current.delete(authorLookup.key);
|
||||
}
|
||||
}),
|
||||
).then((results) => {
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID,
|
||||
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
|
||||
formatPlatformCompactCount,
|
||||
formatPlatformPublicAuthorAvatarLabel,
|
||||
formatPlatformWorkDisplayName,
|
||||
formatPlatformWorkDisplayTags,
|
||||
formatPlatformWorldTime,
|
||||
@@ -18,9 +19,11 @@ import {
|
||||
mapBarkBattleWorkToPlatformGalleryCard,
|
||||
mapVisualNovelWorkToPlatformGalleryCard,
|
||||
mapWoodenFishWorkToPlatformGalleryCard,
|
||||
type PlatformBarkBattleGalleryCard,
|
||||
type PlatformBigFishGalleryCard,
|
||||
type PlatformEdutainmentGalleryCard,
|
||||
type PlatformPuzzleGalleryCard,
|
||||
resolvePlatformPublicWorkAuthorLookup,
|
||||
resolvePlatformPublicWorkCode,
|
||||
resolvePlatformWorkAuthorDisplayName,
|
||||
resolvePlatformWorldFallbackCoverImage,
|
||||
@@ -312,6 +315,57 @@ test('public work author display hides phone masks and public user codes on card
|
||||
expect(resolvePlatformWorkAuthorDisplayName(card, null)).toBe('玩家');
|
||||
});
|
||||
|
||||
test('public work author lookup keeps public user code priority and avatar labels', () => {
|
||||
const barkBattleCard: PlatformBarkBattleGalleryCard = {
|
||||
sourceType: 'bark-battle',
|
||||
workId: 'bark-battle-work-author',
|
||||
profileId: 'bark-battle-profile-author',
|
||||
sourceSessionId: null,
|
||||
publicWorkCode: 'BB-AUTHOR',
|
||||
ownerUserId: 'user-author-id',
|
||||
authorPublicUserCode: ' SY-00012345 ',
|
||||
authorDisplayName: '声浪玩家',
|
||||
worldName: '声浪擂台',
|
||||
subtitle: '汪汪声浪',
|
||||
summaryText: '公开作品',
|
||||
coverImageSrc: null,
|
||||
coverRenderMode: 'image',
|
||||
coverCharacterImageSrcs: [],
|
||||
themeTags: ['声浪'],
|
||||
themeMode: 'martial',
|
||||
playableNpcCount: 0,
|
||||
landmarkCount: 0,
|
||||
visibility: 'published',
|
||||
publishedAt: '2026-05-22T00:00:00.000Z',
|
||||
updatedAt: '2026-05-22T00:00:00.000Z',
|
||||
};
|
||||
|
||||
expect(resolvePlatformPublicWorkAuthorLookup(barkBattleCard)).toEqual({
|
||||
key: 'code:SY-00012345',
|
||||
source: 'publicUserCode',
|
||||
value: 'SY-00012345',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkAuthorLookup({
|
||||
...barkBattleCard,
|
||||
authorPublicUserCode: ' ',
|
||||
}),
|
||||
).toEqual({
|
||||
key: 'id:user-author-id',
|
||||
source: 'ownerUserId',
|
||||
value: 'user-author-id',
|
||||
});
|
||||
expect(
|
||||
resolvePlatformPublicWorkAuthorLookup({
|
||||
...barkBattleCard,
|
||||
authorPublicUserCode: null,
|
||||
ownerUserId: ' ',
|
||||
}),
|
||||
).toBeNull();
|
||||
expect(formatPlatformPublicAuthorAvatarLabel(' 声浪玩家')).toBe('声');
|
||||
expect(formatPlatformPublicAuthorAvatarLabel('')).toBe('玩');
|
||||
});
|
||||
|
||||
test('keeps baby object match public card code and template label intact', () => {
|
||||
const card: PlatformEdutainmentGalleryCard = {
|
||||
sourceType: 'edutainment',
|
||||
|
||||
@@ -300,6 +300,12 @@ export type PlatformPublicGalleryCard =
|
||||
| PlatformBarkBattleGalleryCard
|
||||
| PlatformEdutainmentGalleryCard;
|
||||
|
||||
export type PlatformPublicWorkAuthorLookup = {
|
||||
key: string;
|
||||
source: 'publicUserCode' | 'ownerUserId';
|
||||
value: string;
|
||||
};
|
||||
|
||||
export function isLibraryWorldEntry(
|
||||
entry: PlatformWorldCardLike,
|
||||
): entry is CustomWorldLibraryEntry<CustomWorldProfile> {
|
||||
@@ -923,6 +929,36 @@ export function resolvePlatformWorkAuthorDisplayName(
|
||||
return displayName || entryAuthorName || '玩家';
|
||||
}
|
||||
|
||||
export function resolvePlatformPublicWorkAuthorLookup(
|
||||
entry: PlatformPublicGalleryCard,
|
||||
): PlatformPublicWorkAuthorLookup | null {
|
||||
if ('authorPublicUserCode' in entry) {
|
||||
const authorPublicUserCode = entry.authorPublicUserCode?.trim();
|
||||
if (authorPublicUserCode) {
|
||||
return {
|
||||
key: `code:${authorPublicUserCode}`,
|
||||
source: 'publicUserCode',
|
||||
value: authorPublicUserCode,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const ownerUserId = entry.ownerUserId.trim();
|
||||
return ownerUserId
|
||||
? {
|
||||
key: `id:${ownerUserId}`,
|
||||
source: 'ownerUserId',
|
||||
value: ownerUserId,
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
export function formatPlatformPublicAuthorAvatarLabel(
|
||||
authorDisplayName: string,
|
||||
) {
|
||||
return Array.from(authorDisplayName.trim() || '玩')[0] ?? '玩';
|
||||
}
|
||||
|
||||
function normalizePlatformPublicAuthorName(value: string | null | undefined) {
|
||||
const normalized = value?.trim() ?? '';
|
||||
if (!normalized || normalized === 'null' || normalized === 'undefined') {
|
||||
|
||||
Reference in New Issue
Block a user