Add public work read model and smooth puzzle transitions

This commit is contained in:
kdletters
2026-05-26 16:38:27 +08:00
parent 545f315cbc
commit aeee782fe0
47 changed files with 2679 additions and 79 deletions

View File

@@ -695,7 +695,10 @@ function isRecommendRuntimeReadyForEntry(
return Boolean(state.match3dRun);
}
if (expectedKind === 'puzzle') {
return Boolean(state.puzzleRun);
return (
state.puzzleRun?.entryProfileId === entry.profileId ||
state.puzzleRun?.currentLevel?.profileId === entry.profileId
);
}
if (expectedKind === 'square-hole') {
return Boolean(state.squareHoleRun);
@@ -3139,6 +3142,7 @@ export function PlatformEntryFlowShellImpl({
useState<PuzzleRuntimeAuthMode>('default');
const [isPuzzleLeaderboardBusy, setIsPuzzleLeaderboardBusy] = useState(false);
const submittedPuzzleLeaderboardKeysRef = useRef(new Set<string>());
const puzzleStartInFlightKeyRef = useRef<string | null>(null);
const [puzzleRun, setPuzzleRun] = useState<PuzzleRunSnapshot | null>(null);
const puzzleRunRef = useRef<PuzzleRunSnapshot | null>(null);
const errorSetterRefNoop = useMemo(
@@ -9019,10 +9023,13 @@ export function PlatformEntryFlowShellImpl({
levelId?: string | null,
options: { embedded?: boolean; authMode?: PuzzleRuntimeAuthMode } = {},
) => {
if (isPuzzleBusy) {
const normalizedLevelId = levelId?.trim() ?? '';
const startKey = `${profileId}:${normalizedLevelId}`;
if (isPuzzleBusy || puzzleStartInFlightKeyRef.current === startKey) {
return false;
}
puzzleStartInFlightKeyRef.current = startKey;
setIsPuzzleBusy(true);
setPuzzleError(null);
@@ -9031,7 +9038,7 @@ export function PlatformEntryFlowShellImpl({
detailItem ?? (await getPuzzleGalleryDetail(profileId)).item;
const startRunPayload = {
profileId: item.profileId,
levelId: levelId ?? null,
levelId: normalizedLevelId || null,
};
const canUseRuntimeGuestAuth =
options.embedded || options.authMode === 'isolated';
@@ -9086,6 +9093,9 @@ export function PlatformEntryFlowShellImpl({
}
return false;
} finally {
if (puzzleStartInFlightKeyRef.current === startKey) {
puzzleStartInFlightKeyRef.current = null;
}
setIsPuzzleBusy(false);
}
},
@@ -9857,7 +9867,13 @@ export function PlatformEntryFlowShellImpl({
return;
}
const submitKey = `${puzzleRun.runId}:${currentLevel.profileId}:${currentLevel.gridSize}:${currentLevel.elapsedMs}`;
const submitKey = [
puzzleRun.runId,
currentLevel.profileId,
currentLevel.levelId ?? currentLevel.levelIndex,
currentLevel.gridSize,
currentLevel.elapsedMs,
].join(':');
if (submittedPuzzleLeaderboardKeysRef.current.has(submitKey)) {
return;
}
@@ -9895,7 +9911,6 @@ export function PlatformEntryFlowShellImpl({
void platformBootstrap.refreshSaveArchives();
})
.catch((error) => {
submittedPuzzleLeaderboardKeysRef.current.delete(submitKey);
setPuzzleError(
resolvePuzzleErrorMessage(error, '提交拼图排行榜失败。'),
);
@@ -9942,6 +9957,10 @@ 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
@@ -9981,10 +10000,35 @@ export function PlatformEntryFlowShellImpl({
puzzleRuntimeAuthMode === 'isolated'
? await advancePuzzleNextLevel(
puzzleRun.runId,
{},
preferSimilarWork ? { preferSimilarWork: true } : {},
runtimeGuestOptions,
)
: await advancePuzzleNextLevel(puzzleRun.runId);
: 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),
);
}
setPuzzleRun(run);
} catch (error) {
setPuzzleError(resolvePuzzleErrorMessage(error, '准备下一关失败。'));
@@ -9996,8 +10040,12 @@ export function PlatformEntryFlowShellImpl({
[
isPuzzleBusy,
isPuzzleLeaderboardBusy,
activeRecommendRuntimeKind,
puzzleRun,
puzzleRuntimeReturnStage,
puzzleRuntimeAuthMode,
setActiveRecommendEntryKey,
setPuzzleGalleryEntries,
resolvePuzzleErrorMessage,
selectedPuzzleDetail,
setIsPuzzleBusy,
@@ -12394,6 +12442,10 @@ export function PlatformEntryFlowShellImpl({
recommendRuntimeStartRequestRef.current = startRequestId;
const isCurrentStartRequest = () =>
recommendRuntimeStartRequestRef.current === startRequestId;
setActiveRecommendEntryKey(entryKey);
setActiveRecommendRuntimeKind(runtimeKind);
setActiveRecommendRuntimeError(null);
setIsStartingRecommendEntry(true);
if (entryKey !== activeRecommendEntryKey) {
await saveAndExitRecommendPuzzleRuntime();
if (!isCurrentStartRequest()) {
@@ -12546,14 +12598,16 @@ export function PlatformEntryFlowShellImpl({
],
);
const selectAdjacentRecommendRuntimeEntry = useCallback(
(direction: 1 | -1) => {
(direction: 1 | -1, baseEntryKey?: string | null) => {
if (recommendRuntimeEntries.length === 0) {
return;
}
const normalizedBaseEntryKey =
baseEntryKey?.trim() || activeRecommendEntryKey;
const activeIndex = recommendRuntimeEntries.findIndex(
(entry) =>
getPlatformPublicGalleryEntryKey(entry) === activeRecommendEntryKey,
getPlatformPublicGalleryEntryKey(entry) === normalizedBaseEntryKey,
);
const baseIndex = activeIndex >= 0 ? activeIndex : 0;
const nextIndex =
@@ -12564,7 +12618,7 @@ export function PlatformEntryFlowShellImpl({
return;
}
if (
getPlatformPublicGalleryEntryKey(nextEntry) === activeRecommendEntryKey
getPlatformPublicGalleryEntryKey(nextEntry) === normalizedBaseEntryKey
) {
return;
}
@@ -14325,18 +14379,19 @@ export function PlatformEntryFlowShellImpl({
isStartingRecommendEntry={
isStartingRecommendEntry ||
isBigFishBusy ||
isPuzzleBusy ||
(isPuzzleBusy &&
!(activeRecommendRuntimeKind === 'puzzle' && puzzleRun)) ||
isMatch3DBusy ||
isSquareHoleBusy ||
isVisualNovelBusy ||
isWoodenFishBusy
}
recommendRuntimeError={activeRecommendRuntimeError}
onSelectNextRecommendEntry={() =>
selectAdjacentRecommendRuntimeEntry(1)
onSelectNextRecommendEntry={(activeEntryKey) =>
selectAdjacentRecommendRuntimeEntry(1, activeEntryKey)
}
onSelectPreviousRecommendEntry={() =>
selectAdjacentRecommendRuntimeEntry(-1)
onSelectPreviousRecommendEntry={(activeEntryKey) =>
selectAdjacentRecommendRuntimeEntry(-1, activeEntryKey)
}
onLikeRecommendEntry={(entry) => {
likePublicWork(entry);