refactor: 收口公开作品流聚合

This commit is contained in:
2026-06-04 04:18:16 +08:00
parent c31676a0e1
commit df17f51edf
6 changed files with 345 additions and 114 deletions

View File

@@ -74,8 +74,8 @@
## 2026-06-03 平台入口公开作品流身份规则收口
- 背景:平台入口公开作品推荐流需要同时处理 RPG、拼图、抓大鹅、跳一跳、敲木鱼、视觉小说、Bark Battle、宝贝识物等卡片公开作品身份、跨玩法去重、排序和推荐运行态 kind 判定曾放在 `PlatformEntryFlowShellImpl.tsx` 巨型实现里。
- 决策:公开作品身份、排序规则、推荐 runtime 启动意图和 ready 判定统一收口到 `src/components/platform-entry/platformPublicGalleryFlow.ts`;入口壳层只调用该 Module 的 `getPlatformPublicGalleryEntryKey``getPlatformRecommendRuntimeKind``resolvePlatformRecommendRuntimeStartIntent``isPlatformRecommendRuntimeReadyForEntry``isSamePlatformPublicGalleryEntry``mergePlatformPublicGalleryEntries``edutainment` key 必须带 `templateId`RPG 卡片回退为 `rpg`。推荐 runtime 启动 intent 只返回启动目标、`embedded` / `returnStage` 参数、阻断文案和错误落点ready 判定只接布尔值与拼图 profile id避免把各玩法 run snapshot 类型拖入 Module。壳层仍执行 request key、运行态 API、错误 setter 与 UI 状态。
- 影响范围:平台入口推荐流、公开作品详情、推荐 runtime 启动、跨玩法公开作品合并,以及后续新增玩法的入口接入。
- 决策:公开作品身份、排序规则、公开作品流聚合矩阵、推荐 runtime 启动意图和 ready 判定统一收口到 `src/components/platform-entry/platformPublicGalleryFlow.ts`;入口壳层只调用该 Module 的 `getPlatformPublicGalleryEntryKey``getPlatformRecommendRuntimeKind``buildPlatformPublicGalleryFeeds``resolvePlatformRecommendRuntimeStartIntent``isPlatformRecommendRuntimeReadyForEntry``isSamePlatformPublicGalleryEntry``mergePlatformPublicGalleryEntries``edutainment` key 必须带 `templateId`RPG 卡片回退为 `rpg`公开作品流聚合负责 featured / latest、玩法可见性 gate、汪汪声浪 works fallback 和首屏 `slice(0, 6)`推荐 runtime 启动 intent 只返回启动目标、`embedded` / `returnStage` 参数、阻断文案和错误落点ready 判定只接布尔值与拼图 profile id避免把各玩法 run snapshot 类型拖入 Module。壳层仍执行 request key、运行态 API、错误 setter 与 UI 状态。
- 影响范围:平台入口推荐流、最新公开作品流、公开作品详情、推荐 runtime 启动、跨玩法公开作品合并,以及后续新增玩法的入口接入。
- 验证方式:`npm run test -- src/components/platform-entry/platformPublicGalleryFlow.test.ts``npm run typecheck``npm run check:encoding`、相关文件 ESLint 通过。
- 关联文档:`docs/technical/【前端架构】平台入口PublicGalleryFlowModule收口计划-2026-06-03.md`

View File

@@ -39,7 +39,7 @@ AI 文字游戏模板接入以 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_
前端 Server-Sent Events 客户端传输层收口到 `src/services/sseStream.ts`事件边界、UTF-8 flush、JSON 解析跳过和提前取消约定见 [【前端架构】SSE客户端传输层收口约定-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91SSE%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BC%A0%E8%BE%93%E5%B1%82%E6%94%B6%E5%8F%A3%E7%BA%A6%E5%AE%9A-2026-06-03.md)。
平台入口公开作品身份、跨玩法去重、推荐运行态 kind 判定、推荐 runtime 启动意图、ready 判定和最新排序收口到 `src/components/platform-entry/platformPublicGalleryFlow.ts`,规则见 [【前端架构】平台入口PublicGalleryFlowModule收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91%E5%B9%B3%E5%8F%B0%E5%85%A5%E5%8F%A3PublicGalleryFlowModule%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
平台入口公开作品身份、跨玩法去重、公开作品流聚合、推荐运行态 kind 判定、推荐 runtime 启动意图、ready 判定和最新排序收口到 `src/components/platform-entry/platformPublicGalleryFlow.ts`,规则见 [【前端架构】平台入口PublicGalleryFlowModule收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91%E5%B9%B3%E5%8F%B0%E5%85%A5%E5%8F%A3PublicGalleryFlowModule%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
统一作品详情页的玩法 kind、详情打开策略、自有作品动作模式、编辑 / 点赞 / 改造 / 启动意图和公开详情映射收口到 `src/components/platform-entry/platformPublicWorkDetailFlow.ts`;抓大鹅公开详情映射与启动 / 编辑 Adapter 的素材归一仍归 `platformMatch3DRuntimeProfile.ts`,规则见 [【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md](./technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md)。

View File

@@ -14,8 +14,9 @@
- `isPlatformRecommendRuntimeReadyForEntry(entry, state)`:用标量 ready state 判定当前推荐 runtime 是否已能承接该公开作品。
- `isSamePlatformPublicGalleryEntry(left, right)`:按公开作品身份比较。
- `mergePlatformPublicGalleryEntries(rpgEntries, puzzleEntries)`:统一完成 RPG 与各玩法公开作品去重、覆盖和倒序排序。
- `buildPlatformPublicGalleryFeeds(input)`:统一构造 `featuredEntries``latestEntries`,收口各玩法可见性 gate、mapper 矩阵、汪汪声浪 works fallback 和推荐首屏 `slice(0, 6)`
入口壳层只调用这些函数,不再在 `PlatformEntryFlowShellImpl.tsx` 内手写公开作品身份、排序规则、推荐 runtime 启动能力矩阵和 ready 判定。ready 判定只接布尔值与拼图 profile id不把各玩法 run snapshot 类型拖入 Module。
入口壳层只调用这些函数,不再在 `PlatformEntryFlowShellImpl.tsx` 内手写公开作品身份、排序规则、公开作品流聚合矩阵、推荐 runtime 启动能力矩阵和 ready 判定。ready 判定只接布尔值与拼图 profile id不把各玩法 run snapshot 类型拖入 Module。
## 玩法身份规则
@@ -24,6 +25,7 @@
- 没有 `sourceType` 的 RPG 公开作品回退为 `rpg`
- 最终 key 格式为 `${kind}:${ownerUserId}:${profileId}`
- 合并时后进入的相同 key 会覆盖先进入的卡片,然后按 `publishedAt ?? updatedAt` 新到旧排序;非法时间按 `0` 处理。
- 公开作品流聚合时,大鱼吃小鱼、宝贝识物和视觉小说必须受各自可见性 gate 控制;汪汪声浪优先用 gallery entriesgallery 为空时才从 works 中筛 `status === 'published'` 作为 fallback。
## 推荐 runtime 启动意图

View File

@@ -361,12 +361,7 @@ import {
isEdutainmentGalleryEntry,
mapBabyObjectMatchDraftToPlatformGalleryCard,
mapBarkBattleWorkToPlatformGalleryCard,
mapBigFishWorkToPlatformGalleryCard,
mapJumpHopWorkToPlatformGalleryCard,
mapPuzzleWorkToPlatformGalleryCard,
mapSquareHoleWorkToPlatformGalleryCard,
mapVisualNovelWorkToPlatformGalleryCard,
mapWoodenFishWorkToPlatformGalleryCard,
type PlatformPublicGalleryCard,
resolvePlatformPublicWorkCode,
} from '../rpg-entry/rpgEntryWorldPresentation';
@@ -551,11 +546,11 @@ import {
resolvePlatformPublicCodeSearchPlan,
} from './platformPublicCodeSearchModel';
import {
buildPlatformPublicGalleryFeeds,
getPlatformPublicGalleryEntryKey,
getPlatformRecommendRuntimeKind,
isPlatformRecommendRuntimeReadyForEntry,
isSamePlatformPublicGalleryEntry,
mergePlatformPublicGalleryEntries,
type RecommendRuntimeKind,
resolvePlatformRecommendRuntimeStartIntent,
} from './platformPublicGalleryFlow';
@@ -2795,123 +2790,43 @@ export function PlatformEntryFlowShellImpl({
agentResultPreview?.source,
);
}, [agentResultPreview]);
const featuredGalleryEntries = useMemo(() => {
const bigFishPublicEntries = isBigFishCreationVisible
? bigFishGalleryEntries.map(mapBigFishWorkToPlatformGalleryCard)
: [];
const babyObjectMatchPublicEntries = isBabyObjectMatchVisible
? babyObjectMatchDrafts
.filter((draft) => draft.publicationStatus === 'published')
.map(mapBabyObjectMatchDraftToPlatformGalleryCard)
: [];
const match3dPublicEntries = match3dGalleryEntries.map(
mapMatch3DWorkToPublicWorkDetail,
);
const puzzlePublicEntries = puzzleGalleryEntries.map(
mapPuzzleWorkToPlatformGalleryCard,
);
const barkBattlePublicEntries =
barkBattleGalleryEntries.length > 0
? barkBattleGalleryEntries.map(mapBarkBattleWorkToPlatformGalleryCard)
: barkBattleWorks
.filter((work) => work.status === 'published')
.map(mapBarkBattleWorkToPlatformGalleryCard);
const squareHolePublicEntries = squareHoleGalleryEntries.map(
mapSquareHoleWorkToPlatformGalleryCard,
);
const jumpHopPublicEntries = jumpHopGalleryEntries.map(
mapJumpHopWorkToPlatformGalleryCard,
);
const woodenFishPublicEntries = woodenFishGalleryEntries.map(
mapWoodenFishWorkToPlatformGalleryCard,
);
const visualNovelPublicEntries = visualNovelGalleryEntries.map(
mapVisualNovelWorkToPlatformGalleryCard,
);
return mergePlatformPublicGalleryEntries(
platformBootstrap.publishedGalleryEntries,
[
...bigFishPublicEntries,
...match3dPublicEntries,
...puzzlePublicEntries,
...barkBattlePublicEntries,
...squareHolePublicEntries,
...jumpHopPublicEntries,
...woodenFishPublicEntries,
...(isVisualNovelCreationOpen ? visualNovelPublicEntries : []),
...babyObjectMatchPublicEntries,
],
).slice(0, 6);
}, [
babyObjectMatchDrafts,
isBigFishCreationVisible,
isBabyObjectMatchVisible,
isVisualNovelCreationOpen,
bigFishGalleryEntries,
jumpHopGalleryEntries,
match3dGalleryEntries,
platformBootstrap.publishedGalleryEntries,
puzzleGalleryEntries,
barkBattleGalleryEntries,
barkBattleWorks,
squareHoleGalleryEntries,
visualNovelGalleryEntries,
woodenFishGalleryEntries,
]);
const latestGalleryEntries = useMemo(
const publicGalleryFeeds = useMemo(
() =>
mergePlatformPublicGalleryEntries(
platformBootstrap.publishedGalleryEntries,
[
...(isBigFishCreationVisible
? bigFishGalleryEntries.map(mapBigFishWorkToPlatformGalleryCard)
: []),
...match3dGalleryEntries.map(mapMatch3DWorkToPublicWorkDetail),
...puzzleGalleryEntries.map(mapPuzzleWorkToPlatformGalleryCard),
...barkBattleGalleryEntries.map(
mapBarkBattleWorkToPlatformGalleryCard,
),
...jumpHopGalleryEntries.map(mapJumpHopWorkToPlatformGalleryCard),
...(barkBattleGalleryEntries.length === 0
? barkBattleWorks
.filter((work) => work.status === 'published')
.map(mapBarkBattleWorkToPlatformGalleryCard)
: []),
...woodenFishGalleryEntries.map(
mapWoodenFishWorkToPlatformGalleryCard,
),
...squareHoleGalleryEntries.map(
mapSquareHoleWorkToPlatformGalleryCard,
),
...(isVisualNovelCreationOpen
? visualNovelGalleryEntries.map(
mapVisualNovelWorkToPlatformGalleryCard,
)
: []),
...(isBabyObjectMatchVisible
? babyObjectMatchDrafts
.filter((draft) => draft.publicationStatus === 'published')
.map(mapBabyObjectMatchDraftToPlatformGalleryCard)
: []),
],
),
buildPlatformPublicGalleryFeeds({
rpgEntries: platformBootstrap.publishedGalleryEntries,
bigFishEntries: bigFishGalleryEntries,
match3dEntries: match3dGalleryEntries,
puzzleEntries: puzzleGalleryEntries,
barkBattleGalleryEntries,
barkBattleWorks,
jumpHopEntries: jumpHopGalleryEntries,
woodenFishEntries: woodenFishGalleryEntries,
squareHoleEntries: squareHoleGalleryEntries,
visualNovelEntries: visualNovelGalleryEntries,
babyObjectMatchDrafts,
isBigFishCreationVisible,
isBabyObjectMatchVisible,
isVisualNovelCreationOpen,
}),
[
babyObjectMatchDrafts,
barkBattleGalleryEntries,
barkBattleWorks,
bigFishGalleryEntries,
isBabyObjectMatchVisible,
isBigFishCreationVisible,
isVisualNovelCreationOpen,
bigFishGalleryEntries,
jumpHopGalleryEntries,
match3dGalleryEntries,
platformBootstrap.publishedGalleryEntries,
puzzleGalleryEntries,
squareHoleGalleryEntries,
visualNovelGalleryEntries,
barkBattleGalleryEntries,
barkBattleWorks,
woodenFishGalleryEntries,
],
);
const { featuredEntries: featuredGalleryEntries, latestEntries: latestGalleryEntries } =
publicGalleryFeeds;
const recommendRuntimeEntries = useMemo(
() =>
buildPlatformRecommendFeedEntries(

View File

@@ -1,6 +1,9 @@
import { expect, test } from 'vitest';
import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle';
import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject';
import type { JumpHopGalleryCardResponse } from '../../../packages/shared/src/contracts/jumpHop';
import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks';
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
import type { CustomWorldGalleryCard } from '../../../packages/shared/src/contracts/runtime';
@@ -10,6 +13,7 @@ import {
type PlatformPublicGalleryCard,
} from '../rpg-entry/rpgEntryWorldPresentation';
import {
buildPlatformPublicGalleryFeeds,
getPlatformPublicGalleryEntryKey,
getPlatformPublicGalleryEntryTime,
getPlatformRecommendRuntimeKind,
@@ -59,6 +63,85 @@ function buildRpgEntry(
};
}
function buildBigFishWork(
overrides: Partial<BigFishWorkSummary> = {},
): BigFishWorkSummary {
return {
workId: 'big-fish-work',
sourceSessionId: 'big-fish-session',
ownerUserId: 'user-1',
authorDisplayName: '玩家',
title: '大鱼吃小鱼',
subtitle: '海湾',
summary: '一路长大。',
coverImageSrc: '/big-fish-cover.png',
status: 'published',
updatedAt: '2026-06-01T02:00:00.000Z',
publishedAt: '2026-06-01T02:00:00.000Z',
publishReady: true,
levelCount: 1,
levelMainImageReadyCount: 1,
levelMotionReadyCount: 1,
backgroundReady: true,
playCount: 1,
...overrides,
};
}
function buildBabyObjectMatchDraft(
overrides: Partial<BabyObjectMatchDraft> = {},
): BabyObjectMatchDraft {
const itemAsset = {
itemId: 'item-a',
itemName: '苹果',
imageSrc: '/apple.png',
assetObjectId: null,
generationProvider: 'placeholder' as const,
prompt: '苹果',
};
return {
draftId: 'baby-draft',
profileId: 'baby-profile',
templateId: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID,
templateName: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
workTitle: '宝贝识物',
workDescription: '认识水果。',
itemNames: ['苹果', '香蕉'],
itemAssets: [itemAsset, { ...itemAsset, itemId: 'item-b', itemName: '香蕉' }],
visualPackage: null,
themeTags: ['寓教于乐'],
publicationStatus: 'published',
createdAt: '2026-06-01T00:00:00.000Z',
updatedAt: '2026-06-01T03:00:00.000Z',
publishedAt: '2026-06-01T03:00:00.000Z',
...overrides,
};
}
function buildJumpHopEntry(
overrides: Partial<JumpHopGalleryCardResponse> = {},
): JumpHopGalleryCardResponse {
return {
publicWorkCode: 'JH-JUMP',
workId: 'jump-hop-work',
profileId: 'jump-hop-profile',
ownerUserId: 'user-1',
authorDisplayName: '玩家',
workTitle: '跳一跳',
workDescription: '一路向前。',
coverImageSrc: '/jump-hop-cover.png',
themeTags: ['跳一跳'],
difficulty: 'standard',
stylePreset: 'minimal-blocks',
publicationStatus: 'published',
playCount: 1,
updatedAt: '2026-06-04T00:00:00.000Z',
publishedAt: '2026-06-04T00:00:00.000Z',
generationStatus: 'ready',
...overrides,
};
}
function buildTypedEntry(
sourceType: PlatformGallerySourceType,
overrides: TypedPlatformPublicGalleryCardOverrides = {},
@@ -570,3 +653,144 @@ test('platform public gallery flow merges duplicate identities and sorts newest
expect(merged[0]?.worldName).toBe('新版 RPG');
expect(getPlatformPublicGalleryEntryTime(invalidTimeEntry)).toBe(0);
});
test('platform public gallery flow builds feeds with visibility gates and bark battle fallback', () => {
const hiddenBigFish = buildBigFishWork({
workId: 'hidden-big-fish',
sourceSessionId: 'hidden-big-fish-session',
});
const hiddenBabyDraft = buildBabyObjectMatchDraft({
profileId: 'hidden-baby',
});
const publishedBarkFallback = buildBarkBattleWork({
workId: 'fallback-bark',
publishedAt: '2026-06-04T00:00:00.000Z',
updatedAt: '2026-06-04T00:00:00.000Z',
});
const draftBarkFallback = buildBarkBattleWork({
workId: 'draft-bark',
status: 'draft',
});
const hiddenFeeds = buildPlatformPublicGalleryFeeds({
rpgEntries: [
buildRpgEntry({
profileId: 'rpg-visible',
publishedAt: '2026-06-01T00:00:00.000Z',
}),
],
bigFishEntries: [hiddenBigFish],
match3dEntries: [],
puzzleEntries: [],
barkBattleGalleryEntries: [],
barkBattleWorks: [draftBarkFallback, publishedBarkFallback],
jumpHopEntries: [],
woodenFishEntries: [],
squareHoleEntries: [],
visualNovelEntries: [],
babyObjectMatchDrafts: [hiddenBabyDraft],
isBigFishCreationVisible: false,
isBabyObjectMatchVisible: false,
isVisualNovelCreationOpen: false,
});
expect(
hiddenFeeds.latestEntries.map((entry) =>
'sourceType' in entry ? entry.sourceType : 'rpg',
),
).toEqual(['bark-battle', 'rpg']);
expect(hiddenFeeds.latestEntries[0]?.profileId).toBe('fallback-bark');
const visibleFeeds = buildPlatformPublicGalleryFeeds({
rpgEntries: [],
bigFishEntries: [hiddenBigFish],
match3dEntries: [],
puzzleEntries: [],
barkBattleGalleryEntries: [
buildBarkBattleWork({
workId: 'gallery-bark',
publishedAt: '2026-06-05T00:00:00.000Z',
updatedAt: '2026-06-05T00:00:00.000Z',
}),
],
barkBattleWorks: [publishedBarkFallback],
jumpHopEntries: [],
woodenFishEntries: [],
squareHoleEntries: [],
visualNovelEntries: [],
babyObjectMatchDrafts: [hiddenBabyDraft],
isBigFishCreationVisible: true,
isBabyObjectMatchVisible: true,
isVisualNovelCreationOpen: false,
});
expect(visibleFeeds.latestEntries.map((entry) => entry.profileId)).toEqual([
'gallery-bark',
'hidden-baby',
'hidden-big-fish-session',
]);
expect(visibleFeeds.featuredEntries).toEqual(
visibleFeeds.latestEntries.slice(0, 6),
);
});
test('platform public gallery flow preserves feed tie order and featured slice', () => {
const sameTime = '2026-06-04T00:00:00.000Z';
const tieFeeds = buildPlatformPublicGalleryFeeds({
rpgEntries: [],
bigFishEntries: [],
match3dEntries: [],
puzzleEntries: [],
barkBattleGalleryEntries: [],
barkBattleWorks: [
buildBarkBattleWork({
workId: 'fallback-bark',
publishedAt: sameTime,
updatedAt: sameTime,
}),
],
jumpHopEntries: [
buildJumpHopEntry({
profileId: 'jump-hop',
publishedAt: sameTime,
updatedAt: sameTime,
}),
],
woodenFishEntries: [],
squareHoleEntries: [],
visualNovelEntries: [],
babyObjectMatchDrafts: [],
isBigFishCreationVisible: false,
isBabyObjectMatchVisible: false,
isVisualNovelCreationOpen: false,
});
expect(tieFeeds.latestEntries.map((entry) => entry.profileId)).toEqual([
'jump-hop',
'fallback-bark',
]);
const sliceFeeds = buildPlatformPublicGalleryFeeds({
rpgEntries: Array.from({ length: 7 }, (_, index) =>
buildRpgEntry({
profileId: `rpg-${index}`,
publishedAt: `2026-06-0${index + 1}T00:00:00.000Z`,
updatedAt: `2026-06-0${index + 1}T00:00:00.000Z`,
}),
),
bigFishEntries: [],
match3dEntries: [],
puzzleEntries: [],
barkBattleGalleryEntries: [],
barkBattleWorks: [],
jumpHopEntries: [],
woodenFishEntries: [],
squareHoleEntries: [],
visualNovelEntries: [],
babyObjectMatchDrafts: [],
isBigFishCreationVisible: false,
isBabyObjectMatchVisible: false,
isVisualNovelCreationOpen: false,
});
expect(sliceFeeds.featuredEntries).toHaveLength(6);
});

View File

@@ -1,9 +1,13 @@
import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle';
import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject';
import type { JumpHopGalleryCardResponse } from '../../../packages/shared/src/contracts/jumpHop';
import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks';
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
import type { CustomWorldGalleryCard } from '../../../packages/shared/src/contracts/runtime';
import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks';
import type { VisualNovelWorkSummary } from '../../../packages/shared/src/contracts/visualNovel';
import type { WoodenFishGalleryCardResponse } from '../../../packages/shared/src/contracts/woodenFish';
import {
isBarkBattleGalleryEntry,
isBigFishGalleryEntry,
@@ -14,8 +18,17 @@ import {
isSquareHoleGalleryEntry,
isVisualNovelGalleryEntry,
isWoodenFishGalleryEntry,
mapBabyObjectMatchDraftToPlatformGalleryCard,
mapBarkBattleWorkToPlatformGalleryCard,
mapBigFishWorkToPlatformGalleryCard,
mapJumpHopWorkToPlatformGalleryCard,
mapPuzzleWorkToPlatformGalleryCard,
mapSquareHoleWorkToPlatformGalleryCard,
mapVisualNovelWorkToPlatformGalleryCard,
mapWoodenFishWorkToPlatformGalleryCard,
type PlatformPublicGalleryCard,
} from '../rpg-entry/rpgEntryWorldPresentation';
import { mapMatch3DWorkToPublicWorkDetail } from './platformMatch3DRuntimeProfile';
import {
mapBarkBattlePublicDetailToWorkSummary,
mapPublicWorkDetailToBigFishWork,
@@ -127,6 +140,28 @@ export type PlatformRecommendRuntimeReadyState = {
puzzleRunCurrentLevelProfileId?: string | null;
};
export type PlatformPublicGalleryFeedsInput = {
rpgEntries: readonly CustomWorldGalleryCard[];
bigFishEntries: readonly BigFishWorkSummary[];
match3dEntries: readonly Match3DWorkSummary[];
puzzleEntries: readonly PuzzleWorkSummary[];
barkBattleGalleryEntries: readonly BarkBattleWorkSummary[];
barkBattleWorks: readonly BarkBattleWorkSummary[];
jumpHopEntries: readonly JumpHopGalleryCardResponse[];
woodenFishEntries: readonly WoodenFishGalleryCardResponse[];
squareHoleEntries: readonly SquareHoleWorkSummary[];
visualNovelEntries: readonly VisualNovelWorkSummary[];
babyObjectMatchDrafts: readonly BabyObjectMatchDraft[];
isBigFishCreationVisible: boolean;
isBabyObjectMatchVisible: boolean;
isVisualNovelCreationOpen: boolean;
};
export type PlatformPublicGalleryFeeds = {
featuredEntries: PlatformPublicGalleryCard[];
latestEntries: PlatformPublicGalleryCard[];
};
export function getPlatformPublicGalleryEntryTime(
entry: PlatformPublicGalleryCard,
) {
@@ -399,8 +434,8 @@ export function isSamePlatformPublicGalleryEntry(
}
export function mergePlatformPublicGalleryEntries(
rpgEntries: CustomWorldGalleryCard[],
puzzleEntries: PlatformPublicGalleryCard[],
rpgEntries: readonly CustomWorldGalleryCard[],
puzzleEntries: readonly PlatformPublicGalleryCard[],
) {
const entryMap = new Map<string, PlatformPublicGalleryCard>();
@@ -414,3 +449,58 @@ export function mergePlatformPublicGalleryEntries(
getPlatformPublicGalleryEntryTime(left),
);
}
export function buildPlatformPublicGalleryFeeds(
input: PlatformPublicGalleryFeedsInput,
): PlatformPublicGalleryFeeds {
const bigFishEntries = input.isBigFishCreationVisible
? input.bigFishEntries.map(mapBigFishWorkToPlatformGalleryCard)
: [];
const babyObjectMatchEntries = input.isBabyObjectMatchVisible
? input.babyObjectMatchDrafts
.filter((draft) => draft.publicationStatus === 'published')
.map(mapBabyObjectMatchDraftToPlatformGalleryCard)
: [];
const barkBattleGalleryEntries = input.barkBattleGalleryEntries.map(
mapBarkBattleWorkToPlatformGalleryCard,
);
const barkBattleFallbackEntries =
input.barkBattleGalleryEntries.length === 0
? input.barkBattleWorks
.filter((work) => work.status === 'published')
.map(mapBarkBattleWorkToPlatformGalleryCard)
: [];
const visualNovelEntries = input.isVisualNovelCreationOpen
? input.visualNovelEntries.map(mapVisualNovelWorkToPlatformGalleryCard)
: [];
const latestEntries = mergePlatformPublicGalleryEntries(input.rpgEntries, [
...bigFishEntries,
...input.match3dEntries.map(mapMatch3DWorkToPublicWorkDetail),
...input.puzzleEntries.map(mapPuzzleWorkToPlatformGalleryCard),
...barkBattleGalleryEntries,
...input.jumpHopEntries.map(mapJumpHopWorkToPlatformGalleryCard),
...barkBattleFallbackEntries,
...input.woodenFishEntries.map(mapWoodenFishWorkToPlatformGalleryCard),
...input.squareHoleEntries.map(mapSquareHoleWorkToPlatformGalleryCard),
...visualNovelEntries,
...babyObjectMatchEntries,
]);
const featuredEntries = mergePlatformPublicGalleryEntries(input.rpgEntries, [
...bigFishEntries,
...input.match3dEntries.map(mapMatch3DWorkToPublicWorkDetail),
...input.puzzleEntries.map(mapPuzzleWorkToPlatformGalleryCard),
...(barkBattleGalleryEntries.length > 0
? barkBattleGalleryEntries
: barkBattleFallbackEntries),
...input.squareHoleEntries.map(mapSquareHoleWorkToPlatformGalleryCard),
...input.jumpHopEntries.map(mapJumpHopWorkToPlatformGalleryCard),
...input.woodenFishEntries.map(mapWoodenFishWorkToPlatformGalleryCard),
...visualNovelEntries,
...babyObjectMatchEntries,
]).slice(0, 6);
return {
featuredEntries,
latestEntries,
};
}