1
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
||||
import {
|
||||
createRpgCreationSession,
|
||||
executeRpgCreationAction,
|
||||
getRpgCreationResultView,
|
||||
getRpgCreationSession,
|
||||
streamRpgCreationMessage,
|
||||
} from '../../services/rpg-creation';
|
||||
@@ -29,7 +30,6 @@ import type { CustomWorldProfile } from '../../types';
|
||||
import {
|
||||
buildOptimisticAgentMessage,
|
||||
createFailedAgentOperation,
|
||||
normalizeAgentBackedProfile,
|
||||
resolveRpgCreationErrorMessage,
|
||||
} from './rpgEntryShared';
|
||||
import type {
|
||||
@@ -40,7 +40,9 @@ import type {
|
||||
|
||||
type UseRpgCreationSessionControllerParams = {
|
||||
userId: string | null | undefined;
|
||||
openLoginModal?: ((postLoginAction?: (() => void) | null) => void) | undefined;
|
||||
openLoginModal?:
|
||||
| ((postLoginAction?: (() => void) | null) => void)
|
||||
| undefined;
|
||||
selectionStage: SelectionStage;
|
||||
setSelectionStage: (stage: SelectionStage) => void;
|
||||
enterCreateTab?: (() => void) | undefined;
|
||||
@@ -70,12 +72,23 @@ export function useRpgCreationSessionController(
|
||||
const shouldRestoreInitialAgentUiStateRef = useRef(
|
||||
shouldRestoreCustomWorldAgentUiState(),
|
||||
);
|
||||
const initialAgentSessionId = initialAgentUiStateRef.current.activeSessionId;
|
||||
const isInitialAgentGenerationRestore =
|
||||
Boolean(initialAgentUiStateRef.current.activeOperationId) &&
|
||||
initialAgentUiStateRef.current.customWorldGenerationSource ===
|
||||
'agent-draft-foundation';
|
||||
const canResolveInitialAgentSessionOwner =
|
||||
!initialAgentSessionId ||
|
||||
!userId ||
|
||||
Boolean(initialAgentUiStateRef.current.ownerUserId) ||
|
||||
isInitialAgentGenerationRestore;
|
||||
const isInitialAgentUiStateOwnedByCurrentUser =
|
||||
!initialAgentUiStateRef.current.ownerUserId ||
|
||||
initialAgentUiStateRef.current.ownerUserId === userId;
|
||||
canResolveInitialAgentSessionOwner &&
|
||||
(!initialAgentUiStateRef.current.ownerUserId ||
|
||||
initialAgentUiStateRef.current.ownerUserId === userId);
|
||||
const isHydratingInitialAgentWorkspaceRef = useRef(
|
||||
Boolean(
|
||||
initialAgentUiStateRef.current.activeSessionId &&
|
||||
initialAgentSessionId &&
|
||||
shouldRestoreInitialAgentUiStateRef.current &&
|
||||
isInitialAgentUiStateOwnedByCurrentUser,
|
||||
),
|
||||
@@ -115,9 +128,12 @@ export function useRpgCreationSessionController(
|
||||
const [pendingAgentUserMessage, setPendingAgentUserMessage] =
|
||||
useState<PendingAgentUserMessage | null>(null);
|
||||
const [isLoadingAgentSession, setIsLoadingAgentSession] = useState(false);
|
||||
const [creationTypeError, setCreationTypeError] = useState<string | null>(null);
|
||||
const [agentWorkspaceRestoreError, setAgentWorkspaceRestoreError] =
|
||||
useState<string | null>(null);
|
||||
const [creationTypeError, setCreationTypeError] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const [agentWorkspaceRestoreError, setAgentWorkspaceRestoreError] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [generatedCustomWorldProfile, setGeneratedCustomWorldProfile] =
|
||||
useState<CustomWorldProfile | null>(null);
|
||||
const [customWorldError, setCustomWorldError] = useState<string | null>(null);
|
||||
@@ -127,7 +143,10 @@ export function useRpgCreationSessionController(
|
||||
useState<CustomWorldResultViewSource>(null);
|
||||
const [agentDraftGenerationStartedAt, setAgentDraftGenerationStartedAt] =
|
||||
useState<number | null>(null);
|
||||
const pendingAgentUserMessageRef = useRef<PendingAgentUserMessage | null>(null);
|
||||
const pendingAgentUserMessageRef = useRef<PendingAgentUserMessage | null>(
|
||||
null,
|
||||
);
|
||||
const latestAgentResultViewOpenRequestIdRef = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
currentAgentSessionIdRef.current = agentSession?.sessionId ?? null;
|
||||
@@ -191,35 +210,54 @@ export function useRpgCreationSessionController(
|
||||
[userId],
|
||||
);
|
||||
|
||||
const syncAgentSessionSnapshot = useCallback(async (sessionId: string) => {
|
||||
const requestId = latestAgentSessionSyncRequestIdRef.current + 1;
|
||||
latestAgentSessionSyncRequestIdRef.current = requestId;
|
||||
const nextSession = await getRpgCreationSession(sessionId);
|
||||
const mergedSession = mergePendingAgentUserMessageIntoSession(nextSession);
|
||||
const syncAgentSessionSnapshot = useCallback(
|
||||
async (sessionId: string) => {
|
||||
const requestId = latestAgentSessionSyncRequestIdRef.current + 1;
|
||||
latestAgentSessionSyncRequestIdRef.current = requestId;
|
||||
const nextSession = await getRpgCreationSession(sessionId);
|
||||
const mergedSession =
|
||||
mergePendingAgentUserMessageIntoSession(nextSession);
|
||||
|
||||
if (latestAgentSessionSyncRequestIdRef.current === requestId) {
|
||||
setAgentSession(mergedSession);
|
||||
const currentPendingAgentUserMessage = pendingAgentUserMessageRef.current;
|
||||
const hasServerEchoedPendingMessage =
|
||||
currentPendingAgentUserMessage?.sessionId === nextSession.sessionId &&
|
||||
nextSession.messages.some(
|
||||
(message) => message.id === currentPendingAgentUserMessage.message.id,
|
||||
);
|
||||
if (hasServerEchoedPendingMessage) {
|
||||
setPendingAgentUserMessage(null);
|
||||
if (latestAgentSessionSyncRequestIdRef.current === requestId) {
|
||||
setAgentSession(mergedSession);
|
||||
const currentPendingAgentUserMessage =
|
||||
pendingAgentUserMessageRef.current;
|
||||
const hasServerEchoedPendingMessage =
|
||||
currentPendingAgentUserMessage?.sessionId === nextSession.sessionId &&
|
||||
nextSession.messages.some(
|
||||
(message) =>
|
||||
message.id === currentPendingAgentUserMessage.message.id,
|
||||
);
|
||||
if (hasServerEchoedPendingMessage) {
|
||||
setPendingAgentUserMessage(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mergedSession;
|
||||
}, [mergePendingAgentUserMessageIntoSession]);
|
||||
return mergedSession;
|
||||
},
|
||||
[mergePendingAgentUserMessageIntoSession],
|
||||
);
|
||||
|
||||
const syncAgentCreationResultView = useCallback(
|
||||
async (sessionId: string) => {
|
||||
const resultView = await getRpgCreationResultView(sessionId);
|
||||
const mergedSession = mergePendingAgentUserMessageIntoSession(
|
||||
resultView.session,
|
||||
);
|
||||
setAgentSession(mergedSession);
|
||||
return {
|
||||
...resultView,
|
||||
session: mergedSession ?? resultView.session,
|
||||
};
|
||||
},
|
||||
[mergePendingAgentUserMessageIntoSession],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const initialAgentSessionId = initialAgentUiStateRef.current.activeSessionId;
|
||||
const initialAgentSessionId =
|
||||
initialAgentUiStateRef.current.activeSessionId;
|
||||
|
||||
if (
|
||||
!initialAgentSessionId ||
|
||||
hasAppliedInitialAgentWorkspaceRef.current
|
||||
) {
|
||||
if (!initialAgentSessionId || hasAppliedInitialAgentWorkspaceRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -260,6 +298,20 @@ export function useRpgCreationSessionController(
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!initialAgentUiStateRef.current.ownerUserId &&
|
||||
!(
|
||||
initialAgentUiStateRef.current.activeOperationId &&
|
||||
initialAgentUiStateRef.current.customWorldGenerationSource ===
|
||||
'agent-draft-foundation'
|
||||
)
|
||||
) {
|
||||
hasAppliedInitialAgentWorkspaceRef.current = true;
|
||||
isHydratingInitialAgentWorkspaceRef.current = false;
|
||||
persistAgentUiState(null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
initialAgentUiStateRef.current.ownerUserId &&
|
||||
initialAgentUiStateRef.current.ownerUserId !== userId
|
||||
@@ -283,7 +335,13 @@ export function useRpgCreationSessionController(
|
||||
}
|
||||
|
||||
setSelectionStage('agent-workspace');
|
||||
}, [enterCreateTab, openLoginModal, persistAgentUiState, setSelectionStage, userId]);
|
||||
}, [
|
||||
enterCreateTab,
|
||||
openLoginModal,
|
||||
persistAgentUiState,
|
||||
setSelectionStage,
|
||||
userId,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
@@ -365,7 +423,10 @@ export function useRpgCreationSessionController(
|
||||
setAgentWorkspaceRestoreError(null);
|
||||
} else {
|
||||
setAgentWorkspaceRestoreError(
|
||||
resolveRpgCreationErrorMessage(error, '读取 Agent 共创工作区失败。'),
|
||||
resolveRpgCreationErrorMessage(
|
||||
error,
|
||||
'读取 Agent 共创工作区失败。',
|
||||
),
|
||||
);
|
||||
}
|
||||
setAgentSession(null);
|
||||
@@ -426,37 +487,32 @@ export function useRpgCreationSessionController(
|
||||
attempt += 1
|
||||
) {
|
||||
await new Promise((resolve) => {
|
||||
window.setTimeout(
|
||||
resolve,
|
||||
AGENT_DRAFT_RESULT_AUTO_OPEN_RETRY_MS,
|
||||
);
|
||||
window.setTimeout(resolve, AGENT_DRAFT_RESULT_AUTO_OPEN_RETRY_MS);
|
||||
});
|
||||
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const latestSession = activeAgentSessionId
|
||||
? await syncAgentSessionSnapshot(activeAgentSessionId).catch(
|
||||
const latestResultView = activeAgentSessionId
|
||||
? await syncAgentCreationResultView(activeAgentSessionId).catch(
|
||||
() => null,
|
||||
)
|
||||
: agentSession;
|
||||
: null;
|
||||
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const draftResultProfile =
|
||||
rpgCreationPreviewAdapter.buildPreviewFromSession(
|
||||
latestSession ?? agentSession,
|
||||
rpgCreationPreviewAdapter.buildPreviewFromResultView(
|
||||
latestResultView,
|
||||
);
|
||||
if (!draftResultProfile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
setGeneratedCustomWorldProfile(
|
||||
normalizeAgentBackedProfile(draftResultProfile),
|
||||
);
|
||||
setGeneratedCustomWorldProfile(draftResultProfile);
|
||||
setAgentDraftGenerationStartedAt(null);
|
||||
setCustomWorldGenerationViewSource(null);
|
||||
setCustomWorldResultViewSource('agent-draft');
|
||||
@@ -479,7 +535,7 @@ export function useRpgCreationSessionController(
|
||||
customWorldGenerationViewSource,
|
||||
selectionStage,
|
||||
setSelectionStage,
|
||||
syncAgentSessionSnapshot,
|
||||
syncAgentCreationResultView,
|
||||
]);
|
||||
|
||||
const agentDraftSettingPreview = useMemo(
|
||||
@@ -490,25 +546,6 @@ export function useRpgCreationSessionController(
|
||||
() => buildAgentDraftFoundationAnchorEntries(agentSession),
|
||||
[agentSession],
|
||||
);
|
||||
const agentDraftResultProfile = useMemo(
|
||||
() => rpgCreationPreviewAdapter.buildPreviewFromSession(agentSession),
|
||||
[agentSession],
|
||||
);
|
||||
const shouldAutoOpenAgentDraftResult = useMemo(
|
||||
() =>
|
||||
Boolean(
|
||||
agentDraftResultProfile &&
|
||||
agentSession &&
|
||||
(agentSession.stage === 'object_refining' ||
|
||||
agentSession.stage === 'visual_refining' ||
|
||||
agentSession.stage === 'long_tail_review' ||
|
||||
agentSession.stage === 'ready_to_publish' ||
|
||||
agentSession.stage === 'published') &&
|
||||
agentSession.draftCards.length > 0,
|
||||
),
|
||||
[agentDraftResultProfile, agentSession],
|
||||
);
|
||||
|
||||
const agentDraftGenerationProgress = useMemo(
|
||||
() =>
|
||||
buildAgentDraftFoundationGenerationProgress(
|
||||
@@ -530,7 +567,11 @@ export function useRpgCreationSessionController(
|
||||
: null;
|
||||
|
||||
useEffect(() => {
|
||||
if (!shouldAutoOpenAgentDraftResult || !agentDraftResultProfile) {
|
||||
if (
|
||||
!agentSession ||
|
||||
!activeAgentSessionId ||
|
||||
agentSession.draftCards.length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -538,28 +579,63 @@ export function useRpgCreationSessionController(
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectionStage === 'agent-workspace') {
|
||||
setGeneratedCustomWorldProfile(agentDraftResultProfile);
|
||||
setCustomWorldResultViewSource('agent-draft');
|
||||
isAgentDraftResultAutoOpenSuppressedRef.current = false;
|
||||
setSelectionStage('custom-world-result');
|
||||
if (
|
||||
selectionStage !== 'agent-workspace' &&
|
||||
selectionStage !== 'custom-world-result'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
selectionStage === 'custom-world-result' &&
|
||||
!generatedCustomWorldProfile
|
||||
generatedCustomWorldProfile
|
||||
) {
|
||||
setGeneratedCustomWorldProfile(agentDraftResultProfile);
|
||||
setCustomWorldResultViewSource('agent-draft');
|
||||
isAgentDraftResultAutoOpenSuppressedRef.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const requestId = latestAgentResultViewOpenRequestIdRef.current + 1;
|
||||
latestAgentResultViewOpenRequestIdRef.current = requestId;
|
||||
let cancelled = false;
|
||||
|
||||
void syncAgentCreationResultView(activeAgentSessionId)
|
||||
.then((resultView) => {
|
||||
if (
|
||||
cancelled ||
|
||||
latestAgentResultViewOpenRequestIdRef.current !== requestId ||
|
||||
isAgentDraftResultAutoOpenSuppressedRef.current ||
|
||||
resultView.targetStage !== 'custom-world-result'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resultProfile =
|
||||
rpgCreationPreviewAdapter.buildPreviewFromResultView(resultView);
|
||||
if (!resultProfile) {
|
||||
return;
|
||||
}
|
||||
|
||||
setGeneratedCustomWorldProfile(resultProfile);
|
||||
setCustomWorldGenerationViewSource(null);
|
||||
setCustomWorldResultViewSource(
|
||||
resultView.resultViewSource ?? 'agent-draft',
|
||||
);
|
||||
isAgentDraftResultAutoOpenSuppressedRef.current = false;
|
||||
if (selectionStage === 'agent-workspace') {
|
||||
setSelectionStage('custom-world-result');
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [
|
||||
agentDraftResultProfile,
|
||||
activeAgentSessionId,
|
||||
agentSession,
|
||||
generatedCustomWorldProfile,
|
||||
selectionStage,
|
||||
setSelectionStage,
|
||||
shouldAutoOpenAgentDraftResult,
|
||||
syncAgentCreationResultView,
|
||||
]);
|
||||
|
||||
const openRpgAgentWorkspace = useCallback(
|
||||
@@ -689,26 +765,26 @@ export function useRpgCreationSessionController(
|
||||
kind: 'warning',
|
||||
text: errorMessage,
|
||||
});
|
||||
setAgentSession((current) =>
|
||||
{
|
||||
const mergedCurrentSession = mergePendingAgentUserMessageIntoSession(
|
||||
current,
|
||||
pendingMessagePayload,
|
||||
);
|
||||
return mergedCurrentSession
|
||||
? {
|
||||
...mergedCurrentSession,
|
||||
messages: [...mergedCurrentSession.messages, warningMessage],
|
||||
updatedAt: warningMessage.createdAt,
|
||||
}
|
||||
: current;
|
||||
},
|
||||
);
|
||||
setAgentSession((current) => {
|
||||
const mergedCurrentSession = mergePendingAgentUserMessageIntoSession(
|
||||
current,
|
||||
pendingMessagePayload,
|
||||
);
|
||||
return mergedCurrentSession
|
||||
? {
|
||||
...mergedCurrentSession,
|
||||
messages: [...mergedCurrentSession.messages, warningMessage],
|
||||
updatedAt: warningMessage.createdAt,
|
||||
}
|
||||
: current;
|
||||
});
|
||||
setPendingAgentUserMessage(null);
|
||||
setStreamingAgentReplyText('');
|
||||
persistAgentUiState(activeAgentSessionId, null);
|
||||
} finally {
|
||||
if (activeAgentReplyAbortControllerRef.current === replyAbortController) {
|
||||
if (
|
||||
activeAgentReplyAbortControllerRef.current === replyAbortController
|
||||
) {
|
||||
activeAgentReplyAbortControllerRef.current = null;
|
||||
}
|
||||
if (!replyAbortController.signal.aborted) {
|
||||
@@ -780,9 +856,7 @@ export function useRpgCreationSessionController(
|
||||
|
||||
const setNormalizedGeneratedCustomWorldProfile = useCallback(
|
||||
(profile: CustomWorldProfile | null) => {
|
||||
setGeneratedCustomWorldProfile(
|
||||
profile ? normalizeAgentBackedProfile(profile) : null,
|
||||
);
|
||||
setGeneratedCustomWorldProfile(profile);
|
||||
},
|
||||
[],
|
||||
);
|
||||
@@ -807,7 +881,8 @@ export function useRpgCreationSessionController(
|
||||
|
||||
return {
|
||||
initialAgentSessionId:
|
||||
shouldRestoreInitialAgentUiStateRef.current
|
||||
shouldRestoreInitialAgentUiStateRef.current &&
|
||||
isInitialAgentUiStateOwnedByCurrentUser
|
||||
? (initialAgentUiStateRef.current.activeSessionId ?? null)
|
||||
: null,
|
||||
isCreatingAgentSession,
|
||||
@@ -837,7 +912,10 @@ export function useRpgCreationSessionController(
|
||||
setAgentDraftGenerationStartedAt,
|
||||
agentDraftSettingPreview,
|
||||
agentDraftAnchorPreviewEntries,
|
||||
agentDraftResultProfile,
|
||||
agentDraftResultProfile:
|
||||
rpgCreationPreviewAdapter.buildPreviewFromResultPreview(
|
||||
agentSession?.resultPreview,
|
||||
),
|
||||
agentDraftGenerationProgress,
|
||||
isAgentDraftGenerationView,
|
||||
isAgentDraftResultView,
|
||||
@@ -848,6 +926,7 @@ export function useRpgCreationSessionController(
|
||||
releaseAgentDraftResultAutoOpenSuppression,
|
||||
persistAgentUiState,
|
||||
syncAgentSessionSnapshot,
|
||||
syncAgentCreationResultView,
|
||||
openRpgAgentWorkspace,
|
||||
submitAgentMessage,
|
||||
executeAgentAction,
|
||||
|
||||
Reference in New Issue
Block a user