285 lines
8.0 KiB
TypeScript
285 lines
8.0 KiB
TypeScript
import {
|
|
RUNTIME_BACKGROUND_AUTH_OPTIONS,
|
|
type RuntimeRequestOptions,
|
|
requestPublicRpgRuntimeJson,
|
|
requestRpgRuntimeJson,
|
|
} from '../rpg-runtime/rpgRuntimeRequest';
|
|
import type {
|
|
CustomWorldGalleryDetailResponse,
|
|
CustomWorldGalleryResponse,
|
|
CustomWorldLibraryEntry,
|
|
CustomWorldLibraryMutationResponse,
|
|
CustomWorldLibraryResponse,
|
|
} from '../../../packages/shared/src/contracts/runtime';
|
|
import { normalizeCustomWorldProfileRecord } from '../../data/customWorldLibrary';
|
|
import type { CustomWorldProfile } from '../../types';
|
|
|
|
export type { RuntimeRequestOptions };
|
|
|
|
type RpgEntryWorldEntry = CustomWorldLibraryEntry<CustomWorldProfile>;
|
|
type RpgEntryWorldMutationResponse =
|
|
CustomWorldLibraryMutationResponse<CustomWorldProfile>;
|
|
|
|
function normalizeRpgEntryWorldProfile(entry: RpgEntryWorldEntry) {
|
|
const rawProfile =
|
|
entry.profile && typeof entry.profile === 'object' ? entry.profile : {};
|
|
const fallbackProfile = {
|
|
id: entry.profileId,
|
|
name: entry.worldName,
|
|
subtitle: entry.subtitle,
|
|
summary: entry.summaryText,
|
|
settingText: entry.summaryText || entry.worldName,
|
|
playableNpcs: [],
|
|
storyNpcs: [],
|
|
items: [],
|
|
landmarks: [],
|
|
};
|
|
const normalizedProfile =
|
|
normalizeCustomWorldProfileRecord({
|
|
...fallbackProfile,
|
|
...rawProfile,
|
|
}) ?? normalizeCustomWorldProfileRecord(fallbackProfile);
|
|
|
|
return {
|
|
...entry,
|
|
profile: normalizedProfile ?? entry.profile,
|
|
} as RpgEntryWorldEntry;
|
|
}
|
|
|
|
function normalizeRpgEntryWorldEntries(
|
|
entries: RpgEntryWorldEntry[] | null | undefined,
|
|
) {
|
|
return Array.isArray(entries)
|
|
? entries.map((entry) => normalizeRpgEntryWorldProfile(entry))
|
|
: [];
|
|
}
|
|
|
|
function normalizeRpgEntryWorldMutationResponse(
|
|
response: RpgEntryWorldMutationResponse,
|
|
) {
|
|
return {
|
|
entry: normalizeRpgEntryWorldProfile(response.entry),
|
|
entries: normalizeRpgEntryWorldEntries(response.entries),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* RPG 入口世界库 client 的真实实现。
|
|
* 第三批收口后,平台首页/详情页开始游戏链直接走 rpg-entry 域请求,不再反向穿旧 storageService 兼容层。
|
|
*/
|
|
export async function listRpgEntryWorldLibrary(
|
|
options: RuntimeRequestOptions = {},
|
|
) {
|
|
const response = await requestRpgRuntimeJson<
|
|
CustomWorldLibraryResponse<CustomWorldProfile>
|
|
>(
|
|
'/custom-world-library',
|
|
{ method: 'GET' },
|
|
'读取自定义世界库失败',
|
|
{
|
|
...RUNTIME_BACKGROUND_AUTH_OPTIONS,
|
|
...options,
|
|
},
|
|
);
|
|
|
|
return normalizeRpgEntryWorldEntries(response?.entries);
|
|
}
|
|
|
|
export async function listRpgEntryWorldGallery(
|
|
options: RuntimeRequestOptions = {},
|
|
) {
|
|
const response = await requestPublicRpgRuntimeJson<CustomWorldGalleryResponse>(
|
|
'/custom-world-gallery',
|
|
{ method: 'GET' },
|
|
'读取作品广场失败',
|
|
options,
|
|
);
|
|
|
|
return Array.isArray(response?.entries) ? response.entries : [];
|
|
}
|
|
|
|
export async function getRpgEntryWorldGalleryDetail(
|
|
ownerUserId: string,
|
|
profileId: string,
|
|
options: RuntimeRequestOptions = {},
|
|
) {
|
|
const response = await requestPublicRpgRuntimeJson<
|
|
CustomWorldGalleryDetailResponse<CustomWorldProfile>
|
|
>(
|
|
`/custom-world-gallery/${encodeURIComponent(ownerUserId)}/${encodeURIComponent(profileId)}`,
|
|
{ method: 'GET' },
|
|
'读取作品详情失败',
|
|
options,
|
|
);
|
|
|
|
return normalizeRpgEntryWorldProfile(response.entry);
|
|
}
|
|
|
|
export async function getRpgEntryWorldGalleryDetailByCode(
|
|
code: string,
|
|
options: RuntimeRequestOptions = {},
|
|
) {
|
|
const response = await requestPublicRpgRuntimeJson<
|
|
CustomWorldGalleryDetailResponse<CustomWorldProfile>
|
|
>(
|
|
`/custom-world-gallery/by-code/${encodeURIComponent(code)}`,
|
|
{ method: 'GET' },
|
|
'读取作品详情失败',
|
|
options,
|
|
);
|
|
|
|
return normalizeRpgEntryWorldProfile(response.entry);
|
|
}
|
|
|
|
export async function remixRpgEntryWorldGallery(
|
|
ownerUserId: string,
|
|
profileId: string,
|
|
options: RuntimeRequestOptions = {},
|
|
) {
|
|
const response = await requestRpgRuntimeJson<
|
|
CustomWorldLibraryMutationResponse<CustomWorldProfile>
|
|
>(
|
|
`/custom-world-gallery/${encodeURIComponent(ownerUserId)}/${encodeURIComponent(profileId)}/remix`,
|
|
{ method: 'POST' },
|
|
'Remix 作品失败',
|
|
options,
|
|
);
|
|
|
|
return normalizeRpgEntryWorldMutationResponse(response);
|
|
}
|
|
|
|
export async function recordRpgEntryWorldGalleryPlay(
|
|
ownerUserId: string,
|
|
profileId: string,
|
|
options: RuntimeRequestOptions = {},
|
|
) {
|
|
const response = await requestRpgRuntimeJson<
|
|
CustomWorldGalleryDetailResponse<CustomWorldProfile>
|
|
>(
|
|
`/custom-world-gallery/${encodeURIComponent(ownerUserId)}/${encodeURIComponent(profileId)}/play`,
|
|
{ method: 'POST' },
|
|
'记录作品游玩失败',
|
|
options,
|
|
);
|
|
|
|
return normalizeRpgEntryWorldProfile(response.entry);
|
|
}
|
|
|
|
export async function likeRpgEntryWorldGallery(
|
|
ownerUserId: string,
|
|
profileId: string,
|
|
options: RuntimeRequestOptions = {},
|
|
) {
|
|
const response = await requestRpgRuntimeJson<
|
|
CustomWorldGalleryDetailResponse<CustomWorldProfile>
|
|
>(
|
|
`/custom-world-gallery/${encodeURIComponent(ownerUserId)}/${encodeURIComponent(profileId)}/like`,
|
|
{ method: 'POST' },
|
|
'点赞作品失败',
|
|
options,
|
|
);
|
|
|
|
return normalizeRpgEntryWorldProfile(response.entry);
|
|
}
|
|
|
|
export async function getRpgEntryWorldLibraryDetail(
|
|
profileId: string,
|
|
options: RuntimeRequestOptions = {},
|
|
) {
|
|
const response = await requestRpgRuntimeJson<
|
|
CustomWorldGalleryDetailResponse<CustomWorldProfile>
|
|
>(
|
|
`/custom-world-library/${encodeURIComponent(profileId)}`,
|
|
{ method: 'GET' },
|
|
'读取作品详情失败',
|
|
options,
|
|
);
|
|
|
|
return normalizeRpgEntryWorldProfile(response.entry);
|
|
}
|
|
|
|
export async function upsertRpgEntryWorldProfile(
|
|
profile: CustomWorldProfile,
|
|
options: RuntimeRequestOptions = {},
|
|
) {
|
|
const response = await requestRpgRuntimeJson<
|
|
CustomWorldLibraryMutationResponse<CustomWorldProfile>
|
|
>(
|
|
`/custom-world-library/${encodeURIComponent(profile.id)}`,
|
|
{
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
profile,
|
|
}),
|
|
},
|
|
'保存自定义世界失败',
|
|
options,
|
|
);
|
|
|
|
return normalizeRpgEntryWorldMutationResponse(response);
|
|
}
|
|
|
|
export async function deleteRpgEntryWorldProfile(
|
|
profileId: string,
|
|
options: RuntimeRequestOptions = {},
|
|
) {
|
|
const response = await requestRpgRuntimeJson<
|
|
CustomWorldLibraryResponse<CustomWorldProfile>
|
|
>(
|
|
`/custom-world-library/${encodeURIComponent(profileId)}`,
|
|
{ method: 'DELETE' },
|
|
'删除自定义世界失败',
|
|
options,
|
|
);
|
|
|
|
return normalizeRpgEntryWorldEntries(response?.entries);
|
|
}
|
|
|
|
export async function publishRpgEntryWorldProfile(
|
|
profileId: string,
|
|
options: RuntimeRequestOptions = {},
|
|
) {
|
|
const response = await requestRpgRuntimeJson<
|
|
CustomWorldLibraryMutationResponse<CustomWorldProfile>
|
|
>(
|
|
`/custom-world-library/${encodeURIComponent(profileId)}/publish`,
|
|
{ method: 'POST' },
|
|
'发布自定义世界失败',
|
|
options,
|
|
);
|
|
|
|
return normalizeRpgEntryWorldMutationResponse(response);
|
|
}
|
|
|
|
export async function unpublishRpgEntryWorldProfile(
|
|
profileId: string,
|
|
options: RuntimeRequestOptions = {},
|
|
) {
|
|
const response = await requestRpgRuntimeJson<
|
|
CustomWorldLibraryMutationResponse<CustomWorldProfile>
|
|
>(
|
|
`/custom-world-library/${encodeURIComponent(profileId)}/unpublish`,
|
|
{ method: 'POST' },
|
|
'下架自定义世界失败',
|
|
options,
|
|
);
|
|
|
|
return normalizeRpgEntryWorldMutationResponse(response);
|
|
}
|
|
|
|
export const rpgEntryLibraryClient = {
|
|
listWorldLibrary: listRpgEntryWorldLibrary,
|
|
listWorldGallery: listRpgEntryWorldGallery,
|
|
getWorldGalleryDetail: getRpgEntryWorldGalleryDetail,
|
|
getWorldGalleryDetailByCode: getRpgEntryWorldGalleryDetailByCode,
|
|
getWorldLibraryDetail: getRpgEntryWorldLibraryDetail,
|
|
remixWorldGallery: remixRpgEntryWorldGallery,
|
|
recordWorldGalleryPlay: recordRpgEntryWorldGalleryPlay,
|
|
likeWorldGallery: likeRpgEntryWorldGallery,
|
|
upsertWorldProfile: upsertRpgEntryWorldProfile,
|
|
deleteWorldProfile: deleteRpgEntryWorldProfile,
|
|
publishWorldProfile: publishRpgEntryWorldProfile,
|
|
unpublishWorldProfile: unpublishRpgEntryWorldProfile,
|
|
};
|