Improve local auth env handling and fallbacks
Allow local env files to reliably override authentication feature flags (SMS/WeChat) by whitelisting keys in scripts/dev-utils.mjs and adding a unit test. Add SMS checks to scripts/check-api-server-env.mjs. Make server config.parse_bool tolerant of shell-wrapped quoted values (e.g. '"true"') and add tests so SMS_AUTH_ENABLED is parsed correctly when shells supply quotes. Update docs to clarify SMS env behaviour, restart requirements, and add guidance + a CSS fallback for old mobile browsers (QQ/X5) so public cover images render even when aspect-ratio is unsupported. Also include related frontend test and component adjustments and add puzzle onboarding handlers/endpoints in server-rs/crates/api-server/src/puzzle.rs.
This commit is contained in:
@@ -316,7 +316,7 @@ test('auth gate does not auto-create a guest account when dev guest switch is no
|
||||
expect(await screen.findByText('应用内容')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('auth gate keeps password entry available when login options are empty', async () => {
|
||||
test('auth gate keeps sms and password entries available when login options are empty', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
authMocks.getCurrentAuthUser.mockResolvedValue({
|
||||
@@ -336,12 +336,19 @@ test('auth gate keeps password entry available when login options are empty', as
|
||||
await user.click(await screen.findByRole('button', { name: '进入作品' }));
|
||||
|
||||
const dialog = screen.getByRole('dialog', { name: '账号入口' });
|
||||
expect(within(dialog).getByRole('tab', { name: '短信登录' })).toBeTruthy();
|
||||
expect(within(dialog).getByRole('tab', { name: '密码登录' })).toBeTruthy();
|
||||
expect(within(dialog).getByLabelText('验证码')).toBeTruthy();
|
||||
expect(
|
||||
within(dialog).getByRole('button', { name: '获取验证码' }),
|
||||
).toBeTruthy();
|
||||
await user.click(within(dialog).getByRole('tab', { name: '密码登录' }));
|
||||
expect(within(dialog).getByLabelText('密码')).toBeTruthy();
|
||||
expect(within(dialog).queryByText('当前登录入口暂不可用。')).toBeNull();
|
||||
expect(within(dialog).queryByText('读取登录方式失败')).toBeNull();
|
||||
});
|
||||
|
||||
test('auth gate falls back to password entry when login options request fails', async () => {
|
||||
test('auth gate keeps sms and password entries available when login options request fails', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
authMocks.getAuthLoginOptions.mockRejectedValue(
|
||||
@@ -357,6 +364,13 @@ test('auth gate falls back to password entry when login options request fails',
|
||||
await user.click(await screen.findByRole('button', { name: '进入作品' }));
|
||||
|
||||
const dialog = screen.getByRole('dialog', { name: '账号入口' });
|
||||
expect(within(dialog).getByRole('tab', { name: '短信登录' })).toBeTruthy();
|
||||
expect(within(dialog).getByRole('tab', { name: '密码登录' })).toBeTruthy();
|
||||
expect(within(dialog).getByLabelText('验证码')).toBeTruthy();
|
||||
expect(
|
||||
within(dialog).getByRole('button', { name: '获取验证码' }),
|
||||
).toBeTruthy();
|
||||
await user.click(within(dialog).getByRole('tab', { name: '密码登录' }));
|
||||
expect(within(dialog).getByLabelText('密码')).toBeTruthy();
|
||||
expect(within(dialog).queryByText('当前登录入口暂不可用。')).toBeNull();
|
||||
});
|
||||
|
||||
@@ -61,7 +61,7 @@ type AuthStatus =
|
||||
| 'ready'
|
||||
| 'error';
|
||||
|
||||
const FALLBACK_LOGIN_METHODS: AuthLoginMethod[] = ['password'];
|
||||
const REQUIRED_LOGIN_METHODS: AuthLoginMethod[] = ['phone', 'password'];
|
||||
|
||||
function readInviteCodeFromLocation(): string {
|
||||
const params = new URLSearchParams(window.location.search || '');
|
||||
@@ -76,11 +76,13 @@ function normalizeAvailableLoginMethods(
|
||||
): AuthLoginMethod[] {
|
||||
const normalizedMethods = Array.from(new Set(methods ?? []));
|
||||
|
||||
// 密码登录由 Rust auth entry 固定承载,不依赖短信或微信环境开关。
|
||||
// 当 login-options 联调失败或配置返回空数组时,仍要保留账号入口,避免登录弹窗失去可操作方式。
|
||||
return normalizedMethods.length > 0
|
||||
? normalizedMethods
|
||||
: FALLBACK_LOGIN_METHODS;
|
||||
// 登录面板的核心入口必须稳定展示,login-options 只补充微信等环境相关入口。
|
||||
return Array.from(
|
||||
new Set<AuthLoginMethod>([
|
||||
...REQUIRED_LOGIN_METHODS,
|
||||
...normalizedMethods,
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
type AuthHydrateSessionResult =
|
||||
@@ -367,9 +369,9 @@ export function AuthGate({ children }: AuthGateProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
setAvailableLoginMethods(FALLBACK_LOGIN_METHODS);
|
||||
setAvailableLoginMethods(REQUIRED_LOGIN_METHODS);
|
||||
setUser(null);
|
||||
// 中文注释:登录方式接口失败时按产品约定保留密码登录入口;
|
||||
// 中文注释:登录方式接口失败时按产品约定保留验证码和密码登录入口;
|
||||
// 这里不展示接口读取错误,避免用户误以为登录本身不可用。
|
||||
setError(callbackResult?.error ?? '');
|
||||
setStatus('unauthenticated');
|
||||
|
||||
@@ -80,8 +80,8 @@ export function LoginScreen({
|
||||
const [legalConsentChecked, setLegalConsentChecked] = useState(false);
|
||||
const [activeLegalDocumentId, setActiveLegalDocumentId] =
|
||||
useState<LegalDocumentId | null>(null);
|
||||
const passwordLoginEnabled = availableLoginMethods.includes('password');
|
||||
const phoneLoginEnabled = availableLoginMethods.includes('phone');
|
||||
const passwordLoginEnabled = true;
|
||||
const phoneLoginEnabled = true;
|
||||
const wechatLoginEnabled = availableLoginMethods.includes('wechat');
|
||||
const [activeLoginTab, setActiveLoginTab] = useState<LoginTab>('phone');
|
||||
|
||||
|
||||
@@ -1718,6 +1718,10 @@ function isMiniGameDraftGenerating(state: MiniGameDraftGenerationState | null) {
|
||||
return Boolean(state && state.phase !== 'ready' && state.phase !== 'failed');
|
||||
}
|
||||
|
||||
function isPersistedDraftGenerating(value: string | null | undefined) {
|
||||
return value?.trim() === 'generating';
|
||||
}
|
||||
|
||||
function resolveProfileWalletBalance(
|
||||
dashboard: { walletBalance?: number | null } | null | undefined,
|
||||
) {
|
||||
@@ -8750,7 +8754,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
item.sourceSessionId,
|
||||
buildPuzzleResultWorkId(item.sourceSessionId),
|
||||
buildPuzzleResultProfileId(item.sourceSessionId),
|
||||
]);
|
||||
]) || isPersistedDraftGenerating(item.generationStatus);
|
||||
setPuzzleOperation(null);
|
||||
setPuzzleRun(null);
|
||||
setPuzzleRuntimeAuthMode('default');
|
||||
@@ -8897,7 +8901,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
item.workId,
|
||||
item.profileId,
|
||||
item.sourceSessionId,
|
||||
]);
|
||||
]) || isPersistedDraftGenerating(item.generationStatus);
|
||||
|
||||
const backgroundTask = getMatch3DBackgroundCompileTask(
|
||||
item.sourceSessionId,
|
||||
@@ -8972,7 +8976,10 @@ export function PlatformEntryFlowShellImpl({
|
||||
setMatch3DFormDraftPayload(null);
|
||||
setMatch3DProfile(null);
|
||||
setMatch3DGenerationState(
|
||||
createMiniGameDraftGenerationState('match3d'),
|
||||
createMiniGameDraftGenerationStateFromStartedAt(
|
||||
'match3d',
|
||||
parseDraftGenerationStartedAtMs(item.updatedAt),
|
||||
),
|
||||
);
|
||||
enterCreateTab();
|
||||
selectionStageRef.current = 'match3d-generating';
|
||||
|
||||
@@ -3432,6 +3432,73 @@ test('running match3d persisted draft reopens progress instead of unfinished res
|
||||
);
|
||||
});
|
||||
|
||||
test('persisted generating match3d draft opens generation progress after refresh', async () => {
|
||||
const user = userEvent.setup();
|
||||
const persistedGeneratingWork: Match3DWorkSummary = {
|
||||
workId: 'match3d-work-generating',
|
||||
profileId: 'match3d-profile-generating',
|
||||
ownerUserId: 'user-1',
|
||||
sourceSessionId: 'match3d-session-generating',
|
||||
gameName: '生成中抓鹅',
|
||||
themeText: '霓虹水果摊',
|
||||
summary: '刷新后仍应回到抓大鹅生成面板。',
|
||||
tags: ['水果', '抓大鹅'],
|
||||
coverImageSrc: null,
|
||||
referenceImageSrc: null,
|
||||
clearCount: 12,
|
||||
difficulty: 4,
|
||||
publicationStatus: 'draft',
|
||||
playCount: 0,
|
||||
updatedAt: '2026-05-18T12:05:00.000Z',
|
||||
publishedAt: null,
|
||||
publishReady: false,
|
||||
generationStatus: 'generating',
|
||||
generatedItemAssets: [],
|
||||
};
|
||||
|
||||
vi.mocked(listMatch3DWorks).mockResolvedValue({
|
||||
items: [persistedGeneratingWork],
|
||||
});
|
||||
vi.mocked(match3dCreationClient.getSession).mockResolvedValueOnce({
|
||||
session: buildMockMatch3DAgentSession({
|
||||
sessionId: 'match3d-session-generating',
|
||||
draft: {
|
||||
profileId: 'match3d-profile-generating',
|
||||
gameName: '生成中抓鹅',
|
||||
themeText: '霓虹水果摊',
|
||||
summary: '刷新后仍应回到抓大鹅生成面板。',
|
||||
tags: ['水果', '抓大鹅'],
|
||||
coverImageSrc: null,
|
||||
referenceImageSrc: null,
|
||||
clearCount: 12,
|
||||
difficulty: 4,
|
||||
generatedItemAssets: [],
|
||||
},
|
||||
stage: 'draft_ready',
|
||||
lastAssistantReply: '正在生成抓大鹅素材。',
|
||||
updatedAt: '2026-05-18T12:05:00.000Z',
|
||||
}),
|
||||
});
|
||||
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openDraftHub(user);
|
||||
await user.click(
|
||||
await screen.findByRole('button', { name: /继续创作《生成中抓鹅》/u }),
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(match3dCreationClient.getSession).toHaveBeenCalledWith(
|
||||
'match3d-session-generating',
|
||||
);
|
||||
});
|
||||
expect(await screen.findByText('抓大鹅草稿生成进度')).toBeTruthy();
|
||||
expect(screen.queryByText('抓大鹅结果页')).toBeNull();
|
||||
expect(getMatch3DWorkDetail).not.toHaveBeenCalledWith(
|
||||
'match3d-profile-generating',
|
||||
);
|
||||
});
|
||||
|
||||
test('running match3d form generation keeps other creation templates available', async () => {
|
||||
const user = userEvent.setup();
|
||||
const runningSession = buildMockMatch3DAgentSession({
|
||||
@@ -6410,6 +6477,59 @@ test('puzzle draft result back button returns to creation hub', async () => {
|
||||
expect(screen.queryByText('拼图结果页')).toBeNull();
|
||||
});
|
||||
|
||||
test('persisted generating puzzle draft opens generation progress after refresh', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
vi.mocked(listPuzzleWorks).mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
workId: 'puzzle-work-session-generating',
|
||||
profileId: 'puzzle-profile-session-generating',
|
||||
ownerUserId: 'user-1',
|
||||
sourceSessionId: 'puzzle-session-generating',
|
||||
authorDisplayName: '测试玩家',
|
||||
workTitle: '生成中拼图',
|
||||
workDescription: '刷新后仍应回到生成面板。',
|
||||
levelName: '生成中拼图',
|
||||
summary: '刷新后仍应回到生成面板。',
|
||||
themeTags: ['雨夜'],
|
||||
coverImageSrc: null,
|
||||
coverAssetId: null,
|
||||
publicationStatus: 'draft',
|
||||
updatedAt: '2026-05-18T12:00:00.000Z',
|
||||
publishedAt: null,
|
||||
playCount: 0,
|
||||
remixCount: 0,
|
||||
likeCount: 0,
|
||||
publishReady: false,
|
||||
generationStatus: 'generating',
|
||||
},
|
||||
],
|
||||
});
|
||||
vi.mocked(getPuzzleAgentSession).mockResolvedValueOnce({
|
||||
session: buildMockPuzzleAgentSession({
|
||||
sessionId: 'puzzle-session-generating',
|
||||
stage: 'collecting_anchors',
|
||||
progressPercent: 42,
|
||||
lastAssistantReply: '正在生成拼图草稿。',
|
||||
updatedAt: '2026-05-18T12:00:00.000Z',
|
||||
}),
|
||||
});
|
||||
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openDraftHub(user);
|
||||
await user.click(await screen.findByRole('button', { name: /继续创作/u }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getPuzzleAgentSession).toHaveBeenCalledWith(
|
||||
'puzzle-session-generating',
|
||||
);
|
||||
});
|
||||
expect(await screen.findByText('拼图草稿生成进度')).toBeTruthy();
|
||||
expect(screen.queryByText('拼图结果页')).toBeNull();
|
||||
});
|
||||
|
||||
test('published puzzle work card restores its source session for editing', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
|
||||
@@ -387,16 +387,24 @@ vi.mock('../../services/rpg-entry/rpgProfileClient', () => ({
|
||||
vi.mock('../ResolvedAssetImage', () => ({
|
||||
ResolvedAssetImage: ({
|
||||
src,
|
||||
fallbackSrc,
|
||||
alt,
|
||||
className,
|
||||
...rest
|
||||
}: {
|
||||
src?: string | null;
|
||||
fallbackSrc?: string | null;
|
||||
alt?: string;
|
||||
className?: string;
|
||||
}) =>
|
||||
src ? (
|
||||
<img src={src} alt={alt ?? ''} className={className} {...rest} />
|
||||
<img
|
||||
src={src}
|
||||
data-fallback-src={fallbackSrc ?? undefined}
|
||||
alt={alt ?? ''}
|
||||
className={className}
|
||||
{...rest}
|
||||
/>
|
||||
) : null,
|
||||
}));
|
||||
|
||||
@@ -2901,6 +2909,36 @@ test('mobile discover recommend feed only rotates the card closest to screen cen
|
||||
);
|
||||
});
|
||||
|
||||
test('mobile discover recommend feed renders cover fallback for legacy browsers', async () => {
|
||||
renderStatefulLoggedOutHomeView({
|
||||
latestEntries: [
|
||||
{
|
||||
...puzzlePublicEntry,
|
||||
coverImageSrc:
|
||||
'/generated-puzzle-assets/puzzle-session-1/cover/image.png',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const discoverPanel = document.getElementById('platform-tab-panel-category');
|
||||
if (!discoverPanel) {
|
||||
throw new Error('缺少发现面板');
|
||||
}
|
||||
|
||||
const card = within(discoverPanel).getByRole('button', { name: /奇幻拼图/u });
|
||||
const cover = card.querySelector('.platform-public-work-card__cover');
|
||||
const image = within(card).getByRole('img');
|
||||
|
||||
expect(cover).toBeTruthy();
|
||||
expect(cover?.className).toContain('platform-public-work-card__cover');
|
||||
expect(image.getAttribute('src')).toBe(
|
||||
'/generated-puzzle-assets/puzzle-session-1/cover/image.png',
|
||||
);
|
||||
expect(image.getAttribute('data-fallback-src')).toBe(
|
||||
'/creation-type-references/puzzle.webp',
|
||||
);
|
||||
});
|
||||
|
||||
test('mobile today channel only shows newly published works from today', async () => {
|
||||
const user = userEvent.setup();
|
||||
const now = new Date();
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
UserRound,
|
||||
XCircle,
|
||||
} from 'lucide-react';
|
||||
import QRCode from 'qrcode';
|
||||
import {
|
||||
type ComponentType,
|
||||
type CSSProperties,
|
||||
@@ -42,7 +43,6 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import QRCode from 'qrcode';
|
||||
|
||||
import communityQqQrImage from '../../../media/social-media-group/qq.png';
|
||||
import communityWechatQrImage from '../../../media/social-media-group/wechat.png';
|
||||
@@ -58,13 +58,13 @@ import type {
|
||||
ProfileRechargeCenterResponse,
|
||||
ProfileRechargeProduct,
|
||||
ProfileReferralInviteCenterResponse,
|
||||
WechatNativePayment,
|
||||
ProfileSaveArchiveSummary,
|
||||
ProfileTaskCenterResponse,
|
||||
ProfileTaskItem,
|
||||
ProfileWalletLedgerResponse,
|
||||
RedeemProfileRewardCodeResponse,
|
||||
WechatMiniProgramPayParams,
|
||||
WechatNativePayment,
|
||||
} from '../../../packages/shared/src/contracts/runtime';
|
||||
import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes';
|
||||
import { buildPublicWorkDetailUrl } from '../../routing/appPageRoutes';
|
||||
@@ -137,6 +137,7 @@ import {
|
||||
resolvePlatformPublicWorkCode,
|
||||
resolvePlatformWorldCoverImage,
|
||||
resolvePlatformWorldCoverSlides,
|
||||
resolvePlatformWorldFallbackCoverImage,
|
||||
resolvePlatformWorldLeadPortrait,
|
||||
} from './rpgEntryWorldPresentation';
|
||||
|
||||
@@ -392,11 +393,13 @@ function usePlatformDesktopLayout() {
|
||||
|
||||
function ResolvedAssetBackdrop({
|
||||
src,
|
||||
fallbackSrc,
|
||||
alt,
|
||||
className,
|
||||
ariaHidden = false,
|
||||
}: {
|
||||
src?: string | null;
|
||||
fallbackSrc?: string | null;
|
||||
alt: string;
|
||||
className: string;
|
||||
ariaHidden?: boolean;
|
||||
@@ -404,6 +407,7 @@ function ResolvedAssetBackdrop({
|
||||
return (
|
||||
<ResolvedAssetImage
|
||||
src={src}
|
||||
fallbackSrc={fallbackSrc}
|
||||
alt={alt}
|
||||
aria-hidden={ariaHidden}
|
||||
className={className}
|
||||
@@ -522,6 +526,7 @@ function WorldCard({
|
||||
variant?: 'standard' | 'immersive';
|
||||
}) {
|
||||
const fallbackCoverImage = resolvePlatformWorldCoverImage(entry);
|
||||
const fallbackAssetCoverImage = resolvePlatformWorldFallbackCoverImage(entry);
|
||||
const coverSlides = useMemo(() => {
|
||||
if (!enableCoverCarousel) {
|
||||
return fallbackCoverImage
|
||||
@@ -606,6 +611,7 @@ function WorldCard({
|
||||
{coverImage ? (
|
||||
<ResolvedAssetBackdrop
|
||||
src={coverImage}
|
||||
fallbackSrc={fallbackAssetCoverImage}
|
||||
alt={entry.worldName}
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
/>
|
||||
@@ -692,6 +698,7 @@ function RecommendCoverOnlyCard({
|
||||
onClick: () => void;
|
||||
}) {
|
||||
const coverImage = resolvePlatformWorldCoverImage(entry);
|
||||
const fallbackCoverImage = resolvePlatformWorldFallbackCoverImage(entry);
|
||||
const displayName = formatPlatformWorkDisplayName(entry.worldName);
|
||||
const typeLabel = describePublicGalleryCardKind(entry);
|
||||
const authorName = entry.authorDisplayName.trim() || '玩家';
|
||||
@@ -708,6 +715,7 @@ function RecommendCoverOnlyCard({
|
||||
{coverImage ? (
|
||||
<ResolvedAssetBackdrop
|
||||
src={coverImage}
|
||||
fallbackSrc={fallbackCoverImage}
|
||||
alt={entry.worldName}
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
/>
|
||||
|
||||
@@ -13,7 +13,9 @@ import {
|
||||
mapBabyObjectMatchDraftToPlatformGalleryCard,
|
||||
mapVisualNovelWorkToPlatformGalleryCard,
|
||||
type PlatformEdutainmentGalleryCard,
|
||||
type PlatformPuzzleGalleryCard,
|
||||
resolvePlatformPublicWorkCode,
|
||||
resolvePlatformWorldFallbackCoverImage,
|
||||
} from './rpgEntryWorldPresentation';
|
||||
|
||||
test('formatPlatformWorldTime formats backend seconds timestamp text as date', () => {
|
||||
@@ -46,6 +48,32 @@ test('platform work display text limits names and tags by character count', () =
|
||||
).toEqual(['超长机关', '星桥']);
|
||||
});
|
||||
|
||||
test('platform public cards use play type reference images as cover fallback', () => {
|
||||
const puzzleCard: PlatformPuzzleGalleryCard = {
|
||||
sourceType: 'puzzle',
|
||||
workId: 'puzzle-work-1',
|
||||
profileId: 'puzzle-profile-1',
|
||||
publicWorkCode: 'PZ-PUZZLE1',
|
||||
ownerUserId: 'user-1',
|
||||
authorDisplayName: '玩家',
|
||||
worldName: '机关拼图',
|
||||
subtitle: '拼图关卡',
|
||||
summaryText: '公开作品',
|
||||
coverImageSrc: '/generated-puzzle-assets/session/cover/image.png',
|
||||
themeTags: ['拼图'],
|
||||
playCount: 1,
|
||||
remixCount: 0,
|
||||
likeCount: 0,
|
||||
visibility: 'published',
|
||||
publishedAt: '2026-05-18T00:00:00.000Z',
|
||||
updatedAt: '2026-05-18T00:00:00.000Z',
|
||||
};
|
||||
|
||||
expect(resolvePlatformWorldFallbackCoverImage(puzzleCard)).toBe(
|
||||
'/creation-type-references/puzzle.webp',
|
||||
);
|
||||
});
|
||||
|
||||
test('buildPuzzleWorkCoverSlides prefers each level formal image', () => {
|
||||
const slides = buildPuzzleWorkCoverSlides({
|
||||
workId: 'work-1',
|
||||
|
||||
@@ -446,6 +446,36 @@ export function resolvePlatformWorldCoverImage(entry: PlatformWorldCardLike) {
|
||||
return '';
|
||||
}
|
||||
|
||||
export function resolvePlatformWorldFallbackCoverImage(
|
||||
entry: PlatformWorldCardLike,
|
||||
) {
|
||||
if (isPuzzleGalleryEntry(entry)) {
|
||||
return '/creation-type-references/puzzle.webp';
|
||||
}
|
||||
|
||||
if (isMatch3DGalleryEntry(entry)) {
|
||||
return '/creation-type-references/match3d.webp';
|
||||
}
|
||||
|
||||
if (isSquareHoleGalleryEntry(entry)) {
|
||||
return '/creation-type-references/square-hole.webp';
|
||||
}
|
||||
|
||||
if (isVisualNovelGalleryEntry(entry)) {
|
||||
return '/creation-type-references/visual-novel.webp';
|
||||
}
|
||||
|
||||
if (isBigFishGalleryEntry(entry)) {
|
||||
return '/creation-type-references/big-fish.webp';
|
||||
}
|
||||
|
||||
if (isEdutainmentGalleryEntry(entry)) {
|
||||
return '/creation-type-references/creative-agent.webp';
|
||||
}
|
||||
|
||||
return '/creation-type-references/rpg.webp';
|
||||
}
|
||||
|
||||
export function resolvePlatformWorldCoverSlides(
|
||||
entry: PlatformWorldCardLike,
|
||||
): PlatformPuzzleCoverSlide[] {
|
||||
|
||||
Reference in New Issue
Block a user