feat: 支持创作入口公告配置

This commit is contained in:
2026-06-03 03:31:45 +08:00
parent 1cb11bc1dd
commit 70ff18ad90
52 changed files with 3045 additions and 504 deletions

View File

@@ -36,9 +36,9 @@ const entryConfig = {
visible: true,
open: true,
sortOrder: 10,
categoryId: 'recent',
categoryLabel: '最近创作',
categorySortOrder: 10,
categoryId: 'recommended',
categoryLabel: '热门推荐',
categorySortOrder: 20,
updatedAtMicros: 1,
},
],

View File

@@ -353,6 +353,7 @@ import type { PublishShareModalPayload } from '../common/publishShareModalModel'
import { UnifiedModal } from '../common/UnifiedModal';
import { resolveCreativeAgentTargetSelectionStage } from '../creative-agent/creativeAgentViewModel';
import {
buildCreationWorkShelfItems,
type CreationWorkShelfItem,
isPersistedBarkBattleDraftGenerating,
isPersistedPuzzleDraftGenerating,
@@ -2162,35 +2163,24 @@ function buildDraftCompletionDialogSource(
return formatPlatformTaskCompletionSource('创作草稿', sourceId);
}
/** 为恢复的小游戏草稿重建生成态,保留后端开始时间作为进度事实源。 */
function createMiniGameDraftGenerationStateForRestoredDraft(
kind: MiniGameDraftGenerationKind,
metadata?: MiniGameDraftGenerationState['metadata'],
startedAtMs = Date.now(),
): MiniGameDraftGenerationState {
return {
...createMiniGameDraftGenerationState(kind),
...createMiniGameDraftGenerationState(kind, startedAtMs),
...(metadata ? { metadata } : {}),
};
}
/** 清理生成态完成时间,避免返回生成页后继续沿用结束态计时。 */
function rebaseMiniGameDraftGenerationStateForDisplay(
state: MiniGameDraftGenerationState,
): MiniGameDraftGenerationState {
const rebasedStartedAtMs = Date.now();
if (state.kind === 'puzzle') {
const puzzleAiRedraw = state.metadata?.puzzleAiRedraw;
return {
...state,
startedAtMs: rebasedStartedAtMs,
finishedAtMs: undefined,
metadata:
typeof puzzleAiRedraw === 'boolean' ? { puzzleAiRedraw } : undefined,
};
}
return {
...state,
startedAtMs: rebasedStartedAtMs,
finishedAtMs: undefined,
};
}
@@ -14920,6 +14910,19 @@ export function PlatformEntryFlowShellImpl({
]);
useEffect(() => {
if (
platformBootstrap.platformTab === 'create' &&
platformBootstrap.canReadProtectedData
) {
// 中文注释:底部加号创作入口的“最近创作”依赖 RPG works 摘要;
// 失败草稿也必须随进入创作页刷新,不能只等草稿页刷新后才可见。
void platformBootstrap.refreshCustomWorldWorks().catch((error) => {
platformBootstrap.setPlatformError(
resolveRpgCreationErrorMessage(error, '读取创作作品列表失败。'),
);
});
}
if (
(platformBootstrap.platformTab === 'create' ||
selectionStage === 'platform') &&
@@ -14942,6 +14945,8 @@ export function PlatformEntryFlowShellImpl({
isVisualNovelCreationOpen,
platformBootstrap.canReadProtectedData,
platformBootstrap.platformTab,
platformBootstrap.refreshCustomWorldWorks,
platformBootstrap.setPlatformError,
refreshBabyObjectMatchShelf,
refreshBarkBattleShelf,
refreshMatch3DShelf,
@@ -14969,6 +14974,46 @@ export function PlatformEntryFlowShellImpl({
selectionStage,
]);
// 中文注释:最近创作必须由真实作品架/后端草稿摘要决定,不能混入本地生成中占位。
const backendRecentCreationShelfItems = useMemo(
() =>
buildCreationWorkShelfItems({
rpgItems: creationHubItems,
rpgLibraryEntries: platformBootstrap.savedCustomWorldEntries,
bigFishItems: isBigFishCreationVisible ? bigFishWorks : [],
match3dItems: match3dWorks,
squareHoleItems: isSquareHoleCreationVisible ? squareHoleWorks : [],
jumpHopItems: isJumpHopCreationVisible ? jumpHopWorks : [],
woodenFishItems: woodenFishWorks,
puzzleItems: puzzleWorks,
babyObjectMatchItems: isBabyObjectMatchVisible
? babyObjectMatchDrafts
: [],
barkBattleItems: barkBattleWorks,
visualNovelItems: isVisualNovelCreationOpen ? visualNovelWorks : [],
getItemState: getCreationWorkShelfState,
}),
[
barkBattleWorks,
babyObjectMatchDrafts,
bigFishWorks,
creationHubItems,
getCreationWorkShelfState,
isBabyObjectMatchVisible,
isBigFishCreationVisible,
isJumpHopCreationVisible,
isSquareHoleCreationVisible,
isVisualNovelCreationOpen,
jumpHopWorks,
match3dWorks,
platformBootstrap.savedCustomWorldEntries,
puzzleWorks,
squareHoleWorks,
visualNovelWorks,
woodenFishWorks,
],
);
const renderCreationHubContent = (
mode: 'start-only' | 'works-only',
fallbackLabel: string,
@@ -15065,6 +15110,7 @@ export function PlatformEntryFlowShellImpl({
}
entryConfig={creationEntryConfig}
creationTypes={creationEntryTypes}
recentWorkItems={backendRecentCreationShelfItems}
onCreateType={handleCreationHubCreateType}
getWorkState={getCreationWorkShelfState}
onOpenShelfItem={(item) => {

View File

@@ -306,7 +306,7 @@ test('groups visible platform creation types by backend category metadata', () =
const groups = groupVisiblePlatformCreationTypes(cards);
expect(groups.map((group) => group.label)).toEqual([
'最近创作',
'热门推荐',
'节日主题',
]);
expect(groups[0]?.items.map((item) => item.id)).toEqual([
@@ -337,14 +337,14 @@ test('falls back when backend creation type category metadata is missing', () =>
expect(cards[0]).toEqual(
expect.objectContaining({
id: 'legacy-entry',
categoryId: 'recent',
categoryLabel: '最近创作',
categoryId: 'recommended',
categoryLabel: '热门推荐',
}),
);
expect(groupVisiblePlatformCreationTypes(cards)).toEqual([
expect.objectContaining({
id: 'recent',
label: '最近创作',
id: 'recommended',
label: '热门推荐',
}),
]);
});

View File

@@ -24,8 +24,9 @@ export type PlatformCreationTypeGroup = {
items: PlatformCreationTypeCard[];
};
const FALLBACK_CREATION_CATEGORY_ID = 'recent';
const FALLBACK_CREATION_CATEGORY_LABEL = '最近创作';
const RECENT_CREATION_CATEGORY_ID = 'recent';
const FALLBACK_CREATION_CATEGORY_ID = 'recommended';
const FALLBACK_CREATION_CATEGORY_LABEL = '热门推荐';
export function getVisiblePlatformCreationTypes(
creationTypes: readonly PlatformCreationTypeCard[],
@@ -55,16 +56,25 @@ export function isPlatformCreationTypeOpen(
);
}
/** 归一化模板分类 ID历史 recent 分类会并入推荐分类。 */
function normalizeCategoryId(value: string | null | undefined) {
const normalized = typeof value === 'string' ? value.trim() : '';
if (normalized === RECENT_CREATION_CATEGORY_ID) {
return FALLBACK_CREATION_CATEGORY_ID;
}
return normalized || FALLBACK_CREATION_CATEGORY_ID;
}
/** 归一化模板分类名,避免最近创作被误当作模板页签。 */
function normalizeCategoryLabel(value: string | null | undefined) {
const normalized = typeof value === 'string' ? value.trim() : '';
if (normalized === '最近创作') {
return FALLBACK_CREATION_CATEGORY_LABEL;
}
return normalized || FALLBACK_CREATION_CATEGORY_LABEL;
}
/** 按玩法模板分类分组,旧 recent 分类不再作为模板页签展示。 */
export function groupVisiblePlatformCreationTypes(
creationTypes: readonly PlatformCreationTypeCard[],
): PlatformCreationTypeGroup[] {