1
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-05-11 20:57:16 +08:00
81 changed files with 3410 additions and 132 deletions

View File

@@ -178,6 +178,70 @@ function hasMatch3DGeneratedModelSource(asset: Match3DGeneratedItemAsset) {
return Boolean(asset.modelSrc?.trim() || asset.modelObjectKey?.trim());
}
function hasPersistableMatch3DGeneratedItemAsset(
asset: Match3DGeneratedItemAsset,
) {
return Boolean(
asset.imageSrc?.trim() ||
asset.imageObjectKey?.trim() ||
asset.modelSrc?.trim() ||
asset.modelObjectKey?.trim() ||
asset.taskUuid?.trim() ||
asset.subscriptionKey?.trim() ||
asset.backgroundMusic ||
asset.clickSound,
);
}
function getMatch3DGeneratedItemAssetPersistenceSignature(
asset: Match3DGeneratedItemAsset,
) {
return [
asset.itemId.trim(),
asset.itemName.trim(),
asset.imageSrc?.trim() ?? '',
asset.imageObjectKey?.trim() ?? '',
asset.modelSrc?.trim() ?? '',
asset.modelObjectKey?.trim() ?? '',
asset.modelFileName?.trim() ?? '',
asset.taskUuid?.trim() ?? '',
asset.subscriptionKey?.trim() ?? '',
asset.status.trim(),
asset.backgroundMusic?.audioSrc?.trim() ??
asset.backgroundMusic?.assetObjectId?.trim() ??
asset.backgroundMusic?.taskId?.trim() ??
'',
asset.clickSound?.audioSrc?.trim() ??
asset.clickSound?.assetObjectId?.trim() ??
asset.clickSound?.taskId?.trim() ??
'',
asset.error?.trim() ?? '',
].join('\u001f');
}
function shouldPersistGeneratedItemAssets(
currentAssets: readonly Match3DGeneratedItemAsset[],
savedAssets: readonly Match3DGeneratedItemAsset[] = [],
) {
if (currentAssets.length <= 0) {
return false;
}
if (currentAssets.length !== savedAssets.length) {
return true;
}
return currentAssets.some(
(asset, index) => {
const savedAsset = savedAssets[index];
return (
!savedAsset ||
getMatch3DGeneratedItemAssetPersistenceSignature(asset) !==
getMatch3DGeneratedItemAssetPersistenceSignature(savedAsset)
);
},
);
}
function mergeMatch3DGeneratedItemAsset(
base: Match3DGeneratedItemAsset,
override: Match3DGeneratedItemAsset,
@@ -636,6 +700,19 @@ function attachMatch3DGeneratedItemAssets(
};
}
function buildPersistableGeneratedItemAssets(
assetDrafts: Match3DRodinAssetDraft[],
generatedItemAssets: readonly Match3DGeneratedItemAsset[],
) {
if (generatedItemAssets.length <= 0) {
return [];
}
return createGeneratedAssetsFromDrafts(assetDrafts, generatedItemAssets).filter(
hasPersistableMatch3DGeneratedItemAsset,
);
}
function Match3DResultHeader({
autoSaveState,
isBusy,
@@ -1459,8 +1536,12 @@ export function Match3DResultView({
if (cancelled) {
return;
}
const playableItem = attachMatch3DGeneratedItemAssets(
item,
generatedItemAssets,
);
setAutoSaveState('saved');
onSaved?.(item);
onSaved?.(playableItem);
})
.catch((saveError) => {
if (cancelled) {
@@ -1477,7 +1558,7 @@ export function Match3DResultView({
cancelled = true;
window.clearTimeout(timer);
};
}, [editState, onSaved, profile]);
}, [editState, generatedItemAssets, onSaved, profile]);
const saveNow = async () => {
const payload = buildSavePayload(editState);
@@ -1489,10 +1570,34 @@ export function Match3DResultView({
setAutoSaveState('saving');
setLocalError(null);
const { item } = await updateMatch3DWork(profile.profileId, payload);
const playableItem = attachMatch3DGeneratedItemAssets(
item,
const currentGeneratedItemAssets = buildPersistableGeneratedItemAssets(
assetDrafts,
generatedItemAssets,
);
let playableItem = attachMatch3DGeneratedItemAssets(
item,
currentGeneratedItemAssets.length > 0
? currentGeneratedItemAssets
: generatedItemAssets,
);
if (
shouldPersistGeneratedItemAssets(
currentGeneratedItemAssets,
item.generatedItemAssets ?? [],
)
) {
// 中文注释:历史草稿可能只在页面 draft 中带 3D 模型;试玩和发布前必须先把当前可见素材写回 profile。
const { item: persistedItem } = await updateMatch3DGeneratedItemAssets(
profile.profileId,
{
generatedItemAssets: currentGeneratedItemAssets,
},
);
playableItem = attachMatch3DGeneratedItemAssets(
persistedItem,
currentGeneratedItemAssets,
);
}
setAutoSaveState('saved');
onSaved?.(playableItem);
return playableItem;
@@ -1829,7 +1934,12 @@ export function Match3DResultView({
const { item } = await publishMatch3DWork(
savedProfile?.profileId ?? profile.profileId,
);
onPublished?.(item);
onPublished?.(
attachMatch3DGeneratedItemAssets(
item,
savedProfile?.generatedItemAssets ?? generatedItemAssets,
),
);
setLocalError(null);
} catch (caughtError) {
setLocalError(