feat: complete bark battle playable demo

This commit is contained in:
2026-05-12 14:42:58 +08:00
parent 22810245f5
commit 33c9079d3b
16 changed files with 639 additions and 196 deletions

View File

@@ -2,9 +2,9 @@ import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/
import type { CustomWorldWorkSummary } from '../../../packages/shared/src/contracts/customWorldAgent';
import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks';
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
import type { CustomWorldLibraryEntry } 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 { CustomWorldLibraryEntry } from '../../../packages/shared/src/contracts/runtime';
import { buildPublicWorkStagePath } from '../../routing/appPageRoutes';
import {
buildBigFishPublicWorkCode,
@@ -79,6 +79,12 @@ export type CreationWorkShelfSource =
item: VisualNovelWorkSummary;
};
export type CreationWorkShelfActions = {
open: () => void;
delete?: () => void;
claimPointIncentive?: () => void;
};
export type CreationWorkShelfItem = {
id: string;
kind: CreationWorkShelfKind;
@@ -97,6 +103,7 @@ export type CreationWorkShelfItem = {
badges: CreationWorkShelfBadge[];
metrics: CreationWorkShelfMetric[];
pointIncentive?: CreationWorkShelfPointIncentive;
actions: CreationWorkShelfActions;
source: CreationWorkShelfSource;
};
@@ -114,6 +121,20 @@ export function buildCreationWorkShelfItems(params: {
canDeleteSquareHole?: boolean;
canDeletePuzzle?: boolean;
canDeleteVisualNovel?: boolean;
onOpenRpgDraft?: (item: CustomWorldWorkSummary) => void;
onEnterRpgPublished?: (profileId: string) => void;
onDeleteRpg?: (item: CustomWorldWorkSummary) => void;
onOpenBigFishDetail?: (item: BigFishWorkSummary) => void;
onDeleteBigFish?: (item: BigFishWorkSummary) => void;
onOpenMatch3DDetail?: (item: Match3DWorkSummary) => void;
onDeleteMatch3D?: (item: Match3DWorkSummary) => void;
onOpenSquareHoleDetail?: (item: SquareHoleWorkSummary) => void;
onDeleteSquareHole?: (item: SquareHoleWorkSummary) => void;
onOpenPuzzleDetail?: (item: PuzzleWorkSummary) => void;
onDeletePuzzle?: (item: PuzzleWorkSummary) => void;
onClaimPuzzlePointIncentive?: (item: PuzzleWorkSummary) => void;
onOpenVisualNovelDetail?: (item: VisualNovelWorkSummary) => void;
onDeleteVisualNovel?: (item: VisualNovelWorkSummary) => void;
}) {
const {
rpgItems,
@@ -129,26 +150,60 @@ export function buildCreationWorkShelfItems(params: {
canDeleteSquareHole = false,
canDeletePuzzle = false,
canDeleteVisualNovel = false,
onOpenRpgDraft,
onEnterRpgPublished,
onDeleteRpg,
onOpenBigFishDetail,
onDeleteBigFish,
onOpenMatch3DDetail,
onDeleteMatch3D,
onOpenSquareHoleDetail,
onDeleteSquareHole,
onOpenPuzzleDetail,
onDeletePuzzle,
onClaimPuzzlePointIncentive,
onOpenVisualNovelDetail,
onDeleteVisualNovel,
} = params;
return [
...rpgItems.map((item) =>
mapRpgWorkToShelfItem(item, canDeleteRpg, rpgLibraryEntries),
mapRpgWorkToShelfItem(item, canDeleteRpg, rpgLibraryEntries, {
onOpenDraft: onOpenRpgDraft,
onEnterPublished: onEnterRpgPublished,
onDelete: onDeleteRpg,
}),
),
...bigFishItems.map((item) =>
mapBigFishWorkToShelfItem(item, canDeleteBigFish),
mapBigFishWorkToShelfItem(item, canDeleteBigFish, {
onOpen: onOpenBigFishDetail,
onDelete: onDeleteBigFish,
}),
),
...match3dItems.map((item) =>
mapMatch3DWorkToShelfItem(item, canDeleteMatch3D),
mapMatch3DWorkToShelfItem(item, canDeleteMatch3D, {
onOpen: onOpenMatch3DDetail,
onDelete: onDeleteMatch3D,
}),
),
...squareHoleItems.map((item) =>
mapSquareHoleWorkToShelfItem(item, canDeleteSquareHole),
mapSquareHoleWorkToShelfItem(item, canDeleteSquareHole, {
onOpen: onOpenSquareHoleDetail,
onDelete: onDeleteSquareHole,
}),
),
...puzzleItems.map((item) =>
mapPuzzleWorkToShelfItem(item, canDeletePuzzle),
mapPuzzleWorkToShelfItem(item, canDeletePuzzle, {
onOpen: onOpenPuzzleDetail,
onDelete: onDeletePuzzle,
onClaimPointIncentive: onClaimPuzzlePointIncentive,
}),
),
...visualNovelItems.map((item) =>
mapVisualNovelWorkToShelfItem(item, canDeleteVisualNovel),
mapVisualNovelWorkToShelfItem(item, canDeleteVisualNovel, {
onOpen: onOpenVisualNovelDetail,
onDelete: onDeleteVisualNovel,
}),
),
].sort(
(left, right) =>
@@ -156,10 +211,26 @@ export function buildCreationWorkShelfItems(params: {
);
}
type RpgWorkShelfAdapter = {
onOpenDraft?: (item: CustomWorldWorkSummary) => void;
onEnterPublished?: (profileId: string) => void;
onDelete?: (item: CustomWorldWorkSummary) => void;
};
type WorkShelfAdapter<TItem> = {
onOpen?: (item: TItem) => void;
onDelete?: (item: TItem) => void;
};
type PuzzleWorkShelfAdapter = WorkShelfAdapter<PuzzleWorkSummary> & {
onClaimPointIncentive?: (item: PuzzleWorkSummary) => void;
};
function mapRpgWorkToShelfItem(
item: CustomWorldWorkSummary,
canDelete: boolean,
libraryEntries: CustomWorldLibraryEntry<CustomWorldProfile>[],
adapter: RpgWorkShelfAdapter,
): CreationWorkShelfItem {
const isDraft = item.status === 'draft';
const libraryEntry = item.profileId
@@ -200,6 +271,7 @@ function mapRpgWorkToShelfItem(
: '查看详情',
canDelete,
canShare: item.status === 'published' && Boolean(publicWorkCode),
actions: buildRpgWorkShelfActions(item, adapter),
badges,
metrics: isDraft ? [] : metrics,
source: { kind: 'rpg', item },
@@ -209,6 +281,7 @@ function mapRpgWorkToShelfItem(
function mapBigFishWorkToShelfItem(
item: BigFishWorkSummary,
canDelete: boolean,
adapter: WorkShelfAdapter<BigFishWorkSummary>,
): CreationWorkShelfItem {
const isPublished = item.status === 'published';
const publicWorkCode = isPublished
@@ -233,6 +306,7 @@ function mapBigFishWorkToShelfItem(
openActionLabel: item.status === 'draft' ? '继续创作' : '查看详情',
canDelete,
canShare: isPublished && Boolean(publicWorkCode),
actions: buildWorkShelfActions(item, adapter),
badges: [
buildStatusBadge(item.status),
{ id: 'type', label: '大鱼', tone: 'neutral' },
@@ -251,6 +325,7 @@ function mapBigFishWorkToShelfItem(
function mapMatch3DWorkToShelfItem(
item: Match3DWorkSummary,
canDelete: boolean,
adapter: WorkShelfAdapter<Match3DWorkSummary>,
): CreationWorkShelfItem {
const status = item.publicationStatus === 'published' ? 'published' : 'draft';
const publicWorkCode =
@@ -274,6 +349,7 @@ function mapMatch3DWorkToShelfItem(
openActionLabel: status === 'published' ? '查看详情' : '继续创作',
canDelete,
canShare: status === 'published' && Boolean(publicWorkCode),
actions: buildWorkShelfActions(item, adapter),
badges: [
buildStatusBadge(status),
{ id: 'type', label: '抓鹅', tone: 'neutral' },
@@ -293,6 +369,7 @@ function mapMatch3DWorkToShelfItem(
function mapPuzzleWorkToShelfItem(
item: PuzzleWorkSummary,
canDelete: boolean,
adapter: PuzzleWorkShelfAdapter,
): CreationWorkShelfItem {
const status = item.publicationStatus;
const publicWorkCode =
@@ -320,6 +397,7 @@ function mapPuzzleWorkToShelfItem(
status === 'published' && !item.sourceSessionId ? '查看详情' : '继续创作',
canDelete,
canShare: status === 'published' && Boolean(publicWorkCode),
actions: buildPuzzleWorkShelfActions(item, adapter),
badges: [
buildStatusBadge(status),
{ id: 'type', label: '拼图', tone: 'neutral' },
@@ -354,6 +432,7 @@ function mapPuzzleWorkToShelfItem(
function mapVisualNovelWorkToShelfItem(
item: VisualNovelWorkSummary,
canDelete: boolean,
adapter: WorkShelfAdapter<VisualNovelWorkSummary>,
): CreationWorkShelfItem {
const status =
item.publishStatus === 'published' ? 'published' : 'draft';
@@ -394,6 +473,7 @@ function mapVisualNovelWorkToShelfItem(
likeCount: 0,
})
: [],
actions: buildWorkShelfActions(item, adapter),
source: { kind: 'visual-novel', item },
};
}
@@ -401,6 +481,7 @@ function mapVisualNovelWorkToShelfItem(
function mapSquareHoleWorkToShelfItem(
item: SquareHoleWorkSummary,
canDelete: boolean,
adapter: WorkShelfAdapter<SquareHoleWorkSummary>,
): CreationWorkShelfItem {
const status = item.publicationStatus === 'published' ? 'published' : 'draft';
const publicWorkCode =
@@ -438,10 +519,65 @@ function mapSquareHoleWorkToShelfItem(
likeCount: 0,
})
: [],
actions: buildWorkShelfActions(item, adapter),
source: { kind: 'square-hole', item },
};
}
function buildWorkShelfActions<TItem>(
item: TItem,
adapter: WorkShelfAdapter<TItem>,
): CreationWorkShelfActions {
return {
open: () => {
adapter.onOpen?.(item);
},
delete: adapter.onDelete
? () => {
adapter.onDelete?.(item);
}
: undefined,
};
}
function buildPuzzleWorkShelfActions(
item: PuzzleWorkSummary,
adapter: PuzzleWorkShelfAdapter,
): CreationWorkShelfActions {
return {
...buildWorkShelfActions(item, adapter),
claimPointIncentive: adapter.onClaimPointIncentive
? () => {
adapter.onClaimPointIncentive?.(item);
}
: undefined,
};
}
function buildRpgWorkShelfActions(
item: CustomWorldWorkSummary,
adapter: RpgWorkShelfAdapter,
): CreationWorkShelfActions {
return {
open: () => {
if (item.status === 'draft') {
adapter.onOpenDraft?.(item);
return;
}
if (item.profileId) {
adapter.onEnterPublished?.(item.profileId);
}
},
delete: adapter.onDelete
? () => {
adapter.onDelete?.(item);
}
: undefined,
};
}
function buildPublishedMetrics(params: {
playCount?: number | null;
remixCount?: number | null;