314 lines
9.4 KiB
TypeScript
314 lines
9.4 KiB
TypeScript
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,
|
|
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 { WoodenFishGalleryCardResponse } from '../../../packages/shared/src/contracts/woodenFish';
|
|
import {
|
|
isSameBabyObjectMatchPublicWorkCode,
|
|
isSameBarkBattlePublicWorkCode,
|
|
isSameBigFishPublicWorkCode,
|
|
isSameJumpHopPublicWorkCode,
|
|
isSameMatch3DPublicWorkCode,
|
|
isSamePuzzlePublicWorkCode,
|
|
isSameSquareHolePublicWorkCode,
|
|
isSameVisualNovelPublicWorkCode,
|
|
isSameWoodenFishPublicWorkCode,
|
|
} from '../../services/publicWorkCode';
|
|
import type { CustomWorldProfile } from '../../types';
|
|
import type { PlatformPublicGalleryCard } from '../rpg-entry/rpgEntryWorldPresentation';
|
|
import { mapBabyObjectMatchDraftToPlatformGalleryCard } from '../rpg-entry/rpgEntryWorldPresentation';
|
|
import { canExposePublicWork } from './platformEdutainmentVisibility';
|
|
import { mapMatch3DWorkToPublicWorkDetail } from './platformMatch3DRuntimeProfile';
|
|
import {
|
|
mapBarkBattleWorkToPublicWorkDetail,
|
|
mapBigFishWorkToPublicWorkDetail,
|
|
mapJumpHopWorkToPublicWorkDetail,
|
|
mapPuzzleWorkToPublicWorkDetail,
|
|
mapSquareHoleWorkToPublicWorkDetail,
|
|
mapVisualNovelWorkToPublicWorkDetail,
|
|
mapWoodenFishWorkToPublicWorkDetail,
|
|
} from './platformPublicWorkDetailFlow';
|
|
|
|
export type PlatformPublicCodeSearchStep =
|
|
| 'user-id'
|
|
| 'public-user-code'
|
|
| 'rpg-work'
|
|
| 'puzzle-work'
|
|
| 'big-fish-work'
|
|
| 'jump-hop-work'
|
|
| 'wooden-fish-work'
|
|
| 'baby-object-match-work'
|
|
| 'match3d-work'
|
|
| 'square-hole-work'
|
|
| 'visual-novel-work'
|
|
| 'bark-battle-work';
|
|
|
|
export type PlatformPublicCodeSearchPlan = {
|
|
normalizedKeyword: string;
|
|
steps: readonly PlatformPublicCodeSearchStep[];
|
|
};
|
|
|
|
export type PlatformPublicCodeSearchMatch<TItem> = {
|
|
item: TItem;
|
|
detail: PlatformPublicGalleryCard;
|
|
};
|
|
|
|
type PlatformPublicCodeSearchMatcherInput<TItem> = {
|
|
keyword: string;
|
|
entries: readonly TItem[];
|
|
mapEntry: (item: TItem) => PlatformPublicGalleryCard;
|
|
matchesEntry: (keyword: string, item: TItem) => boolean;
|
|
canExposeEntry?: (entry: PlatformPublicGalleryCard) => boolean;
|
|
};
|
|
|
|
const PLATFORM_PUBLIC_USER_ID_PATTERN = /^user[_-][a-z0-9_-]+$/iu;
|
|
const PLATFORM_RPG_WORK_NUMERIC_CODE_PATTERN = /^\d{1,8}$/u;
|
|
|
|
const DIRECT_WORK_PREFIX_STEPS: ReadonlyArray<
|
|
readonly [prefix: string, step: PlatformPublicCodeSearchStep]
|
|
> = [
|
|
['PZ', 'puzzle-work'],
|
|
['BF', 'big-fish-work'],
|
|
['JH', 'jump-hop-work'],
|
|
['WF', 'wooden-fish-work'],
|
|
['BO', 'baby-object-match-work'],
|
|
['M3', 'match3d-work'],
|
|
['SH', 'square-hole-work'],
|
|
['VN', 'visual-novel-work'],
|
|
['BB', 'bark-battle-work'],
|
|
];
|
|
|
|
/** 收口公开码搜索顺序,壳层只按步骤执行网络读取与打开副作用。 */
|
|
export function resolvePlatformPublicCodeSearchPlan(
|
|
keyword: string,
|
|
): PlatformPublicCodeSearchPlan | null {
|
|
const normalizedKeyword = keyword.trim();
|
|
if (!normalizedKeyword) {
|
|
return null;
|
|
}
|
|
|
|
if (PLATFORM_PUBLIC_USER_ID_PATTERN.test(normalizedKeyword)) {
|
|
return {
|
|
normalizedKeyword,
|
|
steps: ['user-id'],
|
|
};
|
|
}
|
|
|
|
const upperKeyword = normalizedKeyword.toUpperCase();
|
|
const directWorkStep = DIRECT_WORK_PREFIX_STEPS.find(([prefix]) =>
|
|
upperKeyword.startsWith(prefix),
|
|
)?.[1];
|
|
if (directWorkStep) {
|
|
return {
|
|
normalizedKeyword,
|
|
steps: [directWorkStep],
|
|
};
|
|
}
|
|
|
|
if (
|
|
upperKeyword.startsWith('CW') ||
|
|
PLATFORM_RPG_WORK_NUMERIC_CODE_PATTERN.test(normalizedKeyword)
|
|
) {
|
|
return {
|
|
normalizedKeyword,
|
|
steps: ['rpg-work', 'public-user-code'],
|
|
};
|
|
}
|
|
|
|
return {
|
|
normalizedKeyword,
|
|
steps: [
|
|
'public-user-code',
|
|
'rpg-work',
|
|
'bark-battle-work',
|
|
'public-user-code',
|
|
],
|
|
};
|
|
}
|
|
|
|
export function mapRpgPublicCodeSearchDetailToGalleryCard(
|
|
entry: CustomWorldLibraryEntry<CustomWorldProfile>,
|
|
): CustomWorldGalleryCard {
|
|
return {
|
|
ownerUserId: entry.ownerUserId,
|
|
profileId: entry.profileId,
|
|
publicWorkCode: entry.publicWorkCode,
|
|
authorPublicUserCode: entry.authorPublicUserCode,
|
|
visibility: 'published',
|
|
publishedAt: entry.publishedAt,
|
|
updatedAt: entry.updatedAt,
|
|
authorDisplayName: entry.authorDisplayName,
|
|
worldName: entry.worldName,
|
|
subtitle: entry.subtitle,
|
|
summaryText: entry.summaryText,
|
|
coverImageSrc: entry.coverImageSrc,
|
|
themeMode: entry.themeMode,
|
|
playableNpcCount: entry.playableNpcCount,
|
|
landmarkCount: entry.landmarkCount,
|
|
playCount: entry.playCount ?? 0,
|
|
remixCount: entry.remixCount ?? 0,
|
|
likeCount: entry.likeCount ?? 0,
|
|
};
|
|
}
|
|
|
|
export function resolvePuzzlePublicCodeSearchMatch(
|
|
entries: readonly PuzzleWorkSummary[],
|
|
keyword: string,
|
|
canExposeEntry = canExposePublicWork,
|
|
) {
|
|
return resolveMappedPublicCodeSearchMatch({
|
|
keyword,
|
|
entries,
|
|
mapEntry: mapPuzzleWorkToPublicWorkDetail,
|
|
matchesEntry: (searchKeyword, item) =>
|
|
isSamePuzzlePublicWorkCode(searchKeyword, item.profileId),
|
|
canExposeEntry,
|
|
});
|
|
}
|
|
|
|
export function resolveBigFishPublicCodeSearchMatch(
|
|
entries: readonly BigFishWorkSummary[],
|
|
keyword: string,
|
|
canExposeEntry = canExposePublicWork,
|
|
) {
|
|
return resolveMappedPublicCodeSearchMatch({
|
|
keyword,
|
|
entries,
|
|
mapEntry: mapBigFishWorkToPublicWorkDetail,
|
|
matchesEntry: (searchKeyword, item) =>
|
|
isSameBigFishPublicWorkCode(searchKeyword, item.sourceSessionId),
|
|
canExposeEntry,
|
|
});
|
|
}
|
|
|
|
export function resolveJumpHopPublicCodeSearchMatch(
|
|
entries: readonly JumpHopGalleryCardResponse[],
|
|
keyword: string,
|
|
canExposeEntry = canExposePublicWork,
|
|
) {
|
|
return resolveMappedPublicCodeSearchMatch({
|
|
keyword,
|
|
entries,
|
|
mapEntry: mapJumpHopWorkToPublicWorkDetail,
|
|
matchesEntry: (searchKeyword, item) =>
|
|
isSameJumpHopPublicWorkCode(searchKeyword, item.profileId),
|
|
canExposeEntry,
|
|
});
|
|
}
|
|
|
|
export function resolveWoodenFishPublicCodeSearchMatch(
|
|
entries: readonly WoodenFishGalleryCardResponse[],
|
|
keyword: string,
|
|
canExposeEntry = canExposePublicWork,
|
|
) {
|
|
return resolveMappedPublicCodeSearchMatch({
|
|
keyword,
|
|
entries,
|
|
mapEntry: mapWoodenFishWorkToPublicWorkDetail,
|
|
matchesEntry: (searchKeyword, item) =>
|
|
isSameWoodenFishPublicWorkCode(searchKeyword, item.profileId),
|
|
canExposeEntry,
|
|
});
|
|
}
|
|
|
|
export function resolveBabyObjectMatchPublicCodeSearchMatch(
|
|
entries: readonly BabyObjectMatchDraft[],
|
|
keyword: string,
|
|
canExposeEntry = canExposePublicWork,
|
|
) {
|
|
return resolveMappedPublicCodeSearchMatch({
|
|
keyword,
|
|
entries,
|
|
mapEntry: mapBabyObjectMatchDraftToPlatformGalleryCard,
|
|
matchesEntry: (searchKeyword, item) =>
|
|
isSameBabyObjectMatchPublicWorkCode(searchKeyword, item.profileId),
|
|
canExposeEntry,
|
|
});
|
|
}
|
|
|
|
export function resolveMatch3DPublicCodeSearchMatch(
|
|
entries: readonly Match3DWorkSummary[],
|
|
keyword: string,
|
|
canExposeEntry = canExposePublicWork,
|
|
) {
|
|
return resolveMappedPublicCodeSearchMatch({
|
|
keyword,
|
|
entries,
|
|
mapEntry: mapMatch3DWorkToPublicWorkDetail,
|
|
matchesEntry: (searchKeyword, item) =>
|
|
isSameMatch3DPublicWorkCode(searchKeyword, item.profileId),
|
|
canExposeEntry,
|
|
});
|
|
}
|
|
|
|
export function resolveSquareHolePublicCodeSearchMatch(
|
|
entries: readonly SquareHoleWorkSummary[],
|
|
keyword: string,
|
|
canExposeEntry = canExposePublicWork,
|
|
) {
|
|
return resolveMappedPublicCodeSearchMatch({
|
|
keyword,
|
|
entries,
|
|
mapEntry: mapSquareHoleWorkToPublicWorkDetail,
|
|
matchesEntry: (searchKeyword, item) =>
|
|
isSameSquareHolePublicWorkCode(searchKeyword, item.profileId),
|
|
canExposeEntry,
|
|
});
|
|
}
|
|
|
|
export function resolveVisualNovelPublicCodeSearchMatch(
|
|
entries: readonly VisualNovelWorkSummary[],
|
|
keyword: string,
|
|
canExposeEntry = canExposePublicWork,
|
|
) {
|
|
return resolveMappedPublicCodeSearchMatch({
|
|
keyword,
|
|
entries,
|
|
mapEntry: mapVisualNovelWorkToPublicWorkDetail,
|
|
matchesEntry: (searchKeyword, item) =>
|
|
isSameVisualNovelPublicWorkCode(searchKeyword, item.profileId),
|
|
canExposeEntry,
|
|
});
|
|
}
|
|
|
|
export function resolveBarkBattlePublicCodeSearchMatch(
|
|
entries: readonly BarkBattleWorkSummary[],
|
|
keyword: string,
|
|
canExposeEntry = canExposePublicWork,
|
|
) {
|
|
return resolveMappedPublicCodeSearchMatch({
|
|
keyword,
|
|
entries,
|
|
mapEntry: mapBarkBattleWorkToPublicWorkDetail,
|
|
matchesEntry: (searchKeyword, item) =>
|
|
isSameBarkBattlePublicWorkCode(searchKeyword, item.workId),
|
|
canExposeEntry,
|
|
});
|
|
}
|
|
|
|
function resolveMappedPublicCodeSearchMatch<TItem>({
|
|
keyword,
|
|
entries,
|
|
mapEntry,
|
|
matchesEntry,
|
|
canExposeEntry = canExposePublicWork,
|
|
}: PlatformPublicCodeSearchMatcherInput<TItem>):
|
|
| PlatformPublicCodeSearchMatch<TItem>
|
|
| null {
|
|
for (const item of entries) {
|
|
const detail = mapEntry(item);
|
|
if (canExposeEntry(detail) && matchesEntry(keyword, item)) {
|
|
return { item, detail };
|
|
}
|
|
}
|
|
return null;
|
|
}
|