refactor: 收口推荐 runtime 自动启动
This commit is contained in:
@@ -32,6 +32,14 @@
|
|||||||
- 验证方式:`npm run test -- src/components/platform-entry/platformRecommendRuntimeAuthModel.test.ts`、针对新 Module 与平台壳执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
|
- 验证方式:`npm run test -- src/components/platform-entry/platformRecommendRuntimeAuthModel.test.ts`、针对新 Module 与平台壳执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
|
||||||
- 关联文档:`docs/technical/【前端架构】PlatformRecommendRuntimeAuthModel收口计划-2026-06-04.md`。
|
- 关联文档:`docs/technical/【前端架构】PlatformRecommendRuntimeAuthModel收口计划-2026-06-04.md`。
|
||||||
|
|
||||||
|
## 2026-06-04 Platform Recommend Runtime Auto Start 收口
|
||||||
|
|
||||||
|
- 背景:推荐 runtime 自动启动 effect 同时判断桌面断点、stage、Tab、loading、推荐列表、active entry、ready 状态和启动中状态,导致壳层 effect 依赖过长且混合推荐流状态机知识。
|
||||||
|
- 决策:扩展 `src/components/platform-entry/platformPublicGalleryFlow.ts`,新增 `resolvePlatformRecommendRuntimeAutoStartDecision(input)`,只返回 `noop`、`clear` 或 `start(entry)`。平台壳只执行清空 active runtime state 或调用 `selectRecommendRuntimeEntry(entry)`。
|
||||||
|
- 影响范围:移动端首页推荐 runtime 自动启动、推荐列表为空时清空状态、active entry ready 判定,以及后续新增推荐 runtime 玩法的启动时机。
|
||||||
|
- 验证方式:`npm run test -- src/components/platform-entry/platformPublicGalleryFlow.test.ts`、针对 Flow Module 与平台壳执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
|
||||||
|
- 关联文档:`docs/technical/【前端架构】PlatformRecommendRuntimeAutoStart收口计划-2026-06-04.md`。
|
||||||
|
|
||||||
## 2026-06-04 Platform Creation Launch Model 收口
|
## 2026-06-04 Platform Creation Launch Model 收口
|
||||||
|
|
||||||
- 背景:平台创作入口点击回调曾在 `PlatformEntryFlowShellImpl.tsx` 内联判断 `airp` 占位、隐藏的 `baby-object-match`、未知入口和各玩法工作台启动目标,壳层同时承接入口 ID 规则、启动前准备顺序和副作用。
|
- 背景:平台创作入口点击回调曾在 `PlatformEntryFlowShellImpl.tsx` 内联判断 `airp` 占位、隐藏的 `baby-object-match`、未知入口和各玩法工作台启动目标,壳层同时承接入口 ID 规则、启动前准备顺序和副作用。
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ Bark Battle 草稿三图完整性、生成状态归一、发布快照 / 发布
|
|||||||
|
|
||||||
平台首页推荐 runtime 的匿名 Runtime Guest Token、已登录 background auth、非 embedded no-op 和拼图 isolated/default auth mode 计划收口到 `src/components/platform-entry/platformRecommendRuntimeAuthModel.ts`,规则见 [【前端架构】PlatformRecommendRuntimeAuthModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformRecommendRuntimeAuthModel收口计划-2026-06-04.md)。
|
平台首页推荐 runtime 的匿名 Runtime Guest Token、已登录 background auth、非 embedded no-op 和拼图 isolated/default auth mode 计划收口到 `src/components/platform-entry/platformRecommendRuntimeAuthModel.ts`,规则见 [【前端架构】PlatformRecommendRuntimeAuthModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformRecommendRuntimeAuthModel收口计划-2026-06-04.md)。
|
||||||
|
|
||||||
|
平台首页推荐 runtime 自动启动的桌面 / Tab / stage / loading gate、active entry 查找、ready 判定和 clear/start/noop 决策收口到 `src/components/platform-entry/platformPublicGalleryFlow.ts`,规则见 [【前端架构】PlatformRecommendRuntimeAutoStart收口计划-2026-06-04.md](./technical/【前端架构】PlatformRecommendRuntimeAutoStart收口计划-2026-06-04.md)。
|
||||||
|
|
||||||
RPG Agent 结果页发布门禁展示和预览来源 label 收口到 `src/components/platform-entry/platformRpgAgentResultPreviewModel.ts`,壳层只保留 session/profile 编排和结果页 props 传递,规则见 [【前端架构】PlatformRpgAgentResultPreviewModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformRpgAgentResultPreviewModel收口计划-2026-06-04.md)。
|
RPG Agent 结果页发布门禁展示和预览来源 label 收口到 `src/components/platform-entry/platformRpgAgentResultPreviewModel.ts`,壳层只保留 session/profile 编排和结果页 props 传递,规则见 [【前端架构】PlatformRpgAgentResultPreviewModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformRpgAgentResultPreviewModel收口计划-2026-06-04.md)。
|
||||||
|
|
||||||
平台入口创作生成通知、pending 作品架占位、失败覆盖、拼图稳定 ID 和草稿 Tab 未读点收口到 `src/components/platform-entry/platformDraftGenerationShelfModel.ts`,规则见 [【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91DraftGenerationShelfModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
平台入口创作生成通知、pending 作品架占位、失败覆盖、拼图稳定 ID 和草稿 Tab 未读点收口到 `src/components/platform-entry/platformDraftGenerationShelfModel.ts`,规则见 [【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91DraftGenerationShelfModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# 【前端架构】Platform Recommend Runtime Auto Start 收口计划
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
平台推荐页的 embedded runtime 会在移动端首页自动选择当前推荐作品并启动对应玩法。旧 `useEffect` 同时判断桌面断点、当前 stage、当前 Tab、平台 loading、推荐列表是否为空、active entry 是否仍存在、对应 runtime 是否 ready、是否已有启动请求,以及下一条 entry 应选谁。
|
||||||
|
|
||||||
|
这组判断是纯推荐流自动启动决策,但留在 `PlatformEntryFlowShellImpl.tsx` 会让 effect 依赖很长,也让后续新增玩法时容易把 ready 判定和启动时机混在副作用里。
|
||||||
|
|
||||||
|
## 决策
|
||||||
|
|
||||||
|
扩展 `src/components/platform-entry/platformPublicGalleryFlow.ts`,新增 `resolvePlatformRecommendRuntimeAutoStartDecision(input)`:
|
||||||
|
|
||||||
|
- `noop`:当前不需要改变推荐 runtime。
|
||||||
|
- `clear`:推荐列表为空,壳层应清空 active entry、runtime kind 和错误。
|
||||||
|
- `start`:壳层应调用既有 `selectRecommendRuntimeEntry(entry)` 启动指定作品。
|
||||||
|
|
||||||
|
`PlatformEntryFlowShellImpl.tsx` 继续作为 **Adapter**:它负责收集 React state、清空 state、调用 `selectRecommendRuntimeEntry(...)` 和执行各玩法 runtime 副作用。
|
||||||
|
|
||||||
|
## Interface 约束
|
||||||
|
|
||||||
|
- 桌面端、非 `platform` stage、非 `home` Tab 或平台仍在 loading 时返回 `noop`。
|
||||||
|
- 推荐列表为空时返回 `clear`。
|
||||||
|
- active entry 存在且对应 runtime 已 ready 时返回 `noop`。
|
||||||
|
- 当前已有启动请求时返回 `noop`。
|
||||||
|
- active entry 存在但未 ready 时返回 `start(activeEntry)`。
|
||||||
|
- active key 缺失或已不在列表中时返回 `start(firstEntry)`。
|
||||||
|
|
||||||
|
## Depth / Leverage / Locality
|
||||||
|
|
||||||
|
- **Depth**:壳层只消费三态决策;列表查找、ready 判定和自动启动门禁藏入 Flow Module Implementation。
|
||||||
|
- **Leverage**:后续推荐流新增玩法或改 ready 判定,只需补 `platformPublicGalleryFlow.ts` 的模型测试。
|
||||||
|
- **Locality**:effect 只保留副作用动作,不再承载推荐流状态机知识。
|
||||||
|
|
||||||
|
## 验收
|
||||||
|
|
||||||
|
- `npm run test -- src/components/platform-entry/platformPublicGalleryFlow.test.ts`
|
||||||
|
- `npx eslint --max-warnings 0 src/components/platform-entry/platformPublicGalleryFlow.ts src/components/platform-entry/platformPublicGalleryFlow.test.ts`
|
||||||
|
- `npx eslint src/components/platform-entry/PlatformEntryFlowShellImpl.tsx --quiet`
|
||||||
|
- `npm run typecheck`
|
||||||
|
- `npm run check:encoding`
|
||||||
@@ -171,7 +171,7 @@ RPG / 拼图等运行态存档仍以 `/api/profile/save-archives` 的后端列
|
|||||||
|
|
||||||
删除等破坏性动作当前未接入 jump-hop 删除 API;如果后续要在作品架提供删除入口,必须先补齐后端/SpacetimeDB/前端整条删除链路,再开放按钮。
|
删除等破坏性动作当前未接入 jump-hop 删除 API;如果后续要在作品架提供删除入口,必须先补齐后端/SpacetimeDB/前端整条删除链路,再开放按钮。
|
||||||
|
|
||||||
推荐页匿名游玩不再限定为跳一跳。移动端一级 `推荐` Tab 是内嵌运行态刷卡流,会自动选择推荐作品并启动对应玩法;桌面端首页不启动这套移动推荐运行态,而是渲染桌面发现壳,展示 `今日游戏`、`推荐`、`作品分类` 等桌面内容。断点事实统一走 `platformEntryResponsive.ts` 的 `usePlatformDesktopLayout()`,平台壳和首页视图必须共用同一个判断,避免桌面发现页与移动推荐页同时挂载、重复触发请求或启动运行态。推荐页嵌入运行态启动时按真实身份分流:已登录用户或本地已有 access token 时继续使用账号 Bearer,但请求选项必须是 local auth impact,避免单卡 401 清空整站登录态;只有确认为匿名访客时才申请短期 Runtime Guest Token,并只把它作为局部请求头传给运行态客户端,不写入全局登录态、不触发 refresh,也不把匿名流量伪装成普通用户。当前覆盖矩阵为:跳一跳、视觉小说、抓大鹅 Match3D、方洞挑战、拼图、敲木鱼、大鱼吃小鱼、汪汪声浪。每个模板的启动请求、推荐页内后续运行态动作以及需要上报的 play/finish/leaderboard/next-level 类请求,都必须继续按该身份分流;公开读取入口仍可匿名读取,创作、个人作品、删除、发布、Remix 等账号/所有权动作仍保持普通用户鉴权。推荐 runtime 的 `none` / `background` / `runtime-guest` 请求计划和拼图 `default` / `isolated` runtime auth mode 由 `platformRecommendRuntimeAuthModel.ts` 统一判定,平台壳只负责读取 token、申请 Runtime Guest Token 和传递 request options。
|
推荐页匿名游玩不再限定为跳一跳。移动端一级 `推荐` Tab 是内嵌运行态刷卡流,会自动选择推荐作品并启动对应玩法;桌面端首页不启动这套移动推荐运行态,而是渲染桌面发现壳,展示 `今日游戏`、`推荐`、`作品分类` 等桌面内容。断点事实统一走 `platformEntryResponsive.ts` 的 `usePlatformDesktopLayout()`,平台壳和首页视图必须共用同一个判断,避免桌面发现页与移动推荐页同时挂载、重复触发请求或启动运行态。推荐页嵌入运行态启动时按真实身份分流:已登录用户或本地已有 access token 时继续使用账号 Bearer,但请求选项必须是 local auth impact,避免单卡 401 清空整站登录态;只有确认为匿名访客时才申请短期 Runtime Guest Token,并只把它作为局部请求头传给运行态客户端,不写入全局登录态、不触发 refresh,也不把匿名流量伪装成普通用户。当前覆盖矩阵为:跳一跳、视觉小说、抓大鹅 Match3D、方洞挑战、拼图、敲木鱼、大鱼吃小鱼、汪汪声浪。每个模板的启动请求、推荐页内后续运行态动作以及需要上报的 play/finish/leaderboard/next-level 类请求,都必须继续按该身份分流;公开读取入口仍可匿名读取,创作、个人作品、删除、发布、Remix 等账号/所有权动作仍保持普通用户鉴权。推荐 runtime 的 `none` / `background` / `runtime-guest` 请求计划和拼图 `default` / `isolated` runtime auth mode 由 `platformRecommendRuntimeAuthModel.ts` 统一判定,平台壳只负责读取 token、申请 Runtime Guest Token 和传递 request options。推荐 runtime 自动启动只由 `platformPublicGalleryFlow.ts` 输出 `noop` / `clear` / `start(entry)` 决策,平台壳只执行清空 state 或启动指定作品。
|
||||||
|
|
||||||
## 敲木鱼
|
## 敲木鱼
|
||||||
|
|
||||||
|
|||||||
@@ -553,9 +553,9 @@ import {
|
|||||||
buildPlatformPublicGalleryFeeds,
|
buildPlatformPublicGalleryFeeds,
|
||||||
getPlatformPublicGalleryEntryKey,
|
getPlatformPublicGalleryEntryKey,
|
||||||
getPlatformRecommendRuntimeKind,
|
getPlatformRecommendRuntimeKind,
|
||||||
isPlatformRecommendRuntimeReadyForEntry,
|
|
||||||
isSamePlatformPublicGalleryEntry,
|
isSamePlatformPublicGalleryEntry,
|
||||||
type RecommendRuntimeKind,
|
type RecommendRuntimeKind,
|
||||||
|
resolvePlatformRecommendRuntimeAutoStartDecision,
|
||||||
resolvePlatformRecommendRuntimeStartIntent,
|
resolvePlatformRecommendRuntimeStartIntent,
|
||||||
} from './platformPublicGalleryFlow';
|
} from './platformPublicGalleryFlow';
|
||||||
import {
|
import {
|
||||||
@@ -12309,31 +12309,15 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
const decision = resolvePlatformRecommendRuntimeAutoStartDecision({
|
||||||
isDesktopLayout ||
|
isDesktopLayout,
|
||||||
selectionStage !== 'platform' ||
|
selectionStage,
|
||||||
platformBootstrap.platformTab !== 'home' ||
|
platformTab: platformBootstrap.platformTab,
|
||||||
platformBootstrap.isLoadingPlatform
|
isLoadingPlatform: platformBootstrap.isLoadingPlatform,
|
||||||
) {
|
entries: recommendRuntimeEntries,
|
||||||
return;
|
activeEntryKey: activeRecommendEntryKey,
|
||||||
}
|
isStarting: isStartingRecommendEntry,
|
||||||
|
readyState: {
|
||||||
if (recommendRuntimeEntries.length === 0) {
|
|
||||||
setActiveRecommendEntryKey(null);
|
|
||||||
setActiveRecommendRuntimeKind(null);
|
|
||||||
setActiveRecommendRuntimeError(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const activeRecommendEntry = activeRecommendEntryKey
|
|
||||||
? (recommendRuntimeEntries.find(
|
|
||||||
(entry) =>
|
|
||||||
getPlatformPublicGalleryEntryKey(entry) === activeRecommendEntryKey,
|
|
||||||
) ?? null)
|
|
||||||
: null;
|
|
||||||
const isActiveRecommendRuntimeReady =
|
|
||||||
activeRecommendEntry !== null &&
|
|
||||||
isPlatformRecommendRuntimeReadyForEntry(activeRecommendEntry, {
|
|
||||||
activeKind: activeRecommendRuntimeKind,
|
activeKind: activeRecommendRuntimeKind,
|
||||||
hasBabyObjectMatchDraft: Boolean(babyObjectMatchDraft),
|
hasBabyObjectMatchDraft: Boolean(babyObjectMatchDraft),
|
||||||
hasBigFishRun: Boolean(bigFishRun),
|
hasBigFishRun: Boolean(bigFishRun),
|
||||||
@@ -12345,19 +12329,21 @@ export function PlatformEntryFlowShellImpl({
|
|||||||
puzzleRunEntryProfileId: puzzleRun?.entryProfileId ?? null,
|
puzzleRunEntryProfileId: puzzleRun?.entryProfileId ?? null,
|
||||||
puzzleRunCurrentLevelProfileId:
|
puzzleRunCurrentLevelProfileId:
|
||||||
puzzleRun?.currentLevel?.profileId ?? null,
|
puzzleRun?.currentLevel?.profileId ?? null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
if (
|
|
||||||
(activeRecommendEntry !== null && isActiveRecommendRuntimeReady) ||
|
if (decision.type === 'noop') {
|
||||||
isStartingRecommendEntry
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextRecommendEntry =
|
if (decision.type === 'clear') {
|
||||||
activeRecommendEntry ?? recommendRuntimeEntries[0];
|
setActiveRecommendEntryKey(null);
|
||||||
if (nextRecommendEntry) {
|
setActiveRecommendRuntimeKind(null);
|
||||||
void selectRecommendRuntimeEntry(nextRecommendEntry);
|
setActiveRecommendRuntimeError(null);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void selectRecommendRuntimeEntry(decision.entry);
|
||||||
}, [
|
}, [
|
||||||
activeRecommendEntryKey,
|
activeRecommendEntryKey,
|
||||||
activeRecommendRuntimeKind,
|
activeRecommendRuntimeKind,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
mergePlatformPublicGalleryEntries,
|
mergePlatformPublicGalleryEntries,
|
||||||
type PlatformRecommendRuntimeStartIntentDeps,
|
type PlatformRecommendRuntimeStartIntentDeps,
|
||||||
type RecommendRuntimeKind,
|
type RecommendRuntimeKind,
|
||||||
|
resolvePlatformRecommendRuntimeAutoStartDecision,
|
||||||
resolvePlatformRecommendRuntimeStartIntent,
|
resolvePlatformRecommendRuntimeStartIntent,
|
||||||
} from './platformPublicGalleryFlow';
|
} from './platformPublicGalleryFlow';
|
||||||
import {
|
import {
|
||||||
@@ -611,6 +612,92 @@ test('platform public gallery flow resolves puzzle and edutainment readiness det
|
|||||||
).toBe(false);
|
).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('platform public gallery flow resolves recommend runtime auto-start gates', () => {
|
||||||
|
const entry = buildTypedEntry('big-fish');
|
||||||
|
const baseInput: Parameters<
|
||||||
|
typeof resolvePlatformRecommendRuntimeAutoStartDecision
|
||||||
|
>[0] = {
|
||||||
|
isDesktopLayout: false,
|
||||||
|
selectionStage: 'platform',
|
||||||
|
platformTab: 'home',
|
||||||
|
isLoadingPlatform: false,
|
||||||
|
entries: [entry],
|
||||||
|
activeEntryKey: null,
|
||||||
|
isStarting: false,
|
||||||
|
readyState: { activeKind: null },
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
resolvePlatformRecommendRuntimeAutoStartDecision({
|
||||||
|
...baseInput,
|
||||||
|
isDesktopLayout: true,
|
||||||
|
}),
|
||||||
|
).toEqual({ type: 'noop' });
|
||||||
|
expect(
|
||||||
|
resolvePlatformRecommendRuntimeAutoStartDecision({
|
||||||
|
...baseInput,
|
||||||
|
platformTab: 'discover',
|
||||||
|
}),
|
||||||
|
).toEqual({ type: 'noop' });
|
||||||
|
expect(
|
||||||
|
resolvePlatformRecommendRuntimeAutoStartDecision({
|
||||||
|
...baseInput,
|
||||||
|
entries: [],
|
||||||
|
}),
|
||||||
|
).toEqual({ type: 'clear' });
|
||||||
|
expect(
|
||||||
|
resolvePlatformRecommendRuntimeAutoStartDecision({
|
||||||
|
...baseInput,
|
||||||
|
isStarting: true,
|
||||||
|
}),
|
||||||
|
).toEqual({ type: 'noop' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('platform public gallery flow resolves recommend runtime auto-start target', () => {
|
||||||
|
const firstEntry = buildTypedEntry('big-fish', {
|
||||||
|
profileId: 'big-fish-first',
|
||||||
|
});
|
||||||
|
const activeEntry = buildTypedEntry('puzzle', {
|
||||||
|
profileId: 'puzzle-active',
|
||||||
|
});
|
||||||
|
const activeEntryKey = getPlatformPublicGalleryEntryKey(activeEntry);
|
||||||
|
const baseInput: Parameters<
|
||||||
|
typeof resolvePlatformRecommendRuntimeAutoStartDecision
|
||||||
|
>[0] = {
|
||||||
|
isDesktopLayout: false,
|
||||||
|
selectionStage: 'platform',
|
||||||
|
platformTab: 'home',
|
||||||
|
isLoadingPlatform: false,
|
||||||
|
entries: [firstEntry, activeEntry],
|
||||||
|
activeEntryKey,
|
||||||
|
isStarting: false,
|
||||||
|
readyState: { activeKind: 'puzzle' },
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
resolvePlatformRecommendRuntimeAutoStartDecision({
|
||||||
|
...baseInput,
|
||||||
|
readyState: {
|
||||||
|
activeKind: 'puzzle',
|
||||||
|
puzzleRunEntryProfileId: 'puzzle-active',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toEqual({ type: 'noop' });
|
||||||
|
expect(resolvePlatformRecommendRuntimeAutoStartDecision(baseInput)).toEqual({
|
||||||
|
type: 'start',
|
||||||
|
entry: activeEntry,
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
resolvePlatformRecommendRuntimeAutoStartDecision({
|
||||||
|
...baseInput,
|
||||||
|
activeEntryKey: 'missing-entry',
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
type: 'start',
|
||||||
|
entry: firstEntry,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('platform public gallery flow merges duplicate identities and sorts newest first', () => {
|
test('platform public gallery flow merges duplicate identities and sorts newest first', () => {
|
||||||
const staleRpgEntry = buildRpgEntry({
|
const staleRpgEntry = buildRpgEntry({
|
||||||
profileId: 'shared-rpg',
|
profileId: 'shared-rpg',
|
||||||
|
|||||||
@@ -140,6 +140,22 @@ export type PlatformRecommendRuntimeReadyState = {
|
|||||||
puzzleRunCurrentLevelProfileId?: string | null;
|
puzzleRunCurrentLevelProfileId?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PlatformRecommendRuntimeAutoStartDecision =
|
||||||
|
| { type: 'noop' }
|
||||||
|
| { type: 'clear' }
|
||||||
|
| { type: 'start'; entry: PlatformPublicGalleryCard };
|
||||||
|
|
||||||
|
export type PlatformRecommendRuntimeAutoStartInput = {
|
||||||
|
isDesktopLayout: boolean;
|
||||||
|
selectionStage: string;
|
||||||
|
platformTab: string;
|
||||||
|
isLoadingPlatform: boolean;
|
||||||
|
entries: readonly PlatformPublicGalleryCard[];
|
||||||
|
activeEntryKey: string | null;
|
||||||
|
isStarting: boolean;
|
||||||
|
readyState: PlatformRecommendRuntimeReadyState;
|
||||||
|
};
|
||||||
|
|
||||||
export type PlatformPublicGalleryFeedsInput = {
|
export type PlatformPublicGalleryFeedsInput = {
|
||||||
rpgEntries: readonly CustomWorldGalleryCard[];
|
rpgEntries: readonly CustomWorldGalleryCard[];
|
||||||
bigFishEntries: readonly BigFishWorkSummary[];
|
bigFishEntries: readonly BigFishWorkSummary[];
|
||||||
@@ -423,6 +439,40 @@ export function isPlatformRecommendRuntimeReadyForEntry(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolvePlatformRecommendRuntimeAutoStartDecision(
|
||||||
|
input: PlatformRecommendRuntimeAutoStartInput,
|
||||||
|
): PlatformRecommendRuntimeAutoStartDecision {
|
||||||
|
if (
|
||||||
|
input.isDesktopLayout ||
|
||||||
|
input.selectionStage !== 'platform' ||
|
||||||
|
input.platformTab !== 'home' ||
|
||||||
|
input.isLoadingPlatform
|
||||||
|
) {
|
||||||
|
return { type: 'noop' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.entries.length === 0) {
|
||||||
|
return { type: 'clear' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeEntry = input.activeEntryKey
|
||||||
|
? (input.entries.find(
|
||||||
|
(entry) =>
|
||||||
|
getPlatformPublicGalleryEntryKey(entry) === input.activeEntryKey,
|
||||||
|
) ?? null)
|
||||||
|
: null;
|
||||||
|
const isActiveRuntimeReady =
|
||||||
|
activeEntry !== null &&
|
||||||
|
isPlatformRecommendRuntimeReadyForEntry(activeEntry, input.readyState);
|
||||||
|
|
||||||
|
if ((activeEntry !== null && isActiveRuntimeReady) || input.isStarting) {
|
||||||
|
return { type: 'noop' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextEntry = activeEntry ?? input.entries[0];
|
||||||
|
return nextEntry ? { type: 'start', entry: nextEntry } : { type: 'clear' };
|
||||||
|
}
|
||||||
|
|
||||||
export function isSamePlatformPublicGalleryEntry(
|
export function isSamePlatformPublicGalleryEntry(
|
||||||
left: PlatformPublicGalleryCard,
|
left: PlatformPublicGalleryCard,
|
||||||
right: PlatformPublicGalleryCard,
|
right: PlatformPublicGalleryCard,
|
||||||
|
|||||||
Reference in New Issue
Block a user