修复生成完成提示与项目封面快照

生成完成提示展示后后台确认,避免刷新重复弹出

项目封面改为保存并读取静态快照资源

补充封面快照上传、选择和展示测试

同步外部生成与图片画布契约文档
This commit is contained in:
2026-06-24 19:30:16 +08:00
parent 32fc4089cb
commit b6c5fdeb30
16 changed files with 877 additions and 342 deletions
@@ -1,5 +1,4 @@
import { Home, Loader2, Monitor } from 'lucide-react';
import { Home, Monitor } from 'lucide-react';
import { AnimatePresence, motion } from 'motion/react';
import {
type Dispatch,
@@ -168,8 +167,8 @@ import {
fetchCreationEntryConfig,
} from '../../services/creationEntryConfigService';
import {
type CreationUrlState,
clearCreationUrlState,
type CreationUrlState,
readCreationUrlState,
writeCreationUrlState,
} from '../../services/creationUrlState';
@@ -197,6 +196,7 @@ import {
acknowledgeExternalGenerationTasks,
listExternalGenerationTasks,
} from '../../services/external-generation';
import { navigateHostNativePage } from '../../services/host-bridge/hostBridge';
import {
jumpHopClient,
type JumpHopGalleryCardResponse,
@@ -322,7 +322,6 @@ import {
recordRpgEntryWorldGalleryPlay,
remixRpgEntryWorldGallery,
} from '../../services/rpg-entry/rpgEntryLibraryClient';
import { navigateHostNativePage } from '../../services/host-bridge/hostBridge';
import { getRpgProfilePlayStats } from '../../services/rpg-entry/rpgProfileClient';
import { requestRpgRuntimeJson } from '../../services/rpg-runtime/rpgRuntimeRequest';
import { type RuntimeGuestRequestOptions } from '../../services/runtimeGuestAuth';
@@ -372,9 +371,9 @@ import {
} from '../../services/wooden-fish/woodenFishClient';
import type { CustomWorldProfile } from '../../types';
import { useAuthUi } from '../auth/AuthUiContext';
import { FLOATING_FEEDBACK_FORM_URL } from '../common/floatingFeedbackEntryModel';
import { PlatformAcknowledgeStatusDialog } from '../common/PlatformAcknowledgeStatusDialog';
import { PlatformActionButton } from '../common/PlatformActionButton';
import { FLOATING_FEEDBACK_FORM_URL } from '../common/floatingFeedbackEntryModel';
import { PlatformDangerConfirmDialog } from '../common/PlatformDangerConfirmDialog';
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
import { PlatformSubpanel } from '../common/PlatformSubpanel';
@@ -490,6 +489,7 @@ import {
buildPendingSquareHoleWorks,
buildPendingVisualNovelWorks,
buildPendingWoodenFishWorks,
buildPuzzleDraftOpenNoticeKeys,
collectDraftNoticeKeys,
collectVisibleDraftNoticeKeys,
countUnreadDraftGenerationUpdates,
@@ -504,7 +504,6 @@ import {
type PendingDraftShelfKind,
type PendingDraftShelfMap,
type PendingDraftShelfMetadata,
buildPuzzleDraftOpenNoticeKeys,
resolveBigFishDraftOpenIntent,
resolveJumpHopDraftOpenIntent,
resolveMatch3DDraftOpenIntent,
@@ -534,11 +533,6 @@ import {
PlatformEntryHomeView,
type PlatformHomeTab,
} from './PlatformEntryHomeView';
import {
PlatformMobileHomeWelcomeDialog,
shouldOpenMobileHomeWelcomeDialog,
} from './PlatformMobileHomeWelcomeDialog';
import { PlatformProfileReferralModal } from './PlatformProfileReferralModal';
import { usePlatformDesktopLayout } from './platformEntryResponsive';
import {
buildCreationHubFallbackItems,
@@ -552,6 +546,10 @@ import { PlatformEntryWorldDetailView } from './PlatformEntryWorldDetailView';
import { PlatformErrorDialog } from './PlatformErrorDialog';
import { buildExternalGenerationQueueStatus } from './platformExternalGenerationQueueStatusModel';
import { resolvePlatformGenerationProgressTickDecision } from './platformGenerationProgressTickModel';
import {
sendPlatformHostDraftNotificationToHost,
syncPlatformHostDraftBadgeCountToHost,
} from './platformHostBridgeSync';
import {
buildPlatformHostDraftFailureNotification,
buildPlatformHostDraftNotificationResetKeys,
@@ -559,10 +557,6 @@ import {
type PlatformHostDraftNotification,
sendPlatformHostDraftNotificationOnce,
} from './platformHostNotificationModel';
import {
sendPlatformHostDraftNotificationToHost,
syncPlatformHostDraftBadgeCountToHost,
} from './platformHostBridgeSync';
import {
buildMatch3DProfileFromSession,
hasMatch3DRuntimeAsset,
@@ -616,7 +610,12 @@ import {
buildWoodenFishPendingSession,
buildWoodenFishSessionFromWorkDetail,
} from './platformMiniGameSessionMappingModel';
import {
PlatformMobileHomeWelcomeDialog,
shouldOpenMobileHomeWelcomeDialog,
} from './PlatformMobileHomeWelcomeDialog';
import { resolvePlatformPlayedWorkOpenIntent } from './platformPlayedWorkOpenModel';
import { PlatformProfileReferralModal } from './PlatformProfileReferralModal';
import {
adjustProfileDashboardWalletBalance,
reconcileProfileWalletLocalDeltaWithServerDashboard,
@@ -709,20 +708,6 @@ type PuzzleBackgroundCompileTask = {
error: string | null;
};
function isExternalGenerationQueueStage(selectionStage: SelectionStage) {
return (
selectionStage === 'puzzle-generating' ||
selectionStage === 'big-fish-generating' ||
selectionStage === 'square-hole-generating' ||
selectionStage === 'match3d-generating' ||
selectionStage === 'baby-object-match-generating' ||
selectionStage === 'jump-hop-generating' ||
selectionStage === 'puzzle-clear-generating' ||
selectionStage === 'wooden-fish-generating' ||
selectionStage === 'visual-novel-generating'
);
}
function isUnacknowledgedExternalGenerationTerminalTask(
task: ExternalGenerationTaskRecord,
) {
@@ -2158,6 +2143,9 @@ export function PlatformEntryFlowShellImpl({
const sentPlatformHostDraftNotificationKeysRef = useRef<Set<string>>(
new Set(),
);
const acknowledgingExternalGenerationDialogKeysRef = useRef<Set<string>>(
new Set(),
);
const [
pendingPlatformTaskCompletionDialog,
setPendingPlatformTaskCompletionDialog,
@@ -4975,11 +4963,6 @@ export function PlatformEntryFlowShellImpl({
isWoodenFishBusy,
woodenFishGenerationState,
);
const shouldShowExternalGenerationQueueStatus =
isExternalGenerationQueueStage(selectionStage) ||
(selectionStage === 'platform' &&
platformBootstrap.platformTab === 'profile' &&
platformBootstrap.canReadProtectedData);
const isHostNetworkOnline = useHostNetworkOnline();
const shouldPollExternalGenerationTasks =
platformBootstrap.canReadProtectedData && Boolean(authUi?.user?.id);
@@ -4989,6 +4972,7 @@ export function PlatformEntryFlowShellImpl({
: null;
useEffect(() => {
acknowledgingExternalGenerationDialogKeysRef.current.clear();
setExternalGenerationTasks([]);
setExternalGenerationTaskOverview(null);
}, [externalGenerationQueueOwnerKey]);
@@ -5340,6 +5324,68 @@ export function PlatformEntryFlowShellImpl({
dismissedPlatformErrorDialogKey,
buildPlatformErrorDialogDismissKey,
);
const acknowledgeExternalGenerationCompletionDialog = useCallback(
(completion: PlatformTaskCompletionDialogState | null) => {
const taskJobIds = completion?.taskJobIds ?? [];
if (taskJobIds.length === 0) {
return;
}
const dismissKey =
buildPlatformTaskCompletionDialogDismissKey(completion);
const acknowledgingKeys =
acknowledgingExternalGenerationDialogKeysRef.current;
if (dismissKey && acknowledgingKeys.has(dismissKey)) {
return;
}
if (dismissKey) {
acknowledgingKeys.add(dismissKey);
}
const acknowledgedJobIds = new Set(taskJobIds);
setExternalGenerationTasks((current) =>
current.filter((task) => !acknowledgedJobIds.has(task.jobId)),
);
setExternalGenerationTaskOverview((current) =>
current
? {
...current,
unacknowledgedTerminalCount: Math.max(
0,
current.unacknowledgedTerminalCount - acknowledgedJobIds.size,
),
}
: current,
);
void acknowledgeExternalGenerationTasks({ jobIds: taskJobIds })
.then((response) => {
setExternalGenerationTasks((current) =>
current.filter(
(task) =>
!response.acknowledgedTasks.some(
(acknowledgedTask) => acknowledgedTask.jobId === task.jobId,
),
),
);
})
.catch(() => {
if (dismissKey) {
acknowledgingKeys.delete(dismissKey);
}
});
},
[],
);
useEffect(() => {
acknowledgeExternalGenerationCompletionDialog(
activePlatformTaskCompletionDialog,
);
}, [
acknowledgeExternalGenerationCompletionDialog,
activePlatformTaskCompletionDialog,
]);
const closePlatformErrorDialog = useCallback(() => {
if (!currentPlatformErrorDialog) {
return;
@@ -5458,38 +5504,14 @@ export function PlatformEntryFlowShellImpl({
if (dismissKey) {
setDismissedPlatformTaskCompletionDialogKey(dismissKey);
}
const taskJobIds = currentPlatformTaskCompletionDialog.taskJobIds ?? [];
if (taskJobIds.length > 0) {
const acknowledgedJobIds = new Set(taskJobIds);
setExternalGenerationTasks((current) =>
current.filter((task) => !acknowledgedJobIds.has(task.jobId)),
);
setExternalGenerationTaskOverview((current) =>
current
? {
...current,
unacknowledgedTerminalCount: Math.max(
0,
current.unacknowledgedTerminalCount - acknowledgedJobIds.size,
),
}
: current,
);
void acknowledgeExternalGenerationTasks({ jobIds: taskJobIds })
.then((response) => {
setExternalGenerationTasks((current) =>
current.filter(
(task) =>
!response.acknowledgedTasks.some(
(acknowledgedTask) => acknowledgedTask.jobId === task.jobId,
),
),
);
})
.catch(() => undefined);
}
acknowledgeExternalGenerationCompletionDialog(
currentPlatformTaskCompletionDialog,
);
setPendingPlatformTaskCompletionDialog(null);
}, [currentPlatformTaskCompletionDialog]);
}, [
acknowledgeExternalGenerationCompletionDialog,
currentPlatformTaskCompletionDialog,
]);
const shouldPollPuzzleGenerationSession =
selectionStage === 'puzzle-generating' &&
activePuzzleGenerationSessionId != null &&