合并 master 并修复架构分支回归
合入 master 最新的认证、玩法契约与推荐页改动。 修复拼图草稿生成、推荐页下一关和公开详情访客试玩回归。 修复抓大鹅草稿试玩鉴权与首屏推荐详情测试入口。 补齐相关测试夹具、文档与团队记忆更新。
This commit is contained in:
@@ -115,6 +115,7 @@ import { buildCustomWorldPlayableCharacters } from '../../data/characterPresets'
|
||||
import {
|
||||
buildPublicWorkStagePath,
|
||||
pushAppHistoryPath,
|
||||
resolvePathForSelectionStage,
|
||||
} from '../../routing/appPageRoutes';
|
||||
import { resolveWorkNotFoundRecoveryAction } from '../../routing/runtimeNotFoundRecovery';
|
||||
import {
|
||||
@@ -371,7 +372,6 @@ import {
|
||||
isPersistedBarkBattleDraftGenerating,
|
||||
} from '../custom-world-home/creationWorkShelf';
|
||||
import {
|
||||
buildPlatformRecommendFeedEntries,
|
||||
selectAdjacentPlatformRecommendEntry,
|
||||
} from '../rpg-entry/rpgEntryPublicGalleryViewModel';
|
||||
import {
|
||||
@@ -493,7 +493,9 @@ import {
|
||||
import {
|
||||
canExposePublicWork,
|
||||
EDUTAINMENT_HIDDEN_MESSAGE,
|
||||
filterGeneralPublicWorks,
|
||||
} from './platformEdutainmentVisibility';
|
||||
import { buildPlatformRecommendedEntries } from './platformRecommendation';
|
||||
import { PlatformEntryCreationTypeModal } from './PlatformEntryCreationTypeModal';
|
||||
import type { PlatformCreationTypeId } from './platformEntryCreationTypes';
|
||||
import {
|
||||
@@ -2872,10 +2874,10 @@ export function PlatformEntryFlowShellImpl({
|
||||
publicGalleryFeeds;
|
||||
const recommendRuntimeEntries = useMemo(
|
||||
() =>
|
||||
buildPlatformRecommendFeedEntries(
|
||||
featuredGalleryEntries,
|
||||
latestGalleryEntries,
|
||||
),
|
||||
buildPlatformRecommendedEntries({
|
||||
featuredEntries: filterGeneralPublicWorks(featuredGalleryEntries),
|
||||
latestEntries: filterGeneralPublicWorks(latestGalleryEntries),
|
||||
}),
|
||||
[featuredGalleryEntries, latestGalleryEntries],
|
||||
);
|
||||
|
||||
@@ -4440,6 +4442,18 @@ export function PlatformEntryFlowShellImpl({
|
||||
activePuzzleBackgroundCompileTask?.session ?? puzzleSession;
|
||||
const puzzleGenerationViewPayload =
|
||||
activePuzzleBackgroundCompileTask?.payload ?? puzzleFormDraftPayload;
|
||||
const puzzleGenerationViewStateRef = useRef(puzzleGenerationViewState);
|
||||
const puzzleGenerationViewPayloadRef = useRef(puzzleGenerationViewPayload);
|
||||
const setPuzzleSessionRef = useRef(puzzleFlow.setSession);
|
||||
useEffect(() => {
|
||||
puzzleGenerationViewStateRef.current = puzzleGenerationViewState;
|
||||
}, [puzzleGenerationViewState]);
|
||||
useEffect(() => {
|
||||
puzzleGenerationViewPayloadRef.current = puzzleGenerationViewPayload;
|
||||
}, [puzzleGenerationViewPayload]);
|
||||
useEffect(() => {
|
||||
setPuzzleSessionRef.current = puzzleFlow.setSession;
|
||||
}, [puzzleFlow.setSession]);
|
||||
const puzzleGenerationViewError =
|
||||
activePuzzleBackgroundCompileTask?.error ?? puzzleError;
|
||||
const isPuzzleGenerationViewBusy =
|
||||
@@ -4835,21 +4849,21 @@ export function PlatformEntryFlowShellImpl({
|
||||
|
||||
if (hasRecoverableGeneratedPuzzleDraft(latestSession)) {
|
||||
const payload =
|
||||
puzzleGenerationViewPayload ??
|
||||
puzzleGenerationViewPayloadRef.current ??
|
||||
buildPuzzleFormPayloadFromSession(latestSession);
|
||||
const generationState =
|
||||
puzzleGenerationViewState ??
|
||||
puzzleGenerationViewStateRef.current ??
|
||||
createPuzzleDraftGenerationStateFromPayload(payload, latestSession);
|
||||
await recoverCompletedPuzzleDraftGeneration({
|
||||
sessionId: latestSession.sessionId,
|
||||
payload,
|
||||
generationState,
|
||||
setSession: setPuzzleSession,
|
||||
setSession: setPuzzleSessionRef.current,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setPuzzleSession(latestSession);
|
||||
setPuzzleSessionRef.current(latestSession);
|
||||
setPuzzleBackgroundCompileTasks((current) => {
|
||||
const task = current[activePuzzleGenerationSessionId];
|
||||
if (!task) {
|
||||
@@ -4892,11 +4906,8 @@ export function PlatformEntryFlowShellImpl({
|
||||
};
|
||||
}, [
|
||||
activePuzzleGenerationSessionId,
|
||||
puzzleGenerationViewPayload,
|
||||
puzzleGenerationViewState,
|
||||
recoverCompletedPuzzleDraftGeneration,
|
||||
shouldPollPuzzleGenerationSession,
|
||||
setPuzzleSession,
|
||||
]);
|
||||
|
||||
const match3DGeneratingSessionId =
|
||||
@@ -5258,27 +5269,48 @@ export function PlatformEntryFlowShellImpl({
|
||||
);
|
||||
setPuzzleOperation(response.operation);
|
||||
const openResult = isViewingPuzzleGeneration(nextSession.sessionId);
|
||||
const readyGenerationState =
|
||||
resolveFinishedMiniGameDraftGenerationState(
|
||||
generationState,
|
||||
'ready',
|
||||
{
|
||||
completedAssetCount: 1,
|
||||
totalAssetCount: 1,
|
||||
},
|
||||
);
|
||||
const isCompileReady = isPuzzleCompileActionReady(response.session);
|
||||
const nextGenerationState = isCompileReady
|
||||
? resolveFinishedMiniGameDraftGenerationState(
|
||||
generationState,
|
||||
'ready',
|
||||
{
|
||||
completedAssetCount: 1,
|
||||
totalAssetCount: 1,
|
||||
},
|
||||
)
|
||||
: mergePuzzleSessionProgressIntoGenerationState(
|
||||
generationState,
|
||||
response.session,
|
||||
);
|
||||
setPuzzleBackgroundCompileTasks((current) => ({
|
||||
...current,
|
||||
[nextSession.sessionId]: {
|
||||
session: response.session,
|
||||
payload,
|
||||
generationState: readyGenerationState,
|
||||
generationState: nextGenerationState,
|
||||
error: null,
|
||||
},
|
||||
}));
|
||||
if (isViewingPuzzleGeneration(nextSession.sessionId)) {
|
||||
puzzleFlow.setSession(response.session);
|
||||
setPuzzleGenerationState(readyGenerationState);
|
||||
setPuzzleGenerationState(nextGenerationState);
|
||||
}
|
||||
|
||||
if (!isCompileReady) {
|
||||
markDraftGenerating('puzzle', [
|
||||
response.session.sessionId,
|
||||
buildPuzzleResultWorkId(response.session.sessionId),
|
||||
response.session.publishedProfileId,
|
||||
buildPuzzleResultProfileId(response.session.sessionId),
|
||||
]);
|
||||
markPendingDraftGenerating(
|
||||
'puzzle',
|
||||
response.session.sessionId,
|
||||
buildPendingPuzzleDraftMetadata(payload),
|
||||
);
|
||||
void refreshPuzzleShelf();
|
||||
return;
|
||||
}
|
||||
|
||||
const profileId =
|
||||
@@ -6870,10 +6902,10 @@ export function PlatformEntryFlowShellImpl({
|
||||
profileId: targetProfileId,
|
||||
mode: 'play' as const,
|
||||
};
|
||||
const runtimeGuestOptions = await buildRecommendRuntimeAuthOptions(
|
||||
authUi,
|
||||
options.embedded,
|
||||
);
|
||||
const runtimeGuestOptions =
|
||||
options.embedded || workDetail.summary.publishStatus === 'draft'
|
||||
? await buildRecommendRuntimeAuthOptions(authUi, true)
|
||||
: {};
|
||||
const { run } = options.embedded
|
||||
? await startVisualNovelRun(
|
||||
targetProfileId,
|
||||
@@ -8818,7 +8850,11 @@ export function PlatformEntryFlowShellImpl({
|
||||
profile: Match3DWorkProfile | Match3DWorkSummary,
|
||||
returnStage: 'match3d-result' | 'work-detail' = 'match3d-result',
|
||||
mirrorErrorToPublicDetail = false,
|
||||
options: { embedded?: boolean; itemTypeCountOverride?: number } = {},
|
||||
options: {
|
||||
embedded?: boolean;
|
||||
authMode?: PuzzleRuntimeAuthMode;
|
||||
itemTypeCountOverride?: number;
|
||||
} = {},
|
||||
) => {
|
||||
if (isMatch3DBusy) {
|
||||
return false;
|
||||
@@ -8857,10 +8893,10 @@ export function PlatformEntryFlowShellImpl({
|
||||
runtimeProfile.generatedBackgroundAsset,
|
||||
{ expireSeconds: 300 },
|
||||
);
|
||||
const runtimeGuestOptions = await buildRecommendRuntimeAuthOptions(
|
||||
authUi,
|
||||
options.embedded,
|
||||
);
|
||||
const runtimeGuestOptions =
|
||||
options.authMode === 'isolated'
|
||||
? RECOMMEND_RUNTIME_BACKGROUND_AUTH_OPTIONS
|
||||
: await buildRecommendRuntimeAuthOptions(authUi, options.embedded);
|
||||
const runtimeOptions = {
|
||||
...runtimeGuestOptions,
|
||||
...(typeof options.itemTypeCountOverride === 'number'
|
||||
@@ -9657,10 +9693,6 @@ export function PlatformEntryFlowShellImpl({
|
||||
? await buildRecommendRuntimeGuestOptions()
|
||||
: {};
|
||||
const targetProfileId = _target?.profileId?.trim() ?? '';
|
||||
const preferSimilarWork =
|
||||
activeRecommendRuntimeKind === 'puzzle' &&
|
||||
puzzleRuntimeReturnStage === 'platform' &&
|
||||
puzzleRun.nextLevelMode === 'sameWork';
|
||||
if (puzzleRun.nextLevelMode === 'similarWorks' && targetProfileId) {
|
||||
const itemPromise =
|
||||
selectedPuzzleDetail?.profileId === targetProfileId
|
||||
@@ -9700,34 +9732,10 @@ export function PlatformEntryFlowShellImpl({
|
||||
puzzleRuntimeAuthMode === 'isolated'
|
||||
? await advancePuzzleNextLevel(
|
||||
puzzleRun.runId,
|
||||
preferSimilarWork ? { preferSimilarWork: true } : {},
|
||||
{},
|
||||
runtimeGuestOptions,
|
||||
)
|
||||
: await advancePuzzleNextLevel(
|
||||
puzzleRun.runId,
|
||||
preferSimilarWork ? { preferSimilarWork: true } : {},
|
||||
);
|
||||
const nextProfileId = run.currentLevel?.profileId?.trim() ?? '';
|
||||
if (
|
||||
nextProfileId &&
|
||||
selectedPuzzleDetail?.profileId !== nextProfileId
|
||||
) {
|
||||
const item = await getPuzzleGalleryDetail(nextProfileId).then(
|
||||
(response) => response.item,
|
||||
);
|
||||
const nextRecommendEntry = mapPuzzleWorkToPlatformGalleryCard(item);
|
||||
setPuzzleGalleryEntries((current) => {
|
||||
const nextEntries = current.filter(
|
||||
(entry) => entry.profileId !== item.profileId,
|
||||
);
|
||||
nextEntries.push(item);
|
||||
return nextEntries;
|
||||
});
|
||||
setSelectedPuzzleDetail(item);
|
||||
setActiveRecommendEntryKey(
|
||||
getPlatformPublicGalleryEntryKey(nextRecommendEntry),
|
||||
);
|
||||
}
|
||||
: await advancePuzzleNextLevel(puzzleRun.runId, {});
|
||||
setPuzzleRun(run);
|
||||
} catch (error) {
|
||||
setPuzzleError(resolvePuzzleErrorMessage(error, '准备下一关失败。'));
|
||||
@@ -9739,12 +9747,8 @@ export function PlatformEntryFlowShellImpl({
|
||||
[
|
||||
isPuzzleBusy,
|
||||
isPuzzleLeaderboardBusy,
|
||||
activeRecommendRuntimeKind,
|
||||
puzzleRun,
|
||||
puzzleRuntimeReturnStage,
|
||||
puzzleRuntimeAuthMode,
|
||||
setActiveRecommendEntryKey,
|
||||
setPuzzleGalleryEntries,
|
||||
resolvePuzzleErrorMessage,
|
||||
selectedPuzzleDetail,
|
||||
setIsPuzzleBusy,
|
||||
@@ -10956,12 +10960,18 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
|
||||
try {
|
||||
const detail = await jumpHopClient.getWorkDetail(item.profileId);
|
||||
const detail = await jumpHopClient.getWorkDetail(item.profileId, {
|
||||
audience: 'creation',
|
||||
});
|
||||
setJumpHopSession(null);
|
||||
setJumpHopRun(null);
|
||||
setJumpHopWork(detail.item);
|
||||
setJumpHopRuntimeReturnStage('jump-hop-result');
|
||||
enterCreateTab();
|
||||
pushAppHistoryPath(resolvePathForSelectionStage('jump-hop-result'));
|
||||
writeCreationUrlState(
|
||||
buildJumpHopCreationUrlState({ work: detail.item }),
|
||||
);
|
||||
setSelectionStage('jump-hop-result');
|
||||
} catch (error) {
|
||||
setJumpHopError(
|
||||
@@ -11572,6 +11582,8 @@ export function PlatformEntryFlowShellImpl({
|
||||
const started = await startMatch3DRunFromProfile(
|
||||
normalizedProfile,
|
||||
'match3d-result',
|
||||
false,
|
||||
{ authMode: 'isolated' },
|
||||
);
|
||||
if (!started) {
|
||||
setMatch3DProfile(normalizedProfile);
|
||||
@@ -12147,7 +12159,11 @@ export function PlatformEntryFlowShellImpl({
|
||||
let work: JumpHopWorkProfileResponse | null = null;
|
||||
try {
|
||||
if (profileId) {
|
||||
work = (await jumpHopClient.getWorkDetail(profileId)).item;
|
||||
work = (
|
||||
await jumpHopClient.getWorkDetail(profileId, {
|
||||
audience: 'creation',
|
||||
})
|
||||
).item;
|
||||
}
|
||||
} catch {
|
||||
work = null;
|
||||
@@ -12525,6 +12541,13 @@ export function PlatformEntryFlowShellImpl({
|
||||
setActiveRecommendEntryKey(entryKey);
|
||||
setActiveRecommendRuntimeKind(runtimeKind);
|
||||
setActiveRecommendRuntimeError(null);
|
||||
if (
|
||||
runtimeKind === 'puzzle' &&
|
||||
(isPuzzleBusy || puzzleStartInFlightKeyRef.current !== null)
|
||||
) {
|
||||
setIsStartingRecommendEntry(false);
|
||||
return;
|
||||
}
|
||||
setIsStartingRecommendEntry(true);
|
||||
|
||||
try {
|
||||
@@ -12659,6 +12682,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
[
|
||||
activeRecommendEntryKey,
|
||||
barkBattleGalleryEntries,
|
||||
isPuzzleBusy,
|
||||
saveAndExitRecommendPuzzleRuntime,
|
||||
selectedPuzzleDetail,
|
||||
setBarkBattleError,
|
||||
@@ -12700,6 +12724,23 @@ export function PlatformEntryFlowShellImpl({
|
||||
selectRecommendRuntimeEntry,
|
||||
],
|
||||
);
|
||||
const resolveRecommendRuntimeEntryKeyByProfileId = useCallback(
|
||||
(profileId: string | null | undefined) => {
|
||||
const normalizedProfileId = profileId?.trim();
|
||||
if (!normalizedProfileId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const matchedEntry =
|
||||
recommendRuntimeEntries.find(
|
||||
(entry) => entry.profileId === normalizedProfileId,
|
||||
) ?? null;
|
||||
return matchedEntry
|
||||
? getPlatformPublicGalleryEntryKey(matchedEntry)
|
||||
: null;
|
||||
},
|
||||
[recommendRuntimeEntries],
|
||||
);
|
||||
|
||||
const recommendRuntimeContent = useMemo(() => {
|
||||
if (
|
||||
@@ -12861,7 +12902,13 @@ export function PlatformEntryFlowShellImpl({
|
||||
void dragPuzzlePiece(payload);
|
||||
}}
|
||||
onAdvanceNextLevel={(target) => {
|
||||
void advancePuzzleLevel(target);
|
||||
const targetEntryKey = resolveRecommendRuntimeEntryKeyByProfileId(
|
||||
target?.profileId,
|
||||
);
|
||||
void selectAdjacentRecommendRuntimeEntry(
|
||||
1,
|
||||
targetEntryKey ?? activeRecommendEntryKey,
|
||||
);
|
||||
}}
|
||||
onRestartLevel={() => {
|
||||
void restartPuzzleCurrentLevel();
|
||||
@@ -13115,9 +13162,10 @@ export function PlatformEntryFlowShellImpl({
|
||||
squareHoleRun,
|
||||
submitBigFishInput,
|
||||
submitVisualNovelRuntimeAction,
|
||||
advancePuzzleLevel,
|
||||
dragPuzzlePiece,
|
||||
resolveRecommendRuntimeEntryKeyByProfileId,
|
||||
restartPuzzleCurrentLevel,
|
||||
selectAdjacentRecommendRuntimeEntry,
|
||||
setSquareHoleError,
|
||||
swapPuzzlePiecesInRun,
|
||||
syncPuzzleRuntimeTimeout,
|
||||
@@ -13166,7 +13214,8 @@ export function PlatformEntryFlowShellImpl({
|
||||
isLoadingPlatform: platformBootstrap.isLoadingPlatform,
|
||||
entries: recommendRuntimeEntries,
|
||||
activeEntryKey: activeRecommendEntryKey,
|
||||
isStarting: isStartingRecommendEntry,
|
||||
isStarting: isStartingRecommendEntry || isPuzzleBusy,
|
||||
hasStartError: Boolean(activeRecommendRuntimeError),
|
||||
readyState: {
|
||||
activeKind: activeRecommendRuntimeKind,
|
||||
hasBabyObjectMatchDraft: Boolean(babyObjectMatchDraft),
|
||||
@@ -13198,10 +13247,12 @@ export function PlatformEntryFlowShellImpl({
|
||||
}, [
|
||||
activeRecommendEntryKey,
|
||||
activeRecommendRuntimeKind,
|
||||
activeRecommendRuntimeError,
|
||||
babyObjectMatchDraft,
|
||||
bigFishRun,
|
||||
jumpHopRun,
|
||||
isStartingRecommendEntry,
|
||||
isPuzzleBusy,
|
||||
match3dRun,
|
||||
platformBootstrap.isLoadingPlatform,
|
||||
platformBootstrap.platformTab,
|
||||
@@ -13952,8 +14003,11 @@ export function PlatformEntryFlowShellImpl({
|
||||
canDeleteBigFish: isBigFishCreationVisible,
|
||||
canDeleteMatch3D: true,
|
||||
canDeleteSquareHole: isSquareHoleCreationVisible,
|
||||
canDeleteJumpHop: isJumpHopCreationVisible,
|
||||
canDeleteWoodenFish: true,
|
||||
canDeletePuzzle: true,
|
||||
canDeleteBabyObjectMatch: isBabyObjectMatchVisible,
|
||||
canDeleteBarkBattle: true,
|
||||
canDeleteVisualNovel: true,
|
||||
onOpenRpgDraft: (item) => {
|
||||
runProtectedAction(() => {
|
||||
@@ -13993,12 +14047,16 @@ export function PlatformEntryFlowShellImpl({
|
||||
});
|
||||
}
|
||||
: undefined,
|
||||
onDeleteJumpHop: isJumpHopCreationVisible
|
||||
? handleDeleteJumpHopWork
|
||||
: undefined,
|
||||
onOpenWoodenFishDetail: (item) => {
|
||||
runProtectedAction(() => {
|
||||
markCreationFlowReturnToDraftShelf();
|
||||
void openWoodenFishDraft(item);
|
||||
});
|
||||
},
|
||||
onDeleteWoodenFish: handleDeleteWoodenFishWork,
|
||||
onOpenMatch3DDetail: (item) => {
|
||||
runProtectedAction(() => {
|
||||
markCreationFlowReturnToDraftShelf();
|
||||
@@ -14038,6 +14096,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
openBarkBattleDraft(item);
|
||||
});
|
||||
},
|
||||
onDeleteBarkBattle: handleDeleteBarkBattleWork,
|
||||
onOpenVisualNovelDetail: (item) => {
|
||||
runProtectedAction(() => {
|
||||
markCreationFlowReturnToDraftShelf();
|
||||
@@ -14057,11 +14116,14 @@ export function PlatformEntryFlowShellImpl({
|
||||
handleClaimPuzzlePointIncentive,
|
||||
handleDeleteBabyObjectMatchWork,
|
||||
handleDeleteBigFishWork,
|
||||
handleDeleteBarkBattleWork,
|
||||
handleDeleteJumpHopWork,
|
||||
handleDeleteMatch3DWork,
|
||||
handleDeletePublishedWork,
|
||||
handleDeletePuzzleWork,
|
||||
handleDeleteSquareHoleWork,
|
||||
handleDeleteVisualNovelWork,
|
||||
handleDeleteWoodenFishWork,
|
||||
isBabyObjectMatchVisible,
|
||||
isBigFishCreationVisible,
|
||||
isJumpHopCreationVisible,
|
||||
@@ -14884,7 +14946,13 @@ export function PlatformEntryFlowShellImpl({
|
||||
normalizedProfile,
|
||||
'match3d-result',
|
||||
false,
|
||||
options,
|
||||
{
|
||||
...options,
|
||||
authMode:
|
||||
normalizedProfile.publicationStatus === 'draft'
|
||||
? 'isolated'
|
||||
: undefined,
|
||||
},
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user