refactor: 深化公开作品详情状态策略

This commit is contained in:
2026-06-03 22:38:26 +08:00
parent 00820e6571
commit dd52848e9c
5 changed files with 199 additions and 20 deletions

View File

@@ -516,7 +516,9 @@ import {
type RecommendRuntimeKind,
} from './platformPublicGalleryFlow';
import {
resolveActivePlatformPublicWorkAuthorEntry,
resolvePlatformPublicWorkActionMode,
resolvePlatformPublicWorkDetailOpenDecision,
resolvePlatformPublicWorkDetailOpenStrategy,
} from './platformPublicWorkDetailFlow';
import {
@@ -10908,20 +10910,19 @@ export function PlatformEntryFlowShellImpl({
const openPublicWorkDetail = useCallback(
(entry: PlatformPublicGalleryCard) => {
if (!canExposePublicWork(entry)) {
setSelectedPublicWorkDetail(null);
setPublicWorkDetailError(EDUTAINMENT_HIDDEN_MESSAGE);
setSelectionStage('platform');
const decision = resolvePlatformPublicWorkDetailOpenDecision(entry);
if (decision.type === 'blocked') {
setSelectedPublicWorkDetail(decision.selectedDetail);
setPublicWorkDetailError(decision.errorMessage);
setSelectionStage(decision.selectionStage);
return;
}
setSelectedPublicWorkDetail(entry);
setPublicWorkDetailError(null);
setSelectionStage('work-detail');
if (entry.publicWorkCode?.trim()) {
pushAppHistoryPath(
buildPublicWorkStagePath('work-detail', entry.publicWorkCode),
);
setSelectedPublicWorkDetail(decision.selectedDetail);
setPublicWorkDetailError(decision.errorMessage);
setSelectionStage(decision.selectionStage);
if (decision.historyPath) {
pushAppHistoryPath(decision.historyPath);
}
},
[setSelectionStage],
@@ -11118,14 +11119,11 @@ export function PlatformEntryFlowShellImpl({
);
useEffect(() => {
const detailEntry =
selectionStage === 'work-detail'
? selectedPublicWorkDetail
: selectionStage === 'detail' &&
selectedDetailEntry &&
selectedDetailEntry.visibility !== 'draft'
? mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry)
: null;
const detailEntry = resolveActivePlatformPublicWorkAuthorEntry({
selectionStage,
selectedPublicWorkDetail,
selectedRpgDetailEntry: selectedDetailEntry,
});
if (!detailEntry) {
clearSelectedPublicWorkAuthor();

View File

@@ -10,7 +10,9 @@ import {
getPlatformPublicWorkDetailKind,
type PlatformPublicWorkDetailKind,
type PlatformPublicWorkDetailOpenStrategy,
resolveActivePlatformPublicWorkAuthorEntry,
resolvePlatformPublicWorkActionMode,
resolvePlatformPublicWorkDetailOpenDecision,
resolvePlatformPublicWorkDetailOpenStrategy,
} from './platformPublicWorkDetailFlow';
@@ -227,3 +229,91 @@ test('platform public work detail flow resolves edit mode only for owned works',
expect(resolvePlatformPublicWorkActionMode(entry, 'user-2')).toBe('remix');
expect(resolvePlatformPublicWorkActionMode(entry, null)).toBe('remix');
});
test('platform public work detail flow resolves direct open decision', () => {
const entry = buildTypedEntry('match3d', {
publicWorkCode: ' M3D-001 ',
});
const buildWorkDetailPath = (publicWorkCode: string) =>
`/works/detail?work=${publicWorkCode.trim()}`;
expect(
resolvePlatformPublicWorkDetailOpenDecision(entry, {
buildWorkDetailPath,
}),
).toEqual({
type: 'open',
selectedDetail: entry,
errorMessage: null,
selectionStage: 'work-detail',
historyPath: '/works/detail?work=M3D-001',
});
expect(
resolvePlatformPublicWorkDetailOpenDecision(
buildTypedEntry('match3d', { publicWorkCode: ' ' }),
{
buildWorkDetailPath,
},
),
).toEqual({
type: 'open',
selectedDetail: buildTypedEntry('match3d', { publicWorkCode: ' ' }),
errorMessage: null,
selectionStage: 'work-detail',
historyPath: null,
});
expect(
resolvePlatformPublicWorkDetailOpenDecision(entry, {
canExposeEntry: () => false,
hiddenMessage: '隐藏',
buildWorkDetailPath,
}),
).toEqual({
type: 'blocked',
selectedDetail: null,
errorMessage: '隐藏',
selectionStage: 'platform',
historyPath: null,
});
});
test('platform public work detail flow selects author lookup entry by stage', () => {
const selectedPublicWorkDetail = buildTypedEntry('puzzle');
const publishedRpgEntry = buildRpgEntry({
visibility: 'published',
profileId: 'published-rpg-profile',
});
const draftRpgEntry = buildRpgEntry({
visibility: 'draft',
profileId: 'draft-rpg-profile',
});
expect(
resolveActivePlatformPublicWorkAuthorEntry({
selectionStage: 'work-detail',
selectedPublicWorkDetail,
selectedRpgDetailEntry: publishedRpgEntry,
}),
).toBe(selectedPublicWorkDetail);
expect(
resolveActivePlatformPublicWorkAuthorEntry({
selectionStage: 'detail',
selectedPublicWorkDetail: null,
selectedRpgDetailEntry: publishedRpgEntry,
}),
).toBe(publishedRpgEntry);
expect(
resolveActivePlatformPublicWorkAuthorEntry({
selectionStage: 'detail',
selectedPublicWorkDetail: null,
selectedRpgDetailEntry: draftRpgEntry,
}),
).toBeNull();
expect(
resolveActivePlatformPublicWorkAuthorEntry({
selectionStage: 'platform',
selectedPublicWorkDetail,
selectedRpgDetailEntry: publishedRpgEntry,
}),
).toBeNull();
});

View File

@@ -1,4 +1,5 @@
import type { CustomWorldGalleryCard } from '../../../packages/shared/src/contracts/runtime';
import { buildPublicWorkStagePath } from '../../routing/appPageRoutes';
import {
isBarkBattleGalleryEntry,
isBigFishGalleryEntry,
@@ -11,6 +12,10 @@ import {
isWoodenFishGalleryEntry,
type PlatformPublicGalleryCard,
} from '../rpg-entry/rpgEntryWorldPresentation';
import {
canExposePublicWork,
EDUTAINMENT_HIDDEN_MESSAGE,
} from './platformEdutainmentVisibility';
export type PlatformPublicWorkDetailKind =
| 'bark-battle'
@@ -55,6 +60,34 @@ export type PlatformPublicWorkDetailOpenStrategy =
export type PlatformPublicWorkActionMode = 'edit' | 'remix';
export type PlatformPublicWorkDetailOpenDecision =
| {
type: 'blocked';
selectedDetail: null;
errorMessage: string;
selectionStage: 'platform';
historyPath: null;
}
| {
type: 'open';
selectedDetail: PlatformPublicGalleryCard;
errorMessage: null;
selectionStage: 'work-detail';
historyPath: string | null;
};
export type PlatformPublicWorkDetailOpenDecisionDeps = {
canExposeEntry?: (entry: PlatformPublicGalleryCard) => boolean;
hiddenMessage?: string;
buildWorkDetailPath?: (publicWorkCode: string) => string;
};
export type ActivePlatformPublicWorkAuthorEntryInput = {
selectionStage: string;
selectedPublicWorkDetail: PlatformPublicGalleryCard | null;
selectedRpgDetailEntry: CustomWorldGalleryCard | null;
};
export function isRpgPublicWorkDetailEntry(
entry: PlatformPublicGalleryCard,
): entry is CustomWorldGalleryCard {
@@ -188,3 +221,57 @@ export function resolvePlatformPublicWorkActionMode(
? 'edit'
: 'remix';
}
export function resolvePlatformPublicWorkDetailOpenDecision(
entry: PlatformPublicGalleryCard,
deps: PlatformPublicWorkDetailOpenDecisionDeps = {},
): PlatformPublicWorkDetailOpenDecision {
const canExposeEntry = deps.canExposeEntry ?? canExposePublicWork;
const hiddenMessage = deps.hiddenMessage ?? EDUTAINMENT_HIDDEN_MESSAGE;
const buildWorkDetailPath =
deps.buildWorkDetailPath ??
((publicWorkCode: string) =>
buildPublicWorkStagePath('work-detail', publicWorkCode));
if (!canExposeEntry(entry)) {
return {
type: 'blocked',
selectedDetail: null,
errorMessage: hiddenMessage,
selectionStage: 'platform',
historyPath: null,
};
}
const publicWorkCode = entry.publicWorkCode?.trim()
? entry.publicWorkCode
: null;
return {
type: 'open',
selectedDetail: entry,
errorMessage: null,
selectionStage: 'work-detail',
historyPath: publicWorkCode ? buildWorkDetailPath(publicWorkCode) : null,
};
}
export function resolveActivePlatformPublicWorkAuthorEntry({
selectionStage,
selectedPublicWorkDetail,
selectedRpgDetailEntry,
}: ActivePlatformPublicWorkAuthorEntryInput): PlatformPublicGalleryCard | null {
if (selectionStage === 'work-detail') {
return selectedPublicWorkDetail;
}
if (
selectionStage === 'detail' &&
selectedRpgDetailEntry &&
selectedRpgDetailEntry.visibility !== 'draft'
) {
return selectedRpgDetailEntry;
}
return null;
}