fix: polish bark battle creation flow
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle';
|
||||
import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
|
||||
import type { CustomWorldWorkSummary } from '../../../packages/shared/src/contracts/customWorldAgent';
|
||||
import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject';
|
||||
@@ -9,6 +10,7 @@ import type { VisualNovelWorkSummary } from '../../../packages/shared/src/contra
|
||||
import { buildPublicWorkStagePath } from '../../routing/appPageRoutes';
|
||||
import {
|
||||
buildBabyObjectMatchPublicWorkCode,
|
||||
buildBarkBattlePublicWorkCode,
|
||||
buildBigFishPublicWorkCode,
|
||||
buildMatch3DPublicWorkCode,
|
||||
buildPuzzlePublicWorkCode,
|
||||
@@ -19,6 +21,9 @@ import type { CustomWorldProfile } from '../../types';
|
||||
|
||||
const MATCH3D_CONTAINER_REFERENCE_COVER_SRC =
|
||||
'/match3d-background-references/pot-fused-reference.png';
|
||||
const BARK_BATTLE_REFERENCE_COVER_SRC =
|
||||
'/creation-type-references/bark-battle.webp';
|
||||
const DEFAULT_CREATION_WORK_AUTHOR = '玩家';
|
||||
|
||||
export type CreationWorkShelfKind =
|
||||
| 'rpg'
|
||||
@@ -27,6 +32,7 @@ export type CreationWorkShelfKind =
|
||||
| 'square-hole'
|
||||
| 'puzzle'
|
||||
| 'baby-object-match'
|
||||
| 'bark-battle'
|
||||
| 'visual-novel';
|
||||
export type CreationWorkShelfStatus = 'draft' | 'published';
|
||||
|
||||
@@ -84,6 +90,10 @@ export type CreationWorkShelfSource =
|
||||
kind: 'visual-novel';
|
||||
item: VisualNovelWorkSummary;
|
||||
}
|
||||
| {
|
||||
kind: 'bark-battle';
|
||||
item: BarkBattleWorkSummary;
|
||||
}
|
||||
| {
|
||||
kind: 'baby-object-match';
|
||||
item: BabyObjectMatchDraft;
|
||||
@@ -103,6 +113,7 @@ export type CreationWorkShelfItem = {
|
||||
hasUnreadUpdate?: boolean;
|
||||
title: string;
|
||||
summary: string;
|
||||
authorDisplayName: string;
|
||||
updatedAt: string;
|
||||
coverImageSrc: string | null;
|
||||
coverRenderMode: 'image' | 'scene_with_roles';
|
||||
@@ -127,6 +138,7 @@ export function buildCreationWorkShelfItems(params: {
|
||||
squareHoleItems?: SquareHoleWorkSummary[];
|
||||
puzzleItems: PuzzleWorkSummary[];
|
||||
babyObjectMatchItems?: BabyObjectMatchDraft[];
|
||||
barkBattleItems?: BarkBattleWorkSummary[];
|
||||
visualNovelItems?: VisualNovelWorkSummary[];
|
||||
canDeleteRpg?: boolean;
|
||||
canDeleteBigFish?: boolean;
|
||||
@@ -134,6 +146,7 @@ export function buildCreationWorkShelfItems(params: {
|
||||
canDeleteSquareHole?: boolean;
|
||||
canDeletePuzzle?: boolean;
|
||||
canDeleteBabyObjectMatch?: boolean;
|
||||
canDeleteBarkBattle?: boolean;
|
||||
canDeleteVisualNovel?: boolean;
|
||||
onOpenRpgDraft?: (item: CustomWorldWorkSummary) => void;
|
||||
onEnterRpgPublished?: (profileId: string) => void;
|
||||
@@ -149,6 +162,8 @@ export function buildCreationWorkShelfItems(params: {
|
||||
onClaimPuzzlePointIncentive?: (item: PuzzleWorkSummary) => void;
|
||||
onOpenBabyObjectMatchDetail?: (item: BabyObjectMatchDraft) => void;
|
||||
onDeleteBabyObjectMatch?: (item: BabyObjectMatchDraft) => void;
|
||||
onOpenBarkBattleDetail?: (item: BarkBattleWorkSummary) => void;
|
||||
onDeleteBarkBattle?: (item: BarkBattleWorkSummary) => void;
|
||||
onOpenVisualNovelDetail?: (item: VisualNovelWorkSummary) => void;
|
||||
onDeleteVisualNovel?: (item: VisualNovelWorkSummary) => void;
|
||||
getItemState?: (
|
||||
@@ -163,6 +178,7 @@ export function buildCreationWorkShelfItems(params: {
|
||||
squareHoleItems = [],
|
||||
puzzleItems,
|
||||
babyObjectMatchItems = [],
|
||||
barkBattleItems = [],
|
||||
visualNovelItems = [],
|
||||
canDeleteRpg = false,
|
||||
canDeleteBigFish = false,
|
||||
@@ -170,6 +186,7 @@ export function buildCreationWorkShelfItems(params: {
|
||||
canDeleteSquareHole = false,
|
||||
canDeletePuzzle = false,
|
||||
canDeleteBabyObjectMatch = false,
|
||||
canDeleteBarkBattle = false,
|
||||
canDeleteVisualNovel = false,
|
||||
onOpenRpgDraft,
|
||||
onEnterRpgPublished,
|
||||
@@ -185,6 +202,8 @@ export function buildCreationWorkShelfItems(params: {
|
||||
onClaimPuzzlePointIncentive,
|
||||
onOpenBabyObjectMatchDetail,
|
||||
onDeleteBabyObjectMatch,
|
||||
onOpenBarkBattleDetail,
|
||||
onDeleteBarkBattle,
|
||||
onOpenVisualNovelDetail,
|
||||
onDeleteVisualNovel,
|
||||
getItemState,
|
||||
@@ -229,6 +248,12 @@ export function buildCreationWorkShelfItems(params: {
|
||||
onDelete: onDeleteBabyObjectMatch,
|
||||
}),
|
||||
),
|
||||
...mergeBarkBattleShelfSourceItems(barkBattleItems).map((item) =>
|
||||
mapBarkBattleWorkToShelfItem(item, canDeleteBarkBattle, {
|
||||
onOpen: onOpenBarkBattleDetail,
|
||||
onDelete: onDeleteBarkBattle,
|
||||
}),
|
||||
),
|
||||
...visualNovelItems.map((item) =>
|
||||
mapVisualNovelWorkToShelfItem(item, canDeleteVisualNovel, {
|
||||
onOpen: onOpenVisualNovelDetail,
|
||||
@@ -259,6 +284,28 @@ export function buildCreationWorkShelfItems(params: {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function mergeBarkBattleShelfSourceItems(
|
||||
items: readonly BarkBattleWorkSummary[],
|
||||
): BarkBattleWorkSummary[] {
|
||||
const byWorkId = new Map<string, BarkBattleWorkSummary>();
|
||||
for (const item of items) {
|
||||
const current = byWorkId.get(item.workId);
|
||||
if (!current) {
|
||||
byWorkId.set(item.workId, item);
|
||||
continue;
|
||||
}
|
||||
if (current.status !== 'published' && item.status === 'published') {
|
||||
byWorkId.set(item.workId, { ...current, ...item });
|
||||
continue;
|
||||
}
|
||||
if (current.status === item.status) {
|
||||
byWorkId.set(item.workId, { ...current, ...item });
|
||||
}
|
||||
}
|
||||
return Array.from(byWorkId.values());
|
||||
}
|
||||
|
||||
type RpgWorkShelfAdapter = {
|
||||
onOpenDraft?: (item: CustomWorldWorkSummary) => void;
|
||||
onEnterPublished?: (profileId: string) => void;
|
||||
@@ -303,6 +350,7 @@ function mapRpgWorkToShelfItem(
|
||||
status: item.status,
|
||||
title: item.title,
|
||||
summary: item.summary,
|
||||
authorDisplayName: resolveAuthorDisplayName(item, libraryEntry),
|
||||
updatedAt: item.updatedAt,
|
||||
coverImageSrc: item.coverImageSrc ?? null,
|
||||
coverRenderMode: item.coverRenderMode ?? 'image',
|
||||
@@ -342,6 +390,7 @@ function mapBigFishWorkToShelfItem(
|
||||
status: item.status,
|
||||
title: item.title,
|
||||
summary: item.summary,
|
||||
authorDisplayName: resolveAuthorDisplayName(item),
|
||||
updatedAt: item.updatedAt,
|
||||
coverImageSrc: item.coverImageSrc ?? null,
|
||||
coverRenderMode: 'image',
|
||||
@@ -386,6 +435,7 @@ function mapMatch3DWorkToShelfItem(
|
||||
status,
|
||||
title: item.gameName,
|
||||
summary: item.summary,
|
||||
authorDisplayName: resolveAuthorDisplayName(item),
|
||||
updatedAt: item.updatedAt,
|
||||
coverImageSrc,
|
||||
coverRenderMode: 'image',
|
||||
@@ -434,6 +484,7 @@ function mapPuzzleWorkToShelfItem(
|
||||
item.workDescription?.trim() ||
|
||||
item.summary.trim() ||
|
||||
(status === 'draft' ? '未填写作品描述' : ''),
|
||||
authorDisplayName: resolveAuthorDisplayName(item),
|
||||
updatedAt: item.updatedAt,
|
||||
coverImageSrc,
|
||||
coverRenderMode: 'image',
|
||||
@@ -500,6 +551,7 @@ function mapBabyObjectMatchDraftToShelfItem(
|
||||
summary:
|
||||
item.workDescription.trim() ||
|
||||
`${item.itemNames[0]}和${item.itemNames[1]}识物分类`,
|
||||
authorDisplayName: resolveAuthorDisplayName(item),
|
||||
updatedAt: item.updatedAt,
|
||||
coverImageSrc,
|
||||
coverRenderMode: 'image',
|
||||
@@ -549,6 +601,7 @@ function mapVisualNovelWorkToShelfItem(
|
||||
status,
|
||||
title,
|
||||
summary,
|
||||
authorDisplayName: resolveAuthorDisplayName(item),
|
||||
updatedAt: item.updatedAt,
|
||||
coverImageSrc: item.coverImageSrc ?? null,
|
||||
coverRenderMode: 'image',
|
||||
@@ -578,6 +631,72 @@ function mapVisualNovelWorkToShelfItem(
|
||||
};
|
||||
}
|
||||
|
||||
function mapBarkBattleWorkToShelfItem(
|
||||
item: BarkBattleWorkSummary,
|
||||
canDelete: boolean,
|
||||
adapter: WorkShelfAdapter<BarkBattleWorkSummary>,
|
||||
): CreationWorkShelfItem {
|
||||
const status = item.status;
|
||||
const publicWorkCode =
|
||||
status === 'published' ? buildBarkBattlePublicWorkCode(item.workId) : null;
|
||||
const playerCharacterImageSrc = normalizeCoverImageSrc(
|
||||
item.playerCharacterImageSrc,
|
||||
);
|
||||
const opponentCharacterImageSrc = normalizeCoverImageSrc(
|
||||
item.opponentCharacterImageSrc,
|
||||
);
|
||||
const coverImageSrc =
|
||||
normalizeCoverImageSrc(item.uiBackgroundImageSrc) ??
|
||||
playerCharacterImageSrc ??
|
||||
opponentCharacterImageSrc ??
|
||||
BARK_BATTLE_REFERENCE_COVER_SRC;
|
||||
const coverCharacterImageSrcs = [
|
||||
playerCharacterImageSrc,
|
||||
opponentCharacterImageSrc,
|
||||
].filter((imageSrc): imageSrc is string => Boolean(imageSrc));
|
||||
const canRenderSceneWithRoles =
|
||||
Boolean(normalizeCoverImageSrc(item.uiBackgroundImageSrc)) &&
|
||||
coverCharacterImageSrcs.length >= 2;
|
||||
|
||||
return {
|
||||
id: item.workId,
|
||||
kind: 'bark-battle',
|
||||
status,
|
||||
title: item.title.trim() || '汪汪声浪大作战',
|
||||
summary:
|
||||
item.summary.trim() ||
|
||||
item.themeDescription.trim() ||
|
||||
(status === 'draft' ? '未填写作品描述' : ''),
|
||||
authorDisplayName: resolveAuthorDisplayName(item),
|
||||
updatedAt: item.updatedAt,
|
||||
coverImageSrc,
|
||||
coverRenderMode: canRenderSceneWithRoles ? 'scene_with_roles' : 'image',
|
||||
coverCharacterImageSrcs,
|
||||
publicWorkCode,
|
||||
sharePath:
|
||||
publicWorkCode && status === 'published'
|
||||
? buildPublicWorkStagePath('work-detail', publicWorkCode)
|
||||
: null,
|
||||
openActionLabel: status === 'published' ? '查看详情' : '继续创作',
|
||||
canDelete,
|
||||
canShare: status === 'published' && Boolean(publicWorkCode),
|
||||
badges: [
|
||||
buildStatusBadge(status),
|
||||
{ id: 'type', label: '汪汪', tone: 'neutral' },
|
||||
],
|
||||
metrics:
|
||||
status === 'published'
|
||||
? buildPublishedMetrics({
|
||||
playCount: item.playCount,
|
||||
remixCount: 0,
|
||||
likeCount: 0,
|
||||
})
|
||||
: [],
|
||||
actions: buildWorkShelfActions(item, adapter),
|
||||
source: { kind: 'bark-battle', item },
|
||||
};
|
||||
}
|
||||
|
||||
function mapSquareHoleWorkToShelfItem(
|
||||
item: SquareHoleWorkSummary,
|
||||
canDelete: boolean,
|
||||
@@ -596,6 +715,7 @@ function mapSquareHoleWorkToShelfItem(
|
||||
status,
|
||||
title: item.gameName,
|
||||
summary: item.summary,
|
||||
authorDisplayName: resolveAuthorDisplayName(item),
|
||||
updatedAt: item.updatedAt,
|
||||
coverImageSrc,
|
||||
coverRenderMode: 'image',
|
||||
@@ -625,6 +745,26 @@ function mapSquareHoleWorkToShelfItem(
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function resolveAuthorDisplayName(
|
||||
...sources: Array<unknown>
|
||||
) {
|
||||
for (const source of sources) {
|
||||
const authorDisplayName =
|
||||
source &&
|
||||
typeof source === 'object' &&
|
||||
'authorDisplayName' in source &&
|
||||
typeof source.authorDisplayName === 'string'
|
||||
? source.authorDisplayName.trim()
|
||||
: '';
|
||||
if (authorDisplayName) {
|
||||
return authorDisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
return DEFAULT_CREATION_WORK_AUTHOR;
|
||||
}
|
||||
|
||||
function normalizeCoverImageSrc(value?: string | null) {
|
||||
return value?.trim() || null;
|
||||
}
|
||||
@@ -816,11 +956,34 @@ function isPersistedCreationWorkGenerating(item: CreationWorkShelfItem) {
|
||||
return item.source.item.generationStatus === 'generating';
|
||||
case 'puzzle':
|
||||
return isPersistedPuzzleDraftGenerating(item.source.item);
|
||||
case 'bark-battle':
|
||||
return isPersistedBarkBattleDraftGenerating(item.source.item);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isPersistedBarkBattleDraftGenerating(
|
||||
item: BarkBattleWorkSummary,
|
||||
) {
|
||||
if (item.status === 'published') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
item.generationStatus === 'pending_assets' ||
|
||||
!hasBarkBattleRequiredImages(item)
|
||||
);
|
||||
}
|
||||
|
||||
export function hasBarkBattleRequiredImages(item: BarkBattleWorkSummary) {
|
||||
return Boolean(
|
||||
normalizeCoverImageSrc(item.playerCharacterImageSrc) &&
|
||||
normalizeCoverImageSrc(item.opponentCharacterImageSrc) &&
|
||||
normalizeCoverImageSrc(item.uiBackgroundImageSrc),
|
||||
);
|
||||
}
|
||||
|
||||
export function isPersistedPuzzleDraftGenerating(item: PuzzleWorkSummary) {
|
||||
if (item.generationStatus !== 'generating') {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user