Merge branch 'master' into codex/puzzle-clear-template-runtime-fixes
# Conflicts: # .hermes/shared-memory/decision-log.md # .hermes/shared-memory/project-overview.md # docs/【开发运维】本地开发验证与生产运维-2026-05-15.md # scripts/dev.test.ts # server-rs/crates/api-server/src/creation_entry_config.rs # server-rs/crates/api-server/src/wooden_fish.rs # server-rs/crates/module-auth/src/lib.rs # server-rs/crates/spacetime-client/src/wooden_fish.rs # server-rs/crates/spacetime-module/src/auth/procedures.rs # src/components/custom-world-home/creationWorkShelf.ts # src/components/platform-entry/PlatformEntryFlowShellImpl.tsx # src/components/rpg-entry/rpgEntryWorldPresentation.ts # src/services/miniGameDraftGenerationProgress.test.ts # src/services/miniGameDraftGenerationProgress.ts
This commit is contained in:
@@ -36,9 +36,9 @@ const entryConfig = {
|
||||
visible: true,
|
||||
open: true,
|
||||
sortOrder: 10,
|
||||
categoryId: 'recent',
|
||||
categoryLabel: '最近创作',
|
||||
categorySortOrder: 10,
|
||||
categoryId: 'recommended',
|
||||
categoryLabel: '热门推荐',
|
||||
categorySortOrder: 20,
|
||||
updatedAtMicros: 1,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ArrowRight } from 'lucide-react';
|
||||
import { ArrowRight, LockKeyhole } from 'lucide-react';
|
||||
|
||||
import type { CreationEntryConfig } from '../../services/creationEntryConfigService';
|
||||
import { UnifiedModal } from '../common/UnifiedModal';
|
||||
@@ -33,6 +33,7 @@ function CreationTypeCard(props: {
|
||||
}) {
|
||||
const { item, busy, onSelect } = props;
|
||||
const disabled = item.locked || busy;
|
||||
const lockedBadge = item.badge.trim() || '暂未开放';
|
||||
|
||||
return (
|
||||
<button
|
||||
@@ -60,12 +61,15 @@ function CreationTypeCard(props: {
|
||||
/>
|
||||
<div className="relative z-10 flex min-h-6 items-start justify-end gap-3 px-4 pt-4">
|
||||
{item.locked ? (
|
||||
<span className="platform-pill platform-pill--neutral px-3 text-[var(--platform-text-soft)]">
|
||||
{item.badge}
|
||||
<span className="platform-pill platform-pill--neutral gap-1 px-3 text-[var(--platform-text-soft)]">
|
||||
<LockKeyhole className="h-3.5 w-3.5" />
|
||||
{lockedBadge}
|
||||
</span>
|
||||
) : null}
|
||||
{item.locked ? (
|
||||
<span className="text-lg leading-none text-white/62">·</span>
|
||||
<span className="inline-flex h-7 w-7 items-center justify-center rounded-full bg-white/18 text-white/72">
|
||||
<LockKeyhole className="h-3.5 w-3.5" />
|
||||
</span>
|
||||
) : (
|
||||
<ArrowRight className="h-4 w-4 text-white/80" />
|
||||
)}
|
||||
@@ -169,7 +173,6 @@ export function PlatformEntryCreationTypeModal({
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</UnifiedModal>
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -58,6 +58,22 @@ describe('PlatformErrorDialog', () => {
|
||||
|
||||
expect(screen.queryByRole('dialog', { name: '发生错误' })).toBeNull();
|
||||
});
|
||||
|
||||
test('does not render creation entry disabled errors', () => {
|
||||
render(
|
||||
<PlatformErrorDialog
|
||||
error={{
|
||||
source: '大鱼草稿',
|
||||
message:
|
||||
'creation_entry_disabled(requestId: req-big-fish-gallery)',
|
||||
}}
|
||||
onClose={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByRole('dialog', { name: '发生错误' })).toBeNull();
|
||||
expect(screen.queryByText(/creation_entry_disabled/u)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('PlatformTaskCompletionDialog', () => {
|
||||
|
||||
@@ -20,6 +20,11 @@ function buildPlatformErrorReport(error: PlatformErrorDialogPayload) {
|
||||
return [`来源:${error.source}`, `错误:${error.message}`].join('\n');
|
||||
}
|
||||
|
||||
function isBlacklistedPlatformError(error: PlatformErrorDialogPayload | null) {
|
||||
// 中文注释:入口关闭是平台开关状态,不作为全局错误弹窗打扰用户。
|
||||
return Boolean(error?.message.includes('creation_entry_disabled'));
|
||||
}
|
||||
|
||||
export function PlatformErrorDialog({
|
||||
error,
|
||||
onClose,
|
||||
@@ -30,9 +35,10 @@ export function PlatformErrorDialog({
|
||||
'idle',
|
||||
);
|
||||
const resetTimerRef = useRef<number | null>(null);
|
||||
const dialogError = isBlacklistedPlatformError(error) ? null : error;
|
||||
const reportText = useMemo(
|
||||
() => (error ? buildPlatformErrorReport(error) : ''),
|
||||
[error],
|
||||
() => (dialogError ? buildPlatformErrorReport(dialogError) : ''),
|
||||
[dialogError],
|
||||
);
|
||||
|
||||
useEffect(
|
||||
@@ -46,7 +52,7 @@ export function PlatformErrorDialog({
|
||||
|
||||
useEffect(() => {
|
||||
setCopyState('idle');
|
||||
}, [error?.source, error?.message]);
|
||||
}, [dialogError?.source, dialogError?.message]);
|
||||
|
||||
const copyError = () => {
|
||||
if (!reportText) {
|
||||
@@ -67,7 +73,7 @@ export function PlatformErrorDialog({
|
||||
|
||||
return (
|
||||
<UnifiedModal
|
||||
open={Boolean(error)}
|
||||
open={Boolean(dialogError)}
|
||||
title="发生错误"
|
||||
onClose={onClose}
|
||||
size="sm"
|
||||
@@ -95,14 +101,14 @@ export function PlatformErrorDialog({
|
||||
</button>
|
||||
}
|
||||
>
|
||||
{error ? (
|
||||
{dialogError ? (
|
||||
<>
|
||||
<div className="rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-white/72 px-3 py-2">
|
||||
<div className="text-xs font-bold text-[var(--platform-text-soft)]">
|
||||
来源
|
||||
</div>
|
||||
<div className="mt-1 break-words text-sm font-semibold leading-5 text-[var(--platform-text-strong)]">
|
||||
{error.source}
|
||||
{dialogError.source}
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-white/72 px-3 py-2">
|
||||
@@ -110,7 +116,7 @@ export function PlatformErrorDialog({
|
||||
错误
|
||||
</div>
|
||||
<div className="mt-1 whitespace-pre-wrap break-words text-sm leading-6 text-[var(--platform-text-strong)]">
|
||||
{error.message}
|
||||
{dialogError.message}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -163,7 +163,7 @@ test('PlatformWorkDetailView prefers resolved public user display name', () => {
|
||||
expect(screen.queryByText('137****6613')).toBeNull();
|
||||
});
|
||||
|
||||
test('PlatformWorkDetailView prefers display name then public user code for wooden fish works', () => {
|
||||
test('PlatformWorkDetailView prefers display name without appending public user code', () => {
|
||||
render(
|
||||
<PlatformWorkDetailView
|
||||
entry={createWoodenFishEntry()}
|
||||
@@ -183,10 +183,11 @@ test('PlatformWorkDetailView prefers display name then public user code for wood
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('公开昵称 · SY-00000004')).toBeTruthy();
|
||||
expect(screen.getByText('公开昵称')).toBeTruthy();
|
||||
expect(screen.queryByText('公开昵称 · SY-00000004')).toBeNull();
|
||||
expect(screen.queryByText('SY-00000004')).toBeNull();
|
||||
expect(screen.queryByText('phone_00000004')).toBeNull();
|
||||
expect(screen.queryByText('敲木鱼玩家')).toBeNull();
|
||||
expect(screen.queryByText('公开昵称')).toBeNull();
|
||||
});
|
||||
|
||||
test('PlatformWorkDetailView calls like handler', () => {
|
||||
|
||||
@@ -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: '热门推荐',
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -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[] {
|
||||
|
||||
Reference in New Issue
Block a user