1
This commit is contained in:
@@ -171,6 +171,9 @@ import { getRpgProfilePlayStats } from '../../services/rpg-entry/rpgProfileClien
|
||||
import { requestRpgRuntimeJson } from '../../services/rpg-runtime/rpgRuntimeRequest';
|
||||
import type { CustomWorldProfile } from '../../types';
|
||||
import { useAuthUi } from '../auth/AuthUiContext';
|
||||
import { PublishShareModal } from '../common/PublishShareModal';
|
||||
import type { PublishShareModalPayload } from '../common/publishShareModalModel';
|
||||
import { UnifiedModal } from '../common/UnifiedModal';
|
||||
import {
|
||||
isBigFishGalleryEntry,
|
||||
isMatch3DGalleryEntry,
|
||||
@@ -227,6 +230,13 @@ type PuzzleSaveArchiveState = {
|
||||
currentLevelId?: unknown;
|
||||
};
|
||||
|
||||
type DeleteCreationWorkConfirmation = {
|
||||
id: string;
|
||||
title: string;
|
||||
detail: string;
|
||||
run: () => void;
|
||||
};
|
||||
|
||||
async function resumePuzzleProfileSaveArchiveRaw(worldKey: string) {
|
||||
return requestRpgRuntimeJson<
|
||||
ProfileSaveArchiveResumeResponse<PuzzleSaveArchiveState>
|
||||
@@ -345,7 +355,10 @@ function mapPublicWorkDetailToMatch3DWork(
|
||||
workId: entry.workId,
|
||||
profileId: entry.profileId,
|
||||
ownerUserId: entry.ownerUserId,
|
||||
sourceSessionId: null,
|
||||
sourceSessionId:
|
||||
'sourceSessionId' in entry && typeof entry.sourceSessionId === 'string'
|
||||
? entry.sourceSessionId
|
||||
: null,
|
||||
gameName: entry.worldName,
|
||||
themeText: entry.themeTags[0] ?? '经典消除',
|
||||
summary: entry.summaryText,
|
||||
@@ -403,7 +416,10 @@ function mapPublicWorkDetailToPuzzleWork(
|
||||
workId: entry.workId,
|
||||
profileId: entry.profileId,
|
||||
ownerUserId: entry.ownerUserId,
|
||||
sourceSessionId: null,
|
||||
sourceSessionId:
|
||||
'sourceSessionId' in entry && typeof entry.sourceSessionId === 'string'
|
||||
? entry.sourceSessionId
|
||||
: null,
|
||||
authorDisplayName: entry.authorDisplayName,
|
||||
levelName: entry.worldName,
|
||||
summary: entry.summaryText,
|
||||
@@ -1000,10 +1016,14 @@ export function PlatformEntryFlowShellImpl({
|
||||
const [deletingCreationWorkId, setDeletingCreationWorkId] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [pendingDeleteCreationWork, setPendingDeleteCreationWork] =
|
||||
useState<DeleteCreationWorkConfirmation | null>(null);
|
||||
const [
|
||||
claimingPuzzlePointIncentiveProfileId,
|
||||
setClaimingPuzzlePointIncentiveProfileId,
|
||||
] = useState<string | null>(null);
|
||||
const [publishSharePayload, setPublishSharePayload] =
|
||||
useState<PublishShareModalPayload | null>(null);
|
||||
const isBigFishCreationVisible = isPlatformCreationTypeVisible('big-fish');
|
||||
const [profilePlayStats, setProfilePlayStats] =
|
||||
useState<ProfilePlayStatsResponse | null>(null);
|
||||
@@ -1279,6 +1299,50 @@ export function PlatformEntryFlowShellImpl({
|
||||
() => agentResultPreview?.qualityFindings ?? [],
|
||||
[agentResultPreview],
|
||||
);
|
||||
|
||||
const openPublishShareModal = useCallback(
|
||||
(payload: PublishShareModalPayload) => {
|
||||
const publicWorkCode = payload.publicWorkCode.trim();
|
||||
if (!publicWorkCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPublishSharePayload({
|
||||
...payload,
|
||||
publicWorkCode,
|
||||
title: payload.title.trim() || '我的作品',
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const openRpgPublishShareModal = useCallback(
|
||||
async (profile: CustomWorldProfile | null | undefined) => {
|
||||
const profileId = profile?.id?.trim();
|
||||
if (!profileId) {
|
||||
return;
|
||||
}
|
||||
const profileName = profile?.name?.trim() || '我的作品';
|
||||
|
||||
const galleryEntries = await platformBootstrap
|
||||
.refreshPublishedGallery()
|
||||
.catch(() => [] as CustomWorldGalleryCard[]);
|
||||
const galleryEntry = galleryEntries.find(
|
||||
(entry) => entry.profileId === profileId,
|
||||
);
|
||||
const publicWorkCode = galleryEntry?.publicWorkCode?.trim();
|
||||
if (!publicWorkCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
openPublishShareModal({
|
||||
title: galleryEntry?.worldName || profileName,
|
||||
publicWorkCode,
|
||||
stage: 'work-detail',
|
||||
});
|
||||
},
|
||||
[openPublishShareModal, platformBootstrap],
|
||||
);
|
||||
const agentResultPreviewSourceLabel = useMemo(() => {
|
||||
if (!agentResultPreview?.source) {
|
||||
return null;
|
||||
@@ -1347,6 +1411,13 @@ export function PlatformEntryFlowShellImpl({
|
||||
const resultViewError =
|
||||
autosaveCoordinator.customWorldAutoSaveError ??
|
||||
sessionController.customWorldError;
|
||||
const isSelectedPublicWorkOwned = Boolean(
|
||||
authUi?.user?.id &&
|
||||
selectedPublicWorkDetail?.ownerUserId === authUi.user.id,
|
||||
);
|
||||
const selectedPublicWorkActionMode = isSelectedPublicWorkOwned
|
||||
? 'edit'
|
||||
: 'remix';
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
@@ -1374,6 +1445,37 @@ export function PlatformEntryFlowShellImpl({
|
||||
[authUi],
|
||||
);
|
||||
|
||||
const requestDeleteCreationWork = useCallback(
|
||||
(confirmation: DeleteCreationWorkConfirmation) => {
|
||||
if (deletingCreationWorkId) {
|
||||
return;
|
||||
}
|
||||
|
||||
runProtectedAction(() => {
|
||||
setPendingDeleteCreationWork(confirmation);
|
||||
});
|
||||
},
|
||||
[deletingCreationWorkId, runProtectedAction],
|
||||
);
|
||||
|
||||
const closeDeleteCreationWorkConfirmation = useCallback(() => {
|
||||
if (deletingCreationWorkId) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPendingDeleteCreationWork(null);
|
||||
}, [deletingCreationWorkId]);
|
||||
|
||||
const confirmDeleteCreationWork = useCallback(() => {
|
||||
const confirmation = pendingDeleteCreationWork;
|
||||
if (!confirmation || deletingCreationWorkId) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPendingDeleteCreationWork(null);
|
||||
confirmation.run();
|
||||
}, [deletingCreationWorkId, pendingDeleteCreationWork]);
|
||||
|
||||
const prepareCreationLaunch = useCallback(() => {
|
||||
if (sessionController.isCreatingAgentSession) {
|
||||
return false;
|
||||
@@ -1433,6 +1535,11 @@ export function PlatformEntryFlowShellImpl({
|
||||
if (payload.action === 'big_fish_publish_game') {
|
||||
void refreshBigFishShelf();
|
||||
void refreshBigFishGallery();
|
||||
openPublishShareModal({
|
||||
title: response.session.draft?.title ?? '大鱼吃小鱼',
|
||||
publicWorkCode: buildBigFishPublicWorkCode(response.session.sessionId),
|
||||
stage: 'big-fish-runtime',
|
||||
});
|
||||
}
|
||||
if (payload.action !== 'big_fish_compile_draft') {
|
||||
return;
|
||||
@@ -1610,6 +1717,11 @@ export function PlatformEntryFlowShellImpl({
|
||||
buildPuzzlePublicWorkCode(galleryDetail.item.profileId),
|
||||
),
|
||||
);
|
||||
openPublishShareModal({
|
||||
title: galleryDetail.item.workTitle || galleryDetail.item.levelName,
|
||||
publicWorkCode: buildPuzzlePublicWorkCode(galleryDetail.item.profileId),
|
||||
stage: 'puzzle-gallery-detail',
|
||||
});
|
||||
}
|
||||
},
|
||||
beforeExecuteAction: ({ payload }) => {
|
||||
@@ -1805,6 +1917,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
setPuzzleError(null);
|
||||
setDeletingCreationWorkId(null);
|
||||
setClaimingPuzzlePointIncentiveProfileId(null);
|
||||
setPublishSharePayload(null);
|
||||
setProfilePlayStats(null);
|
||||
setProfilePlayStatsError(null);
|
||||
setIsProfilePlayStatsOpen(false);
|
||||
@@ -2623,6 +2736,46 @@ export function PlatformEntryFlowShellImpl({
|
||||
],
|
||||
);
|
||||
|
||||
const remodelCurrentPuzzleRuntimeWork = useCallback((profileId: string) => {
|
||||
const targetProfileId = profileId.trim();
|
||||
if (!targetProfileId || isPublicWorkDetailBusy || isPuzzleBusy) {
|
||||
return;
|
||||
}
|
||||
|
||||
runProtectedAction(() => {
|
||||
setIsPublicWorkDetailBusy(true);
|
||||
setIsPuzzleBusy(true);
|
||||
setPuzzleError(null);
|
||||
setPublicWorkDetailError(null);
|
||||
|
||||
void remixPuzzleGalleryWork(targetProfileId)
|
||||
.then((response) => {
|
||||
puzzleFlow.setSession(response.session);
|
||||
setPuzzleOperation(null);
|
||||
setPuzzleRun(null);
|
||||
enterCreateTab();
|
||||
setSelectionStage('puzzle-result');
|
||||
})
|
||||
.catch((error) => {
|
||||
setPuzzleError(resolvePuzzleErrorMessage(error, '改造拼图作品失败。'));
|
||||
})
|
||||
.finally(() => {
|
||||
setIsPublicWorkDetailBusy(false);
|
||||
setIsPuzzleBusy(false);
|
||||
});
|
||||
});
|
||||
}, [
|
||||
enterCreateTab,
|
||||
isPublicWorkDetailBusy,
|
||||
isPuzzleBusy,
|
||||
puzzleFlow,
|
||||
resolvePuzzleErrorMessage,
|
||||
runProtectedAction,
|
||||
setIsPuzzleBusy,
|
||||
setPuzzleError,
|
||||
setSelectionStage,
|
||||
]);
|
||||
|
||||
const leaveAgentWorkspace = useCallback(() => {
|
||||
enterCreateTab();
|
||||
sessionController.resetSessionViewState();
|
||||
@@ -2696,34 +2849,32 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
runProtectedAction(() => {
|
||||
const confirmed = window.confirm(
|
||||
`确认删除作品《${entry.worldName}》吗?删除后会从你的作品列表和公开广场中移除。`,
|
||||
);
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
requestDeleteCreationWork({
|
||||
id: entry.profileId,
|
||||
title: entry.worldName,
|
||||
detail: '删除后会从你的作品列表和公开广场中移除。',
|
||||
run: () => {
|
||||
setDeletingCreationWorkId(entry.profileId);
|
||||
platformBootstrap.setPlatformError(null);
|
||||
|
||||
setDeletingCreationWorkId(entry.profileId);
|
||||
platformBootstrap.setPlatformError(null);
|
||||
|
||||
void deleteRpgEntryWorldProfile(entry.profileId)
|
||||
.then(async (entries) => {
|
||||
platformBootstrap.setSavedCustomWorldEntries(entries);
|
||||
await platformBootstrap.refreshCustomWorldWorks().catch(() => []);
|
||||
await platformBootstrap.refreshPublishedGallery().catch(() => []);
|
||||
})
|
||||
.catch((error) => {
|
||||
platformBootstrap.setPlatformError(
|
||||
resolveRpgCreationErrorMessage(error, '删除自定义世界失败。'),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeletingCreationWorkId(null);
|
||||
});
|
||||
void deleteRpgEntryWorldProfile(entry.profileId)
|
||||
.then(async (entries) => {
|
||||
platformBootstrap.setSavedCustomWorldEntries(entries);
|
||||
await platformBootstrap.refreshCustomWorldWorks().catch(() => []);
|
||||
await platformBootstrap.refreshPublishedGallery().catch(() => []);
|
||||
})
|
||||
.catch((error) => {
|
||||
platformBootstrap.setPlatformError(
|
||||
resolveRpgCreationErrorMessage(error, '删除自定义世界失败。'),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeletingCreationWorkId(null);
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
[deletingCreationWorkId, platformBootstrap, runProtectedAction],
|
||||
[deletingCreationWorkId, platformBootstrap, requestDeleteCreationWork],
|
||||
);
|
||||
|
||||
const handleDeletePublishedWork = useCallback(
|
||||
@@ -2732,47 +2883,51 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
runProtectedAction(() => {
|
||||
const confirmed = window.confirm(
|
||||
`确认删除作品《${work.title}》吗?删除后会从你的作品列表和公开广场中移除。`,
|
||||
);
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
setDeletingCreationWorkId(work.workId);
|
||||
platformBootstrap.setPlatformError(null);
|
||||
requestDeleteCreationWork({
|
||||
id: work.workId,
|
||||
title: work.title,
|
||||
detail:
|
||||
work.status === 'published'
|
||||
? '删除后会从你的作品列表和公开广场中移除。'
|
||||
: '删除后会从你的作品列表中移除。',
|
||||
run: () => {
|
||||
setDeletingCreationWorkId(work.workId);
|
||||
platformBootstrap.setPlatformError(null);
|
||||
|
||||
const deleteTask =
|
||||
work.sourceType === 'published_profile' && work.profileId
|
||||
? deleteRpgEntryWorldProfile(work.profileId).then(
|
||||
async (entries) => {
|
||||
platformBootstrap.setSavedCustomWorldEntries(entries);
|
||||
await platformBootstrap
|
||||
.refreshCustomWorldWorks()
|
||||
.catch(() => []);
|
||||
},
|
||||
)
|
||||
: work.sourceType === 'agent_session' && work.sessionId
|
||||
? deleteRpgCreationAgentSession(work.sessionId).then((items) => {
|
||||
platformBootstrap.setCustomWorldWorkEntries(items);
|
||||
})
|
||||
: Promise.reject(new Error('当前 RPG 作品缺少可删除 ID。'));
|
||||
const deleteTask =
|
||||
work.sourceType === 'published_profile' && work.profileId
|
||||
? deleteRpgEntryWorldProfile(work.profileId).then(
|
||||
async (entries) => {
|
||||
platformBootstrap.setSavedCustomWorldEntries(entries);
|
||||
await platformBootstrap
|
||||
.refreshCustomWorldWorks()
|
||||
.catch(() => []);
|
||||
},
|
||||
)
|
||||
: work.sourceType === 'agent_session' && work.sessionId
|
||||
? deleteRpgCreationAgentSession(work.sessionId).then(
|
||||
(items) => {
|
||||
platformBootstrap.setCustomWorldWorkEntries(items);
|
||||
},
|
||||
)
|
||||
: Promise.reject(new Error('当前 RPG 作品缺少可删除 ID。'));
|
||||
|
||||
void deleteTask
|
||||
.then(async () => {
|
||||
await platformBootstrap.refreshPublishedGallery().catch(() => []);
|
||||
})
|
||||
.catch((error) => {
|
||||
platformBootstrap.setPlatformError(
|
||||
resolveRpgCreationErrorMessage(error, '删除自定义世界失败。'),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeletingCreationWorkId(null);
|
||||
});
|
||||
void deleteTask
|
||||
.then(async () => {
|
||||
await platformBootstrap.refreshPublishedGallery().catch(() => []);
|
||||
})
|
||||
.catch((error) => {
|
||||
platformBootstrap.setPlatformError(
|
||||
resolveRpgCreationErrorMessage(error, '删除自定义世界失败。'),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeletingCreationWorkId(null);
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
[deletingCreationWorkId, platformBootstrap, runProtectedAction],
|
||||
[deletingCreationWorkId, platformBootstrap, requestDeleteCreationWork],
|
||||
);
|
||||
|
||||
const handleDeleteBigFishWork = useCallback(
|
||||
@@ -2781,37 +2936,39 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
runProtectedAction(() => {
|
||||
const confirmed = window.confirm(
|
||||
`确认删除作品《${work.title}》吗?删除后会从你的作品列表中移除。`,
|
||||
);
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
requestDeleteCreationWork({
|
||||
id: work.workId,
|
||||
title: work.title,
|
||||
detail:
|
||||
work.status === 'published'
|
||||
? '删除后会从你的作品列表和公开广场中移除。'
|
||||
: '删除后会从你的作品列表中移除。',
|
||||
run: () => {
|
||||
setDeletingCreationWorkId(work.workId);
|
||||
setBigFishError(null);
|
||||
|
||||
setDeletingCreationWorkId(work.workId);
|
||||
setBigFishError(null);
|
||||
|
||||
void deleteBigFishWork(work.sourceSessionId)
|
||||
.then(async (response) => {
|
||||
setBigFishWorks(response.items);
|
||||
await refreshBigFishGallery().catch(() => []);
|
||||
})
|
||||
.catch((error) => {
|
||||
setBigFishError(
|
||||
resolveBigFishErrorMessage(error, '删除大鱼吃小鱼作品失败。'),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeletingCreationWorkId(null);
|
||||
});
|
||||
void deleteBigFishWork(work.sourceSessionId)
|
||||
.then(async (response) => {
|
||||
setBigFishWorks(response.items);
|
||||
await refreshBigFishGallery().catch(() => []);
|
||||
})
|
||||
.catch((error) => {
|
||||
setBigFishError(
|
||||
resolveBigFishErrorMessage(error, '删除大鱼吃小鱼作品失败。'),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeletingCreationWorkId(null);
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
[
|
||||
deletingCreationWorkId,
|
||||
refreshBigFishGallery,
|
||||
requestDeleteCreationWork,
|
||||
resolveBigFishErrorMessage,
|
||||
runProtectedAction,
|
||||
setBigFishError,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -2821,40 +2978,42 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
runProtectedAction(() => {
|
||||
const displayName =
|
||||
work.workTitle?.trim() || work.levelName.trim() || '未命名拼图';
|
||||
const confirmed = window.confirm(
|
||||
`确认删除作品《${displayName}》吗?删除后会从你的作品列表和公开广场中移除。`,
|
||||
);
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
const displayName =
|
||||
work.workTitle?.trim() || work.levelName.trim() || '未命名拼图';
|
||||
requestDeleteCreationWork({
|
||||
id: work.workId,
|
||||
title: displayName,
|
||||
detail:
|
||||
work.publicationStatus === 'published'
|
||||
? '删除后会从你的作品列表和公开广场中移除。'
|
||||
: '删除后会从你的作品列表中移除。',
|
||||
run: () => {
|
||||
setDeletingCreationWorkId(work.workId);
|
||||
setPuzzleFormDraftPayload(null);
|
||||
setPuzzleError(null);
|
||||
|
||||
setDeletingCreationWorkId(work.workId);
|
||||
setPuzzleFormDraftPayload(null);
|
||||
setPuzzleError(null);
|
||||
|
||||
void deletePuzzleWork(work.profileId)
|
||||
.then((response) => {
|
||||
setPuzzleWorks(response.items);
|
||||
void refreshPuzzleGallery();
|
||||
})
|
||||
.catch((error) => {
|
||||
setPuzzleError(
|
||||
resolvePuzzleErrorMessage(error, '删除拼图作品失败。'),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeletingCreationWorkId(null);
|
||||
});
|
||||
void deletePuzzleWork(work.profileId)
|
||||
.then((response) => {
|
||||
setPuzzleWorks(response.items);
|
||||
void refreshPuzzleGallery();
|
||||
})
|
||||
.catch((error) => {
|
||||
setPuzzleError(
|
||||
resolvePuzzleErrorMessage(error, '删除拼图作品失败。'),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeletingCreationWorkId(null);
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
[
|
||||
deletingCreationWorkId,
|
||||
refreshPuzzleGallery,
|
||||
requestDeleteCreationWork,
|
||||
resolvePuzzleErrorMessage,
|
||||
runProtectedAction,
|
||||
setPuzzleError,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -2864,37 +3023,38 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
runProtectedAction(() => {
|
||||
const confirmed = window.confirm(
|
||||
`确认删除作品《${work.gameName}》吗?删除后会从你的作品列表中移除。`,
|
||||
);
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
requestDeleteCreationWork({
|
||||
id: work.workId,
|
||||
title: work.gameName,
|
||||
detail:
|
||||
work.publicationStatus === 'published'
|
||||
? '删除后会从你的作品列表和公开广场中移除。'
|
||||
: '删除后会从你的作品列表中移除。',
|
||||
run: () => {
|
||||
setDeletingCreationWorkId(work.workId);
|
||||
setMatch3DError(null);
|
||||
|
||||
setDeletingCreationWorkId(work.workId);
|
||||
setMatch3DError(null);
|
||||
|
||||
void deleteMatch3DWork(work.profileId)
|
||||
.then((response) => {
|
||||
setMatch3DWorks(response.items);
|
||||
void refreshMatch3DGallery();
|
||||
})
|
||||
.catch((error) => {
|
||||
setMatch3DError(
|
||||
resolveMatch3DErrorMessage(error, '删除抓大鹅作品失败。'),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeletingCreationWorkId(null);
|
||||
});
|
||||
void deleteMatch3DWork(work.profileId)
|
||||
.then((response) => {
|
||||
setMatch3DWorks(response.items);
|
||||
void refreshMatch3DGallery();
|
||||
})
|
||||
.catch((error) => {
|
||||
setMatch3DError(
|
||||
resolveMatch3DErrorMessage(error, '删除抓大鹅作品失败。'),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeletingCreationWorkId(null);
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
[
|
||||
deletingCreationWorkId,
|
||||
refreshMatch3DGallery,
|
||||
requestDeleteCreationWork,
|
||||
resolveMatch3DErrorMessage,
|
||||
runProtectedAction,
|
||||
setMatch3DError,
|
||||
],
|
||||
);
|
||||
@@ -3326,12 +3486,15 @@ export function PlatformEntryFlowShellImpl({
|
||||
);
|
||||
|
||||
const openMatch3DDraft = useCallback(
|
||||
async (item: Match3DWorkSummary) => {
|
||||
async (
|
||||
item: Match3DWorkSummary,
|
||||
options: { forceDraft?: boolean } = {},
|
||||
) => {
|
||||
setMatch3DRun(null);
|
||||
setMatch3DError(null);
|
||||
setMatch3DProfile(null);
|
||||
|
||||
if (item.publicationStatus === 'published') {
|
||||
if (item.publicationStatus === 'published' && !options.forceDraft) {
|
||||
openPublicWorkDetail(mapMatch3DWorkToPublicWorkDetail(item));
|
||||
return;
|
||||
}
|
||||
@@ -3368,6 +3531,19 @@ export function PlatformEntryFlowShellImpl({
|
||||
],
|
||||
);
|
||||
|
||||
const openBigFishDraft = useCallback(
|
||||
async (item: BigFishWorkSummary) => {
|
||||
setBigFishRun(null);
|
||||
const restoredSession = await bigFishFlow.restoreDraft(
|
||||
item.sourceSessionId,
|
||||
);
|
||||
if (!restoredSession) {
|
||||
await refreshBigFishShelf().catch(() => undefined);
|
||||
}
|
||||
},
|
||||
[bigFishFlow, refreshBigFishShelf],
|
||||
);
|
||||
|
||||
const startBigFishRunFromWork = useCallback(
|
||||
(
|
||||
item: BigFishWorkSummary,
|
||||
@@ -3580,12 +3756,94 @@ export function PlatformEntryFlowShellImpl({
|
||||
],
|
||||
);
|
||||
|
||||
const editOwnedPublicWork = useCallback(
|
||||
(entry: PlatformPublicGalleryCard) => {
|
||||
if (isPublicWorkDetailBusy) {
|
||||
return;
|
||||
}
|
||||
|
||||
runProtectedAction(() => {
|
||||
setPublicWorkDetailError(null);
|
||||
|
||||
// 中文注释:自有公开作品必须恢复原草稿,不能复用 remix 复制链路。
|
||||
if (isBigFishGalleryEntry(entry)) {
|
||||
const work = mapPublicWorkDetailToBigFishWork(entry);
|
||||
if (!work?.sourceSessionId?.trim()) {
|
||||
setPublicWorkDetailError(
|
||||
'这份大鱼吃小鱼作品缺少原草稿会话,暂时无法编辑。',
|
||||
);
|
||||
return;
|
||||
}
|
||||
void openBigFishDraft(work);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPuzzleGalleryEntry(entry)) {
|
||||
const work =
|
||||
selectedPuzzleDetail?.profileId === entry.profileId
|
||||
? selectedPuzzleDetail
|
||||
: mapPublicWorkDetailToPuzzleWork(entry);
|
||||
if (!work?.sourceSessionId?.trim()) {
|
||||
setPublicWorkDetailError(
|
||||
'这份拼图作品缺少原草稿会话,暂时无法编辑。',
|
||||
);
|
||||
return;
|
||||
}
|
||||
void openPuzzleDraft(work);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMatch3DGalleryEntry(entry)) {
|
||||
const work = mapPublicWorkDetailToMatch3DWork(entry);
|
||||
if (!work?.sourceSessionId?.trim()) {
|
||||
setPublicWorkDetailError(
|
||||
'这份抓大鹅作品缺少原草稿会话,暂时无法编辑。',
|
||||
);
|
||||
return;
|
||||
}
|
||||
void openMatch3DDraft(work, { forceDraft: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const editEntry =
|
||||
selectedDetailEntry?.profileId === entry.profileId
|
||||
? selectedDetailEntry
|
||||
: null;
|
||||
if (!editEntry) {
|
||||
setPublicWorkDetailError('作品详情尚未读取完成。');
|
||||
return;
|
||||
}
|
||||
|
||||
void detailNavigation.openSavedCustomWorldEditor(editEntry);
|
||||
});
|
||||
},
|
||||
[
|
||||
detailNavigation,
|
||||
isPublicWorkDetailBusy,
|
||||
openBigFishDraft,
|
||||
openMatch3DDraft,
|
||||
openPuzzleDraft,
|
||||
runProtectedAction,
|
||||
selectedDetailEntry,
|
||||
selectedPuzzleDetail,
|
||||
],
|
||||
);
|
||||
|
||||
const remixSelectedPublicWork = useCallback(() => {
|
||||
if (!selectedPublicWorkDetail) {
|
||||
return;
|
||||
}
|
||||
if (isSelectedPublicWorkOwned) {
|
||||
editOwnedPublicWork(selectedPublicWorkDetail);
|
||||
return;
|
||||
}
|
||||
remixPublicWork(selectedPublicWorkDetail);
|
||||
}, [remixPublicWork, selectedPublicWorkDetail]);
|
||||
}, [
|
||||
editOwnedPublicWork,
|
||||
isSelectedPublicWorkOwned,
|
||||
remixPublicWork,
|
||||
selectedPublicWorkDetail,
|
||||
]);
|
||||
|
||||
const handlePublicCodeSearch = useCallback(
|
||||
async (keyword: string) => {
|
||||
@@ -3906,19 +4164,6 @@ export function PlatformEntryFlowShellImpl({
|
||||
void handlePublicCodeSearch(publicWorkCode);
|
||||
}, [handlePublicCodeSearch, initialPublicWorkCode]);
|
||||
|
||||
const openBigFishDraft = useCallback(
|
||||
async (item: BigFishWorkSummary) => {
|
||||
setBigFishRun(null);
|
||||
const restoredSession = await bigFishFlow.restoreDraft(
|
||||
item.sourceSessionId,
|
||||
);
|
||||
if (!restoredSession) {
|
||||
await refreshBigFishShelf().catch(() => undefined);
|
||||
}
|
||||
},
|
||||
[bigFishFlow, refreshBigFishShelf],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectionStage === 'platform') {
|
||||
if (isBigFishCreationVisible) {
|
||||
@@ -4209,6 +4454,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
isMatch3DBusy
|
||||
}
|
||||
error={publicWorkDetailError}
|
||||
actionMode={selectedPublicWorkActionMode}
|
||||
visibleCoverCount={resolveVisiblePuzzleDetailCoverCount(
|
||||
selectedPublicWorkDetail,
|
||||
puzzleRun,
|
||||
@@ -4250,6 +4496,9 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
isBusy={detailNavigation.isMutatingDetail}
|
||||
error={detailNavigation.detailError}
|
||||
actionMode={
|
||||
detailNavigation.isSelectedWorldOwned ? 'edit' : 'remix'
|
||||
}
|
||||
onBack={() => {
|
||||
detailNavigation.setDetailError(null);
|
||||
clearSelectedPublicWorkAuthor();
|
||||
@@ -4262,9 +4511,13 @@ export function PlatformEntryFlowShellImpl({
|
||||
}}
|
||||
onStart={handleStartSelectedWorld}
|
||||
onRemix={() => {
|
||||
remixPublicWork(
|
||||
mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry),
|
||||
);
|
||||
const publicWorkEntry =
|
||||
mapRpgGalleryCardToPublicWorkDetail(selectedDetailEntry);
|
||||
if (detailNavigation.isSelectedWorldOwned) {
|
||||
editOwnedPublicWork(publicWorkEntry);
|
||||
return;
|
||||
}
|
||||
remixPublicWork(publicWorkEntry);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
@@ -4574,6 +4827,13 @@ export function PlatformEntryFlowShellImpl({
|
||||
openPublicWorkDetail(
|
||||
mapMatch3DWorkToPublicWorkDetail(profile),
|
||||
);
|
||||
openPublishShareModal({
|
||||
title: profile.gameName,
|
||||
publicWorkCode: buildMatch3DPublicWorkCode(
|
||||
profile.profileId,
|
||||
),
|
||||
stage: 'work-detail',
|
||||
});
|
||||
}}
|
||||
onStartTestRun={(profile) => {
|
||||
setMatch3DProfile(profile);
|
||||
@@ -4840,6 +5100,11 @@ export function PlatformEntryFlowShellImpl({
|
||||
onBack={() => {
|
||||
setSelectionStage(puzzleRuntimeReturnStage);
|
||||
}}
|
||||
onRemodelWork={
|
||||
selectedPuzzleDetail?.publicationStatus === 'published'
|
||||
? remodelCurrentPuzzleRuntimeWork
|
||||
: undefined
|
||||
}
|
||||
onSwapPieces={(payload) => {
|
||||
void swapPuzzlePiecesInRun(payload);
|
||||
}}
|
||||
@@ -4980,7 +5245,9 @@ export function PlatformEntryFlowShellImpl({
|
||||
sessionController.agentSession?.stage !== 'published'
|
||||
? async () => {
|
||||
try {
|
||||
await enterWorldCoordinator.publishCurrentResult();
|
||||
const publishedProfile =
|
||||
await enterWorldCoordinator.publishCurrentResult();
|
||||
void openRpgPublishShareModal(publishedProfile);
|
||||
} catch (error) {
|
||||
sessionController.setCustomWorldError(
|
||||
resolveRpgCreationErrorMessage(
|
||||
@@ -5144,6 +5411,48 @@ export function PlatformEntryFlowShellImpl({
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<PublishShareModal
|
||||
open={Boolean(publishSharePayload)}
|
||||
payload={publishSharePayload}
|
||||
onClose={() => setPublishSharePayload(null)}
|
||||
/>
|
||||
<UnifiedModal
|
||||
open={Boolean(pendingDeleteCreationWork)}
|
||||
title="删除作品"
|
||||
description={
|
||||
pendingDeleteCreationWork
|
||||
? `确认删除《${pendingDeleteCreationWork.title}》吗?`
|
||||
: undefined
|
||||
}
|
||||
onClose={closeDeleteCreationWorkConfirmation}
|
||||
closeDisabled={Boolean(deletingCreationWorkId)}
|
||||
closeOnBackdrop={!deletingCreationWorkId}
|
||||
size="sm"
|
||||
footer={
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeDeleteCreationWorkConfirmation}
|
||||
disabled={Boolean(deletingCreationWorkId)}
|
||||
className="platform-button platform-button--ghost min-h-0 rounded-full px-4 py-2 text-sm"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={confirmDeleteCreationWork}
|
||||
disabled={Boolean(deletingCreationWorkId)}
|
||||
className="platform-button platform-button--danger min-h-0 rounded-full px-4 py-2 text-sm disabled:cursor-not-allowed disabled:opacity-55"
|
||||
>
|
||||
{deletingCreationWorkId ? '删除中' : '确认删除'}
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className="text-sm leading-6 text-[var(--platform-text-base)]">
|
||||
{pendingDeleteCreationWork?.detail}
|
||||
</div>
|
||||
</UnifiedModal>
|
||||
<AnimatePresence>
|
||||
{(searchedPublicUser || publicSearchError) && (
|
||||
<motion.div
|
||||
|
||||
Reference in New Issue
Block a user