Merge branch 'master' of http://82.157.175.59:3000/GenarrativeAI/Genarrative
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
@@ -27,6 +27,11 @@ import type {
|
||||
Match3DSessionResponse,
|
||||
SendMatch3DMessageRequest,
|
||||
} from '../../../packages/shared/src/contracts/match3dAgent';
|
||||
import type { Match3DRunSnapshot } from '../../../packages/shared/src/contracts/match3dRuntime';
|
||||
import type {
|
||||
Match3DWorkProfile,
|
||||
Match3DWorkSummary,
|
||||
} from '../../../packages/shared/src/contracts/match3dWorks';
|
||||
import type {
|
||||
PuzzleAgentActionRequest,
|
||||
PuzzleAgentOperationRecord,
|
||||
@@ -85,6 +90,19 @@ import {
|
||||
shouldRestoreCustomWorldAgentUiState,
|
||||
} from '../../services/customWorldAgentUiState';
|
||||
import { match3dCreationClient } from '../../services/match3d-creation';
|
||||
import {
|
||||
clickMatch3DItem,
|
||||
finishMatch3DTimeUp,
|
||||
restartMatch3DRun,
|
||||
startMatch3DRun,
|
||||
stopMatch3DRun,
|
||||
} from '../../services/match3d-runtime';
|
||||
import {
|
||||
deleteMatch3DWork,
|
||||
getMatch3DWorkDetail,
|
||||
listMatch3DGallery,
|
||||
listMatch3DWorks,
|
||||
} from '../../services/match3d-works';
|
||||
import {
|
||||
buildBigFishGenerationAnchorEntries,
|
||||
buildMiniGameDraftGenerationProgress,
|
||||
@@ -95,8 +113,10 @@ import {
|
||||
import { getPlatformProfileDashboard } from '../../services/platform-entry/platformProfileClient';
|
||||
import {
|
||||
buildBigFishPublicWorkCode,
|
||||
buildMatch3DPublicWorkCode,
|
||||
buildPuzzlePublicWorkCode,
|
||||
isSameBigFishPublicWorkCode,
|
||||
isSameMatch3DPublicWorkCode,
|
||||
isSamePuzzlePublicWorkCode,
|
||||
} from '../../services/publicWorkCode';
|
||||
import {
|
||||
@@ -153,8 +173,10 @@ import type { CustomWorldProfile } from '../../types';
|
||||
import { useAuthUi } from '../auth/AuthUiContext';
|
||||
import {
|
||||
isBigFishGalleryEntry,
|
||||
isMatch3DGalleryEntry,
|
||||
isPuzzleGalleryEntry,
|
||||
mapBigFishWorkToPlatformGalleryCard,
|
||||
mapMatch3DWorkToPlatformGalleryCard,
|
||||
mapPuzzleWorkToPlatformGalleryCard,
|
||||
type PlatformPublicGalleryCard,
|
||||
} from '../rpg-entry/rpgEntryWorldPresentation';
|
||||
@@ -239,7 +261,9 @@ function getPlatformPublicGalleryEntryKey(entry: PlatformPublicGalleryCard) {
|
||||
? 'big-fish'
|
||||
: isPuzzleGalleryEntry(entry)
|
||||
? 'puzzle'
|
||||
: 'rpg';
|
||||
: isMatch3DGalleryEntry(entry)
|
||||
? 'match3d'
|
||||
: 'rpg';
|
||||
return `${kind}:${entry.ownerUserId}:${entry.profileId}`;
|
||||
}
|
||||
|
||||
@@ -282,12 +306,76 @@ function mapPuzzleWorkToPublicWorkDetail(
|
||||
return mapPuzzleWorkToPlatformGalleryCard(item);
|
||||
}
|
||||
|
||||
function mapMatch3DWorkToPublicWorkDetail(
|
||||
item: Match3DWorkSummary,
|
||||
): PlatformPublicGalleryCard {
|
||||
return mapMatch3DWorkToPlatformGalleryCard(item);
|
||||
}
|
||||
|
||||
function mapBigFishWorkToPublicWorkDetail(
|
||||
item: BigFishWorkSummary,
|
||||
): PlatformPublicGalleryCard {
|
||||
return mapBigFishWorkToPlatformGalleryCard(item);
|
||||
}
|
||||
|
||||
function mapPublicWorkDetailToMatch3DWork(
|
||||
entry: PlatformPublicGalleryCard,
|
||||
): Match3DWorkSummary | null {
|
||||
if (!isMatch3DGalleryEntry(entry)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
workId: entry.workId,
|
||||
profileId: entry.profileId,
|
||||
ownerUserId: entry.ownerUserId,
|
||||
sourceSessionId: null,
|
||||
gameName: entry.worldName,
|
||||
themeText: entry.themeTags[0] ?? '经典消除',
|
||||
summary: entry.summaryText,
|
||||
tags: entry.themeTags,
|
||||
coverImageSrc: entry.coverImageSrc,
|
||||
referenceImageSrc: null,
|
||||
clearCount: 12,
|
||||
difficulty: 4,
|
||||
publicationStatus: 'published',
|
||||
playCount: entry.playCount ?? 0,
|
||||
updatedAt: entry.updatedAt,
|
||||
publishedAt: entry.publishedAt,
|
||||
publishReady: true,
|
||||
};
|
||||
}
|
||||
|
||||
function buildMatch3DProfileFromSession(
|
||||
session: Match3DAgentSessionSnapshot | null,
|
||||
): Match3DWorkProfile | null {
|
||||
const draft = session?.draft;
|
||||
if (!session || !draft?.profileId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const now = session.updatedAt || new Date().toISOString();
|
||||
return {
|
||||
workId: draft.profileId,
|
||||
profileId: draft.profileId,
|
||||
ownerUserId: 'current-user',
|
||||
sourceSessionId: session.sessionId,
|
||||
gameName: draft.gameName,
|
||||
themeText: draft.themeText,
|
||||
summary: draft.summary ?? draft.summaryText ?? '',
|
||||
tags: draft.tags,
|
||||
coverImageSrc: draft.coverImageSrc ?? draft.referenceImageSrc ?? null,
|
||||
referenceImageSrc: draft.referenceImageSrc ?? null,
|
||||
clearCount: draft.clearCount,
|
||||
difficulty: draft.difficulty,
|
||||
publicationStatus: 'draft',
|
||||
playCount: 0,
|
||||
updatedAt: now,
|
||||
publishedAt: null,
|
||||
publishReady: Boolean(draft.publishReady),
|
||||
};
|
||||
}
|
||||
|
||||
function mapPublicWorkDetailToPuzzleWork(
|
||||
entry: PlatformPublicGalleryCard,
|
||||
): PuzzleWorkSummary | null {
|
||||
@@ -686,10 +774,17 @@ const Match3DAgentWorkspace = lazy(async () => {
|
||||
};
|
||||
});
|
||||
|
||||
const Match3DDraftReadyView = lazy(async () => {
|
||||
const module = await import('../match3d-creation/Match3DDraftReadyView');
|
||||
const Match3DResultView = lazy(async () => {
|
||||
const module = await import('../match3d-result/Match3DResultView');
|
||||
return {
|
||||
default: module.Match3DDraftReadyView,
|
||||
default: module.Match3DResultView,
|
||||
};
|
||||
});
|
||||
|
||||
const Match3DRuntimeShell = lazy(async () => {
|
||||
const module = await import('../match3d-runtime/Match3DRuntimeShell');
|
||||
return {
|
||||
default: module.Match3DRuntimeShell,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -823,6 +918,16 @@ export function PlatformEntryFlowShellImpl({
|
||||
const [bigFishGalleryEntries, setBigFishGalleryEntries] = useState<
|
||||
BigFishWorkSummary[]
|
||||
>([]);
|
||||
const [match3dWorks, setMatch3DWorks] = useState<Match3DWorkSummary[]>([]);
|
||||
const [match3dGalleryEntries, setMatch3DGalleryEntries] = useState<
|
||||
Match3DWorkSummary[]
|
||||
>([]);
|
||||
const [match3dProfile, setMatch3DProfile] =
|
||||
useState<Match3DWorkProfile | null>(null);
|
||||
const [match3dRun, setMatch3DRun] = useState<Match3DRunSnapshot | null>(null);
|
||||
const [match3dRuntimeReturnStage, setMatch3DRuntimeReturnStage] =
|
||||
useState<'match3d-result' | 'work-detail'>('match3d-result');
|
||||
const [isMatch3DLoadingLibrary, setIsMatch3DLoadingLibrary] = useState(false);
|
||||
const [bigFishRun, setBigFishRun] =
|
||||
useState<BigFishRuntimeSnapshotResponse | null>(null);
|
||||
const [bigFishRuntimeShare, setBigFishRuntimeShare] = useState<{
|
||||
@@ -955,6 +1060,34 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
}, [resolveBigFishErrorMessage]);
|
||||
|
||||
const refreshMatch3DShelf = useCallback(async () => {
|
||||
setIsMatch3DLoadingLibrary(true);
|
||||
|
||||
try {
|
||||
const worksResponse = await listMatch3DWorks();
|
||||
setMatch3DWorks(worksResponse.items);
|
||||
setMatch3DError(null);
|
||||
} catch (error) {
|
||||
setMatch3DError(
|
||||
resolveMatch3DErrorMessage(error, '读取抓大鹅作品列表失败。'),
|
||||
);
|
||||
} finally {
|
||||
setIsMatch3DLoadingLibrary(false);
|
||||
}
|
||||
}, [resolveMatch3DErrorMessage]);
|
||||
|
||||
const refreshMatch3DGallery = useCallback(async () => {
|
||||
try {
|
||||
const galleryResponse = await listMatch3DGallery();
|
||||
setMatch3DGalleryEntries(galleryResponse.items);
|
||||
return galleryResponse.items;
|
||||
} catch (error) {
|
||||
setMatch3DGalleryEntries([]);
|
||||
setMatch3DError(resolveMatch3DErrorMessage(error, '读取抓大鹅广场失败。'));
|
||||
return [];
|
||||
}
|
||||
}, [resolveMatch3DErrorMessage]);
|
||||
|
||||
const refreshPuzzleShelf = useCallback(async () => {
|
||||
setIsPuzzleLoadingLibrary(true);
|
||||
|
||||
@@ -1136,16 +1269,20 @@ export function PlatformEntryFlowShellImpl({
|
||||
const bigFishPublicEntries = isBigFishCreationVisible
|
||||
? bigFishGalleryEntries.map(mapBigFishWorkToPlatformGalleryCard)
|
||||
: [];
|
||||
const match3dPublicEntries = match3dGalleryEntries.map(
|
||||
mapMatch3DWorkToPlatformGalleryCard,
|
||||
);
|
||||
const puzzlePublicEntries = puzzleGalleryEntries.map(
|
||||
mapPuzzleWorkToPlatformGalleryCard,
|
||||
);
|
||||
return mergePlatformPublicGalleryEntries(
|
||||
platformBootstrap.publishedGalleryEntries,
|
||||
[...bigFishPublicEntries, ...puzzlePublicEntries],
|
||||
[...bigFishPublicEntries, ...match3dPublicEntries, ...puzzlePublicEntries],
|
||||
).slice(0, 6);
|
||||
}, [
|
||||
isBigFishCreationVisible,
|
||||
bigFishGalleryEntries,
|
||||
match3dGalleryEntries,
|
||||
platformBootstrap.publishedGalleryEntries,
|
||||
puzzleGalleryEntries,
|
||||
]);
|
||||
@@ -1157,12 +1294,14 @@ export function PlatformEntryFlowShellImpl({
|
||||
...(isBigFishCreationVisible
|
||||
? bigFishGalleryEntries.map(mapBigFishWorkToPlatformGalleryCard)
|
||||
: []),
|
||||
...match3dGalleryEntries.map(mapMatch3DWorkToPlatformGalleryCard),
|
||||
...puzzleGalleryEntries.map(mapPuzzleWorkToPlatformGalleryCard),
|
||||
],
|
||||
),
|
||||
[
|
||||
isBigFishCreationVisible,
|
||||
bigFishGalleryEntries,
|
||||
match3dGalleryEntries,
|
||||
platformBootstrap.publishedGalleryEntries,
|
||||
puzzleGalleryEntries,
|
||||
],
|
||||
@@ -1336,8 +1475,25 @@ export function PlatformEntryFlowShellImpl({
|
||||
onSessionOpened: () => {
|
||||
setShowCreationTypeModal(false);
|
||||
},
|
||||
onActionComplete: ({ response, setSession }) => {
|
||||
onActionComplete: async ({ payload, response, setSession }) => {
|
||||
setSession(response.session);
|
||||
if (payload.action !== 'match3d_compile_draft') {
|
||||
return;
|
||||
}
|
||||
|
||||
const profileId = response.session.draft?.profileId;
|
||||
if (!profileId) {
|
||||
setMatch3DProfile(null);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { item } = await getMatch3DWorkDetail(profileId);
|
||||
setMatch3DProfile(item);
|
||||
await refreshMatch3DShelf().catch(() => undefined);
|
||||
} catch {
|
||||
setMatch3DProfile(buildMatch3DProfileFromSession(response.session));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1495,6 +1651,8 @@ export function PlatformEntryFlowShellImpl({
|
||||
|
||||
const openMatch3DAgentWorkspace = useCallback(async () => {
|
||||
setMatch3DSession(null);
|
||||
setMatch3DProfile(null);
|
||||
setMatch3DRun(null);
|
||||
setMatch3DError(null);
|
||||
setStreamingMatch3DReplyText('');
|
||||
setIsStreamingMatch3DReply(false);
|
||||
@@ -1503,6 +1661,8 @@ export function PlatformEntryFlowShellImpl({
|
||||
match3dFlow,
|
||||
setIsStreamingMatch3DReply,
|
||||
setMatch3DError,
|
||||
setMatch3DProfile,
|
||||
setMatch3DRun,
|
||||
setMatch3DSession,
|
||||
setStreamingMatch3DReplyText,
|
||||
]);
|
||||
@@ -1595,6 +1755,11 @@ export function PlatformEntryFlowShellImpl({
|
||||
setBigFishGenerationState(null);
|
||||
setBigFishError(null);
|
||||
setMatch3DSession(null);
|
||||
setMatch3DProfile(null);
|
||||
setMatch3DWorks([]);
|
||||
setMatch3DGalleryEntries([]);
|
||||
setMatch3DRun(null);
|
||||
setMatch3DRuntimeReturnStage('match3d-result');
|
||||
setMatch3DError(null);
|
||||
setStreamingMatch3DReplyText('');
|
||||
setIsStreamingMatch3DReply(false);
|
||||
@@ -1689,6 +1854,8 @@ export function PlatformEntryFlowShellImpl({
|
||||
}, [bigFishFlow]);
|
||||
|
||||
const leaveMatch3DFlow = useCallback(() => {
|
||||
setMatch3DRun(null);
|
||||
setMatch3DRuntimeReturnStage('match3d-result');
|
||||
match3dFlow.leaveFlow();
|
||||
}, [match3dFlow]);
|
||||
|
||||
@@ -1758,7 +1925,10 @@ export function PlatformEntryFlowShellImpl({
|
||||
match3dSession ? 'match3d-agent-workspace' : 'platform',
|
||||
);
|
||||
}
|
||||
}, [match3dSession, selectionStage, setSelectionStage]);
|
||||
if (selectionStage === 'match3d-runtime' && !match3dRun) {
|
||||
setSelectionStage(match3dSession?.draft ? 'match3d-result' : 'platform');
|
||||
}
|
||||
}, [match3dRun, match3dSession, selectionStage, setSelectionStage]);
|
||||
|
||||
const startBigFishRun = useCallback(() => {
|
||||
if (!bigFishSession) {
|
||||
@@ -1875,6 +2045,54 @@ export function PlatformEntryFlowShellImpl({
|
||||
],
|
||||
);
|
||||
|
||||
const startMatch3DRunFromProfile = useCallback(
|
||||
async (
|
||||
profile: Match3DWorkProfile | Match3DWorkSummary,
|
||||
returnStage: 'match3d-result' | 'work-detail' = 'match3d-result',
|
||||
mirrorErrorToPublicDetail = false,
|
||||
) => {
|
||||
if (isMatch3DBusy) {
|
||||
return;
|
||||
}
|
||||
|
||||
match3dFlow.setIsBusy(true);
|
||||
setMatch3DError(null);
|
||||
|
||||
try {
|
||||
const { run } = await startMatch3DRun(profile.profileId);
|
||||
setMatch3DRun(run);
|
||||
setMatch3DRuntimeReturnStage(returnStage);
|
||||
setSelectionStage('match3d-runtime');
|
||||
if (profile.publicationStatus === 'published') {
|
||||
pushAppHistoryPath(
|
||||
buildPublicWorkStagePath(
|
||||
'work-detail',
|
||||
buildMatch3DPublicWorkCode(profile.profileId),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
const message = resolveMatch3DErrorMessage(
|
||||
error,
|
||||
'启动抓大鹅玩法失败。',
|
||||
);
|
||||
setMatch3DError(message);
|
||||
if (mirrorErrorToPublicDetail) {
|
||||
setPublicWorkDetailError(message);
|
||||
}
|
||||
} finally {
|
||||
match3dFlow.setIsBusy(false);
|
||||
}
|
||||
},
|
||||
[
|
||||
isMatch3DBusy,
|
||||
match3dFlow,
|
||||
resolveMatch3DErrorMessage,
|
||||
setMatch3DError,
|
||||
setSelectionStage,
|
||||
],
|
||||
);
|
||||
|
||||
const buildPuzzleTestWork = useCallback(
|
||||
(draft: PuzzleResultDraft) => {
|
||||
const profileId =
|
||||
@@ -2595,6 +2813,47 @@ export function PlatformEntryFlowShellImpl({
|
||||
],
|
||||
);
|
||||
|
||||
const handleDeleteMatch3DWork = useCallback(
|
||||
(work: Match3DWorkSummary) => {
|
||||
if (deletingCreationWorkId) {
|
||||
return;
|
||||
}
|
||||
|
||||
runProtectedAction(() => {
|
||||
const confirmed = window.confirm(
|
||||
`确认删除作品《${work.gameName}》吗?删除后会从你的作品列表中移除。`,
|
||||
);
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
setDeletingCreationWorkId(work.workId);
|
||||
setMatch3DError(null);
|
||||
|
||||
void deleteMatch3DWork(work.profileId)
|
||||
.then((response) => {
|
||||
setMatch3DWorks(response.items);
|
||||
void refreshMatch3DGallery();
|
||||
})
|
||||
.catch((error) => {
|
||||
setMatch3DError(
|
||||
resolveMatch3DErrorMessage(error, '删除抓大鹅作品失败。'),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setDeletingCreationWorkId(null);
|
||||
});
|
||||
});
|
||||
},
|
||||
[
|
||||
deletingCreationWorkId,
|
||||
refreshMatch3DGallery,
|
||||
resolveMatch3DErrorMessage,
|
||||
runProtectedAction,
|
||||
setMatch3DError,
|
||||
],
|
||||
);
|
||||
|
||||
const clearSelectedPublicWorkAuthor = useCallback(() => {
|
||||
publicWorkAuthorRequestKeyRef.current += 1;
|
||||
setSelectedPublicWorkAuthor(null);
|
||||
@@ -2911,6 +3170,45 @@ export function PlatformEntryFlowShellImpl({
|
||||
],
|
||||
);
|
||||
|
||||
const openMatch3DPublicWorkDetail = useCallback(
|
||||
async (profileId: string) => {
|
||||
setIsPublicWorkDetailBusy(true);
|
||||
setMatch3DError(null);
|
||||
setPublicWorkDetailError(null);
|
||||
setSelectionStage('work-detail');
|
||||
|
||||
try {
|
||||
const entries =
|
||||
match3dGalleryEntries.length > 0
|
||||
? match3dGalleryEntries
|
||||
: await refreshMatch3DGallery();
|
||||
const matchedEntry = entries.find(
|
||||
(entry) => entry.profileId === profileId,
|
||||
);
|
||||
|
||||
if (!matchedEntry) {
|
||||
throw new Error('未找到抓大鹅作品。');
|
||||
}
|
||||
|
||||
openPublicWorkDetail(mapMatch3DWorkToPublicWorkDetail(matchedEntry));
|
||||
} catch (error) {
|
||||
setPublicWorkDetailError(
|
||||
resolveMatch3DErrorMessage(error, '读取抓大鹅详情失败。'),
|
||||
);
|
||||
} finally {
|
||||
setIsPublicWorkDetailBusy(false);
|
||||
}
|
||||
},
|
||||
[
|
||||
match3dGalleryEntries,
|
||||
openPublicWorkDetail,
|
||||
refreshMatch3DGallery,
|
||||
resolveMatch3DErrorMessage,
|
||||
setMatch3DError,
|
||||
setSelectionStage,
|
||||
],
|
||||
);
|
||||
|
||||
const openPuzzleDetail = useCallback(
|
||||
async (
|
||||
profileId: string,
|
||||
@@ -2986,6 +3284,47 @@ export function PlatformEntryFlowShellImpl({
|
||||
],
|
||||
);
|
||||
|
||||
const openMatch3DDraft = useCallback(
|
||||
async (item: Match3DWorkSummary) => {
|
||||
setMatch3DRun(null);
|
||||
setMatch3DError(null);
|
||||
setMatch3DProfile(null);
|
||||
|
||||
if (item.publicationStatus === 'published') {
|
||||
openPublicWorkDetail(mapMatch3DWorkToPublicWorkDetail(item));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item.sourceSessionId?.trim()) {
|
||||
setMatch3DError('这份抓大鹅草稿缺少会话信息,请重新开始创作。');
|
||||
return;
|
||||
}
|
||||
|
||||
const restoredSession = await match3dFlow.restoreDraft(item.sourceSessionId);
|
||||
if (!restoredSession) {
|
||||
await refreshMatch3DShelf().catch(() => undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { item: profile } = await getMatch3DWorkDetail(item.profileId);
|
||||
setMatch3DProfile(profile);
|
||||
} catch (error) {
|
||||
setMatch3DProfile(buildMatch3DProfileFromSession(restoredSession));
|
||||
setMatch3DError(
|
||||
resolveMatch3DErrorMessage(error, '读取抓大鹅作品详情失败。'),
|
||||
);
|
||||
}
|
||||
},
|
||||
[
|
||||
match3dFlow,
|
||||
openPublicWorkDetail,
|
||||
refreshMatch3DShelf,
|
||||
resolveMatch3DErrorMessage,
|
||||
setMatch3DError,
|
||||
],
|
||||
);
|
||||
|
||||
const startBigFishRunFromWork = useCallback(
|
||||
(
|
||||
item: BigFishWorkSummary,
|
||||
@@ -3053,6 +3392,17 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMatch3DGalleryEntry(selectedPublicWorkDetail)) {
|
||||
const work = mapPublicWorkDetailToMatch3DWork(selectedPublicWorkDetail);
|
||||
if (!work) {
|
||||
setPublicWorkDetailError('当前抓大鹅作品信息不完整,暂时无法进入玩法。');
|
||||
return;
|
||||
}
|
||||
setPublicWorkDetailError(null);
|
||||
void startMatch3DRunFromProfile(work, 'work-detail', true);
|
||||
return;
|
||||
}
|
||||
|
||||
const launchEntry =
|
||||
selectedDetailEntry?.profileId === selectedPublicWorkDetail.profileId
|
||||
? selectedDetailEntry
|
||||
@@ -3090,6 +3440,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
selectedDetailEntry,
|
||||
selectedPublicWorkDetail,
|
||||
startBigFishRunFromWork,
|
||||
startMatch3DRunFromProfile,
|
||||
startPuzzleRunFromProfile,
|
||||
]);
|
||||
|
||||
@@ -3140,6 +3491,12 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMatch3DGalleryEntry(entry)) {
|
||||
setPublicWorkDetailError('抓大鹅作品改造将在后续版本开放。');
|
||||
setIsPublicWorkDetailBusy(false);
|
||||
return;
|
||||
}
|
||||
|
||||
void remixRpgEntryWorldGallery(entry.ownerUserId, entry.profileId)
|
||||
.then((response) => {
|
||||
const nextEntry = response.entry;
|
||||
@@ -3199,10 +3556,12 @@ export function PlatformEntryFlowShellImpl({
|
||||
normalizedKeyword,
|
||||
);
|
||||
const shouldSearchBigFishFirst = upperKeyword.startsWith('BF');
|
||||
const shouldSearchMatch3DFirst = upperKeyword.startsWith('M3');
|
||||
const shouldSearchPuzzleFirst = upperKeyword.startsWith('PZ');
|
||||
const shouldSearchWorkFirst =
|
||||
!shouldSearchUserIdFirst &&
|
||||
!shouldSearchBigFishFirst &&
|
||||
!shouldSearchMatch3DFirst &&
|
||||
!shouldSearchPuzzleFirst &&
|
||||
(upperKeyword.startsWith('CW') || /^\d{1,8}$/u.test(normalizedKeyword));
|
||||
const shouldSearchUserFirst =
|
||||
@@ -3210,6 +3569,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
upperKeyword.startsWith('SY') ||
|
||||
(!shouldSearchWorkFirst &&
|
||||
!shouldSearchBigFishFirst &&
|
||||
!shouldSearchMatch3DFirst &&
|
||||
!shouldSearchPuzzleFirst);
|
||||
|
||||
const tryOpenGalleryEntry = async () => {
|
||||
@@ -3270,6 +3630,21 @@ export function PlatformEntryFlowShellImpl({
|
||||
|
||||
openPublicWorkDetail(mapBigFishWorkToPublicWorkDetail(matchedEntry));
|
||||
};
|
||||
const tryOpenMatch3DGalleryEntry = async () => {
|
||||
const entries =
|
||||
match3dGalleryEntries.length > 0
|
||||
? match3dGalleryEntries
|
||||
: await refreshMatch3DGallery();
|
||||
const matchedEntry = entries.find((entry) =>
|
||||
isSameMatch3DPublicWorkCode(normalizedKeyword, entry.profileId),
|
||||
);
|
||||
|
||||
if (!matchedEntry) {
|
||||
throw new Error('未找到抓大鹅作品。');
|
||||
}
|
||||
|
||||
openPublicWorkDetail(mapMatch3DWorkToPublicWorkDetail(matchedEntry));
|
||||
};
|
||||
|
||||
try {
|
||||
if (shouldSearchUserIdFirst) {
|
||||
@@ -3288,6 +3663,11 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldSearchMatch3DFirst) {
|
||||
await tryOpenMatch3DGalleryEntry();
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldSearchWorkFirst) {
|
||||
try {
|
||||
await tryOpenGalleryEntry();
|
||||
@@ -3324,6 +3704,8 @@ export function PlatformEntryFlowShellImpl({
|
||||
},
|
||||
[
|
||||
bigFishGalleryEntries,
|
||||
match3dGalleryEntries,
|
||||
refreshMatch3DGallery,
|
||||
openPuzzlePublicWorkDetail,
|
||||
openPublicWorkDetail,
|
||||
platformBootstrap.platformTab,
|
||||
@@ -3365,6 +3747,19 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
worldType === 'match3d' ||
|
||||
worldType === 'match_3d' ||
|
||||
work.worldKey.startsWith('match3d:')
|
||||
) {
|
||||
const profileId =
|
||||
work.profileId ?? work.worldKey.replace(/^match3d:/u, '');
|
||||
if (profileId) {
|
||||
void openMatch3DPublicWorkDetail(profileId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
worldType === 'big_fish' ||
|
||||
worldType === 'big-fish' ||
|
||||
@@ -3442,6 +3837,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
});
|
||||
},
|
||||
[
|
||||
openMatch3DPublicWorkDetail,
|
||||
openPuzzlePublicWorkDetail,
|
||||
openPublicWorkDetail,
|
||||
openRpgPublicWorkDetail,
|
||||
@@ -3481,11 +3877,13 @@ export function PlatformEntryFlowShellImpl({
|
||||
if (isBigFishCreationVisible) {
|
||||
void refreshBigFishGallery();
|
||||
}
|
||||
void refreshMatch3DGallery();
|
||||
void refreshPuzzleGallery();
|
||||
}
|
||||
}, [
|
||||
isBigFishCreationVisible,
|
||||
refreshBigFishGallery,
|
||||
refreshMatch3DGallery,
|
||||
refreshPuzzleGallery,
|
||||
selectionStage,
|
||||
]);
|
||||
@@ -3497,10 +3895,12 @@ export function PlatformEntryFlowShellImpl({
|
||||
platformBootstrap.canReadProtectedData
|
||||
) {
|
||||
void refreshPuzzleShelf();
|
||||
void refreshMatch3DShelf();
|
||||
}
|
||||
}, [
|
||||
platformBootstrap.canReadProtectedData,
|
||||
platformBootstrap.platformTab,
|
||||
refreshMatch3DShelf,
|
||||
refreshPuzzleShelf,
|
||||
selectionStage,
|
||||
]);
|
||||
@@ -3529,11 +3929,13 @@ export function PlatformEntryFlowShellImpl({
|
||||
loading={
|
||||
platformBootstrap.isLoadingPlatform ||
|
||||
isBigFishLoadingLibrary ||
|
||||
isMatch3DLoadingLibrary ||
|
||||
isPuzzleLoadingLibrary
|
||||
}
|
||||
error={
|
||||
platformBootstrap.isLoadingPlatform ||
|
||||
isBigFishLoadingLibrary ||
|
||||
isMatch3DLoadingLibrary ||
|
||||
isPuzzleLoadingLibrary
|
||||
? null
|
||||
: (platformBootstrap.platformError ??
|
||||
@@ -3555,6 +3957,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
if (isBigFishCreationVisible) {
|
||||
void refreshBigFishShelf();
|
||||
}
|
||||
void refreshMatch3DShelf();
|
||||
void refreshPuzzleShelf();
|
||||
}}
|
||||
createError={
|
||||
@@ -3608,6 +4011,15 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
: null
|
||||
}
|
||||
match3dItems={match3dWorks}
|
||||
onOpenMatch3DDetail={(item) => {
|
||||
runProtectedAction(() => {
|
||||
void openMatch3DDraft(item);
|
||||
});
|
||||
}}
|
||||
onDeleteMatch3D={(item) => {
|
||||
handleDeleteMatch3DWork(item);
|
||||
}}
|
||||
puzzleItems={puzzleWorks}
|
||||
onOpenPuzzleDetail={(item) => {
|
||||
runProtectedAction(() => {
|
||||
@@ -3689,6 +4101,11 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMatch3DGalleryEntry(entry)) {
|
||||
openPublicWorkDetail(entry);
|
||||
return;
|
||||
}
|
||||
|
||||
void openRpgPublicWorkDetail(entry);
|
||||
}}
|
||||
onOpenLibraryDetail={(entry) => {
|
||||
@@ -4081,13 +4498,106 @@ export function PlatformEntryFlowShellImpl({
|
||||
<Suspense
|
||||
fallback={<LazyPanelFallback label="正在加载抓大鹅结果..." />}
|
||||
>
|
||||
<Match3DDraftReadyView
|
||||
session={match3dSession}
|
||||
<Match3DResultView
|
||||
profile={
|
||||
match3dProfile ?? buildMatch3DProfileFromSession(match3dSession)!
|
||||
}
|
||||
draft={match3dSession.draft}
|
||||
isBusy={isMatch3DBusy}
|
||||
error={match3dError}
|
||||
onBack={() => {
|
||||
setSelectionStage('match3d-agent-workspace');
|
||||
}}
|
||||
onSaved={(profile) => {
|
||||
setMatch3DProfile(profile);
|
||||
}}
|
||||
onPublished={(profile) => {
|
||||
setMatch3DProfile(profile);
|
||||
void Promise.allSettled([
|
||||
refreshMatch3DShelf(),
|
||||
refreshMatch3DGallery(),
|
||||
]);
|
||||
openPublicWorkDetail(mapMatch3DWorkToPublicWorkDetail(profile));
|
||||
}}
|
||||
onStartTestRun={(profile) => {
|
||||
setMatch3DProfile(profile);
|
||||
void startMatch3DRunFromProfile(profile, 'match3d-result');
|
||||
}}
|
||||
/>
|
||||
</Suspense>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{selectionStage === 'match3d-runtime' && (
|
||||
<motion.div
|
||||
key="match3d-runtime"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 z-[100]"
|
||||
>
|
||||
<Suspense
|
||||
fallback={<LazyPanelFallback label="正在加载抓大鹅玩法..." />}
|
||||
>
|
||||
<Match3DRuntimeShell
|
||||
run={match3dRun}
|
||||
isBusy={isMatch3DBusy}
|
||||
error={match3dError}
|
||||
onBack={() => {
|
||||
if (match3dRun?.runId && match3dRun.status === 'running') {
|
||||
void stopMatch3DRun(match3dRun.runId).catch(() => undefined);
|
||||
}
|
||||
setSelectionStage(match3dRuntimeReturnStage);
|
||||
}}
|
||||
onRestart={() => {
|
||||
if (!match3dRun?.runId || isMatch3DBusy) {
|
||||
return;
|
||||
}
|
||||
|
||||
match3dFlow.setIsBusy(true);
|
||||
setMatch3DError(null);
|
||||
void restartMatch3DRun(match3dRun.runId)
|
||||
.then(({ run }) => {
|
||||
setMatch3DRun(run);
|
||||
})
|
||||
.catch((error) => {
|
||||
setMatch3DError(
|
||||
resolveMatch3DErrorMessage(
|
||||
error,
|
||||
'重新开始抓大鹅玩法失败。',
|
||||
),
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
match3dFlow.setIsBusy(false);
|
||||
});
|
||||
}}
|
||||
onOptimisticRunChange={setMatch3DRun}
|
||||
onClickItem={(payload) => {
|
||||
const runId = payload.runId ?? match3dRun?.runId;
|
||||
if (!runId) {
|
||||
return Promise.reject(new Error('抓大鹅运行态缺少 runId。'));
|
||||
}
|
||||
return clickMatch3DItem(runId, payload);
|
||||
}}
|
||||
onTimeExpired={() => {
|
||||
if (!match3dRun?.runId) {
|
||||
return;
|
||||
}
|
||||
|
||||
void finishMatch3DTimeUp(match3dRun.runId)
|
||||
.then(({ run }) => {
|
||||
setMatch3DRun(run);
|
||||
})
|
||||
.catch((error) => {
|
||||
setMatch3DError(
|
||||
resolveMatch3DErrorMessage(
|
||||
error,
|
||||
'同步抓大鹅倒计时失败。',
|
||||
),
|
||||
);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Suspense>
|
||||
</motion.div>
|
||||
|
||||
@@ -53,6 +53,9 @@ function getSourceLabel(entry: PlatformPublicGalleryCard) {
|
||||
if ('sourceType' in entry && entry.sourceType === 'big-fish') {
|
||||
return '大鱼吃小鱼';
|
||||
}
|
||||
if ('sourceType' in entry && entry.sourceType === 'match3d') {
|
||||
return '抓大鹅';
|
||||
}
|
||||
return 'RPG';
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ export type SelectionStage =
|
||||
| 'big-fish-runtime'
|
||||
| 'match3d-agent-workspace'
|
||||
| 'match3d-result'
|
||||
| 'match3d-runtime'
|
||||
| 'puzzle-agent-workspace'
|
||||
| 'puzzle-generating'
|
||||
| 'puzzle-result'
|
||||
|
||||
Reference in New Issue
Block a user