fix: tighten public work type routing
This commit is contained in:
@@ -42,7 +42,10 @@ import type {
|
||||
CustomWorldGalleryCard,
|
||||
CustomWorldLibraryEntry,
|
||||
} from '../../../packages/shared/src/contracts/runtime';
|
||||
import type { WoodenFishWorkSummaryResponse } from '../../../packages/shared/src/contracts/woodenFish';
|
||||
import type {
|
||||
WoodenFishGalleryCardResponse,
|
||||
WoodenFishWorkSummaryResponse,
|
||||
} from '../../../packages/shared/src/contracts/woodenFish';
|
||||
import { normalizeCustomWorldProfileRecord } from '../../data/customWorldLibrary';
|
||||
import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes';
|
||||
import {
|
||||
@@ -155,6 +158,7 @@ import {
|
||||
deleteRpgEntryWorldProfile,
|
||||
getRpgEntryWorldGalleryDetail as getRpgEntryWorldGalleryDetailFromClient,
|
||||
getRpgEntryWorldGalleryDetailByCode,
|
||||
likeRpgEntryWorldGallery,
|
||||
recordRpgEntryWorldGalleryPlay,
|
||||
remixRpgEntryWorldGallery,
|
||||
} from '../../services/rpg-entry/rpgEntryLibraryClient';
|
||||
@@ -538,6 +542,7 @@ const rpgEntryLibraryServiceMocks = vi.hoisted(() => ({
|
||||
getRpgEntryWorldGalleryDetail: vi.fn(),
|
||||
getRpgEntryWorldGalleryDetailByCode: vi.fn(),
|
||||
getRpgEntryWorldLibraryDetail: vi.fn(),
|
||||
likeRpgEntryWorldGallery: vi.fn(),
|
||||
listRpgEntryWorldGallery: vi.fn(),
|
||||
listRpgEntryWorldLibrary: vi.fn(),
|
||||
publishRpgEntryWorldProfile: vi.fn(),
|
||||
@@ -7365,6 +7370,75 @@ test('home recommendation share opens publish share modal', async () => {
|
||||
.toBeTruthy();
|
||||
});
|
||||
|
||||
test('home recommendation wooden fish like does not call RPG gallery like', async () => {
|
||||
const user = userEvent.setup();
|
||||
const publishedWoodenFishWork: WoodenFishGalleryCardResponse = {
|
||||
publicWorkCode: 'WF-3A9EC89B',
|
||||
workId: 'wooden-fish-work-like-1',
|
||||
profileId: 'wooden-fish-profile-like-1',
|
||||
ownerUserId: 'wooden-fish-user-1',
|
||||
authorDisplayName: '木鱼作者',
|
||||
workTitle: '莲台木鱼',
|
||||
workDescription: '推荐页里的敲木鱼作品。',
|
||||
coverImageSrc: null,
|
||||
themeTags: ['敲木鱼'],
|
||||
publicationStatus: 'published',
|
||||
playCount: 0,
|
||||
updatedAt: '2026-04-25T09:00:00.000Z',
|
||||
publishedAt: '2026-04-25T09:00:00.000Z',
|
||||
generationStatus: 'ready',
|
||||
};
|
||||
|
||||
vi.mocked(woodenFishClient.listGallery).mockResolvedValue({
|
||||
items: [publishedWoodenFishWork],
|
||||
hasMore: false,
|
||||
nextCursor: null,
|
||||
});
|
||||
vi.mocked(woodenFishClient.startRun).mockResolvedValue({
|
||||
run: {
|
||||
runId: 'wooden-fish-run-like-1',
|
||||
profileId: publishedWoodenFishWork.profileId,
|
||||
ownerUserId: publishedWoodenFishWork.ownerUserId,
|
||||
status: 'playing',
|
||||
totalTapCount: 0,
|
||||
wordCounters: [],
|
||||
startedAtMs: 1,
|
||||
updatedAtMs: 1,
|
||||
finishedAtMs: null,
|
||||
},
|
||||
});
|
||||
vi.mocked(likeRpgEntryWorldGallery).mockResolvedValue(
|
||||
buildMockRpgGalleryDetail({
|
||||
ownerUserId: 'custom-world-user-1',
|
||||
profileId: 'custom-world-profile-1',
|
||||
publicWorkCode: 'CW-00000001',
|
||||
authorPublicUserCode: 'SY-00000001',
|
||||
visibility: 'published',
|
||||
publishedAt: '2026-04-25T09:00:00.000Z',
|
||||
updatedAt: '2026-04-25T09:00:00.000Z',
|
||||
authorDisplayName: 'RPG 作者',
|
||||
worldName: '不应被点赞的 RPG',
|
||||
subtitle: '错误分流',
|
||||
summaryText: 'WF 点赞不应进入这里。',
|
||||
coverImageSrc: null,
|
||||
themeMode: 'mythic',
|
||||
playableNpcCount: 0,
|
||||
landmarkCount: 0,
|
||||
likeCount: 1,
|
||||
}),
|
||||
);
|
||||
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
const meta = await screen.findByLabelText('莲台木鱼 作品信息');
|
||||
await user.click(within(meta).getByRole('button', { name: '点赞 0' }));
|
||||
|
||||
expect(likeRpgEntryWorldGallery).not.toHaveBeenCalled();
|
||||
expect(
|
||||
await screen.findByText('作品类型 wooden-fish 暂不支持点赞。'),
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test('home recommendation keeps logged-in puzzle start on default auth instead of guest token', async () => {
|
||||
const publishedPuzzleWork = {
|
||||
workId: 'puzzle-work-public-2',
|
||||
@@ -7467,12 +7541,6 @@ test('logged out home recommendation next starts the next puzzle work', async ()
|
||||
/>,
|
||||
);
|
||||
|
||||
const recommendNavButton = document.querySelector<HTMLButtonElement>(
|
||||
'.platform-bottom-nav [aria-label="推荐"]',
|
||||
);
|
||||
expect(recommendNavButton).toBeTruthy();
|
||||
await user.click(recommendNavButton!);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(startPuzzleRun).toHaveBeenCalledWith(
|
||||
{
|
||||
|
||||
@@ -144,6 +144,7 @@ import {
|
||||
formatPlatformWorldTime,
|
||||
isBarkBattleGalleryEntry,
|
||||
isBigFishGalleryEntry,
|
||||
isCustomWorldGalleryEntry,
|
||||
isEdutainmentGalleryEntry,
|
||||
isJumpHopGalleryEntry,
|
||||
isMatch3DGalleryEntry,
|
||||
@@ -373,11 +374,15 @@ type PlatformRankingTab = 'hot' | 'remix' | 'new' | 'like';
|
||||
type PlatformCategoryKindFilter =
|
||||
| 'all'
|
||||
| 'puzzle'
|
||||
| 'puzzle-clear'
|
||||
| 'jump-hop'
|
||||
| 'wooden-fish'
|
||||
| 'match3d'
|
||||
| 'square-hole'
|
||||
| 'visual-novel'
|
||||
| 'bark-battle'
|
||||
| 'big-fish'
|
||||
| 'edutainment'
|
||||
| 'custom-world';
|
||||
type PlatformCategorySortMode = 'composite' | 'latest' | 'play' | 'like';
|
||||
|
||||
@@ -413,11 +418,15 @@ const PLATFORM_CATEGORY_KIND_FILTERS: Array<{
|
||||
}> = [
|
||||
{ id: 'all', label: '全部' },
|
||||
{ id: 'puzzle', label: '拼图' },
|
||||
{ id: 'puzzle-clear', label: '拼消' },
|
||||
{ id: 'jump-hop', label: '跳一跳' },
|
||||
{ id: 'wooden-fish', label: '木鱼' },
|
||||
{ id: 'match3d', label: '抓鹅' },
|
||||
{ id: 'square-hole', label: '方洞' },
|
||||
{ id: 'visual-novel', label: '视觉' },
|
||||
{ id: 'bark-battle', label: '汪汪' },
|
||||
{ id: 'big-fish', label: '大鱼' },
|
||||
{ id: 'edutainment', label: EDUTAINMENT_WORK_TAG },
|
||||
{ id: 'custom-world', label: 'RPG' },
|
||||
];
|
||||
const PLATFORM_CATEGORY_SORT_OPTIONS: Array<{
|
||||
@@ -2192,6 +2201,18 @@ function getPlatformCategoryKindFilter(entry: PlatformPublicGalleryCard) {
|
||||
return 'puzzle';
|
||||
}
|
||||
|
||||
if (isPuzzleClearGalleryEntry(entry)) {
|
||||
return 'puzzle-clear';
|
||||
}
|
||||
|
||||
if (isJumpHopGalleryEntry(entry)) {
|
||||
return 'jump-hop';
|
||||
}
|
||||
|
||||
if (isWoodenFishGalleryEntry(entry)) {
|
||||
return 'wooden-fish';
|
||||
}
|
||||
|
||||
if (isMatch3DGalleryEntry(entry)) {
|
||||
return 'match3d';
|
||||
}
|
||||
@@ -2212,7 +2233,15 @@ function getPlatformCategoryKindFilter(entry: PlatformPublicGalleryCard) {
|
||||
return 'big-fish';
|
||||
}
|
||||
|
||||
return 'custom-world';
|
||||
if (isEdutainmentGalleryEntry(entry)) {
|
||||
return 'edutainment';
|
||||
}
|
||||
|
||||
if (isCustomWorldGalleryEntry(entry)) {
|
||||
return 'custom-world';
|
||||
}
|
||||
|
||||
throw new Error('未知公开作品类型。');
|
||||
}
|
||||
|
||||
function matchesPlatformCategoryKindFilter(
|
||||
|
||||
@@ -23,6 +23,7 @@ import type {
|
||||
CustomWorldGalleryCard,
|
||||
CustomWorldLibraryEntry,
|
||||
} from '../../../packages/shared/src/contracts/runtime';
|
||||
import type { PublicWorkSourceType } from '../../../packages/shared/src/contracts/playTypes';
|
||||
import type {
|
||||
SquareHoleHoleOption,
|
||||
SquareHoleShapeOption,
|
||||
@@ -55,8 +56,12 @@ export const PLATFORM_WORK_TAG_DISPLAY_LIMIT = 4;
|
||||
export const EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID = 'baby-object-match';
|
||||
export const EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME = '宝贝识物';
|
||||
|
||||
export type PlatformCustomWorldGalleryCard = CustomWorldGalleryCard & {
|
||||
sourceType?: 'custom-world';
|
||||
};
|
||||
|
||||
export type PlatformWorldCardLike =
|
||||
| CustomWorldGalleryCard
|
||||
| PlatformCustomWorldGalleryCard
|
||||
| CustomWorldLibraryEntry<CustomWorldProfile>
|
||||
| PlatformBigFishGalleryCard
|
||||
| PlatformMatch3DGalleryCard
|
||||
@@ -319,7 +324,7 @@ export type PlatformBarkBattleGalleryCard = {
|
||||
};
|
||||
|
||||
export type PlatformPublicGalleryCard =
|
||||
| CustomWorldGalleryCard
|
||||
| PlatformCustomWorldGalleryCard
|
||||
| PlatformBigFishGalleryCard
|
||||
| PlatformMatch3DGalleryCard
|
||||
| PlatformSquareHoleGalleryCard
|
||||
@@ -337,6 +342,14 @@ export function isLibraryWorldEntry(
|
||||
return 'profile' in entry;
|
||||
}
|
||||
|
||||
export function isCustomWorldGalleryEntry(
|
||||
entry: PlatformWorldCardLike,
|
||||
): entry is PlatformCustomWorldGalleryCard {
|
||||
return !isLibraryWorldEntry(entry) && !('sourceType' in entry)
|
||||
? true
|
||||
: 'sourceType' in entry && entry.sourceType === 'custom-world';
|
||||
}
|
||||
|
||||
export function isPuzzleGalleryEntry(
|
||||
entry: PlatformWorldCardLike,
|
||||
): entry is PlatformPuzzleGalleryCard {
|
||||
@@ -397,28 +410,62 @@ export function isBarkBattleGalleryEntry(
|
||||
return 'sourceType' in entry && entry.sourceType === 'bark-battle';
|
||||
}
|
||||
|
||||
export function resolvePlatformPublicWorkSourceType(
|
||||
entry: PlatformPublicGalleryCard,
|
||||
): PublicWorkSourceType {
|
||||
if (isCustomWorldGalleryEntry(entry)) {
|
||||
return 'custom-world';
|
||||
}
|
||||
|
||||
if (isBigFishGalleryEntry(entry)) {
|
||||
return 'big-fish';
|
||||
}
|
||||
|
||||
if (isPuzzleGalleryEntry(entry)) {
|
||||
return 'puzzle';
|
||||
}
|
||||
|
||||
if (isPuzzleClearGalleryEntry(entry)) {
|
||||
return 'puzzle-clear';
|
||||
}
|
||||
|
||||
if (isJumpHopGalleryEntry(entry)) {
|
||||
return 'jump-hop';
|
||||
}
|
||||
|
||||
if (isWoodenFishGalleryEntry(entry)) {
|
||||
return 'wooden-fish';
|
||||
}
|
||||
|
||||
if (isMatch3DGalleryEntry(entry)) {
|
||||
return 'match3d';
|
||||
}
|
||||
|
||||
if (isSquareHoleGalleryEntry(entry)) {
|
||||
return 'square-hole';
|
||||
}
|
||||
|
||||
if (isVisualNovelGalleryEntry(entry)) {
|
||||
return 'visual-novel';
|
||||
}
|
||||
|
||||
if (isBarkBattleGalleryEntry(entry)) {
|
||||
return 'bark-battle';
|
||||
}
|
||||
|
||||
if (isEdutainmentGalleryEntry(entry)) {
|
||||
return 'edutainment';
|
||||
}
|
||||
|
||||
throw new Error('未知公开作品类型。');
|
||||
}
|
||||
|
||||
export function buildPlatformPublicGalleryCardKey(
|
||||
entry: PlatformPublicGalleryCard,
|
||||
) {
|
||||
const kind = isBigFishGalleryEntry(entry)
|
||||
? 'big-fish'
|
||||
: isPuzzleGalleryEntry(entry)
|
||||
? 'puzzle'
|
||||
: isJumpHopGalleryEntry(entry)
|
||||
? 'jump-hop'
|
||||
: isWoodenFishGalleryEntry(entry)
|
||||
? 'wooden-fish'
|
||||
: isMatch3DGalleryEntry(entry)
|
||||
? 'match3d'
|
||||
: isSquareHoleGalleryEntry(entry)
|
||||
? 'square-hole'
|
||||
: isVisualNovelGalleryEntry(entry)
|
||||
? 'visual-novel'
|
||||
: isBarkBattleGalleryEntry(entry)
|
||||
? 'bark-battle'
|
||||
: isEdutainmentGalleryEntry(entry)
|
||||
? `edutainment:${entry.templateId}`
|
||||
: 'rpg';
|
||||
const kind = isEdutainmentGalleryEntry(entry)
|
||||
? `edutainment:${entry.templateId}`
|
||||
: resolvePlatformPublicWorkSourceType(entry);
|
||||
return `${kind}:${entry.ownerUserId}:${entry.profileId}`;
|
||||
}
|
||||
|
||||
@@ -868,7 +915,11 @@ export function resolvePlatformWorldFallbackCoverImage(
|
||||
return '/creation-type-references/bark-battle.webp';
|
||||
}
|
||||
|
||||
return '/creation-type-references/rpg.webp';
|
||||
if (isCustomWorldGalleryEntry(entry) || isLibraryWorldEntry(entry)) {
|
||||
return '/creation-type-references/rpg.webp';
|
||||
}
|
||||
|
||||
throw new Error('未知公开作品类型。');
|
||||
}
|
||||
|
||||
export function resolvePlatformWorldCoverSlides(
|
||||
|
||||
Reference in New Issue
Block a user