Merge branch 'master' of http://82.157.175.59:3000/GenarrativeAI/Genarrative
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import { ArrowLeft, Copy } from 'lucide-react';
|
||||
import { ArrowLeft, Copy, Share2 } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import type { CustomWorldLibraryEntry } from '../../../packages/shared/src/contracts/runtime';
|
||||
import { buildCustomWorldPlayableCharacters } from '../../data/characterPresets';
|
||||
import { buildPublicWorkDetailUrl } from '../../routing/appPageRoutes';
|
||||
import { copyTextToClipboard } from '../../services/clipboard';
|
||||
import type { CustomWorldProfile } from '../../types';
|
||||
import { ResolvedAssetImage } from '../ResolvedAssetImage';
|
||||
@@ -74,6 +75,9 @@ export function RpgEntryWorldDetailView({
|
||||
const [copyState, setCopyState] = useState<'idle' | 'copied' | 'failed'>(
|
||||
'idle',
|
||||
);
|
||||
const [shareState, setShareState] = useState<'idle' | 'copied' | 'failed'>(
|
||||
'idle',
|
||||
);
|
||||
const canStartGame = entry.visibility === 'published';
|
||||
const previewCharacters = buildCustomWorldPlayableCharacters(
|
||||
entry.profile,
|
||||
@@ -96,6 +100,19 @@ export function RpgEntryWorldDetailView({
|
||||
window.setTimeout(() => setCopyState('idle'), 1400);
|
||||
});
|
||||
};
|
||||
const sharePublicWork = () => {
|
||||
if (!publicWorkCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const shareUrl = buildPublicWorkDetailUrl(publicWorkCode);
|
||||
const shareText = `邀请你来玩《${entry.worldName}》\n作品号:${publicWorkCode}\n${shareUrl}`;
|
||||
|
||||
void copyTextToClipboard(shareText).then((copied) => {
|
||||
setShareState(copied ? 'copied' : 'failed');
|
||||
window.setTimeout(() => setShareState('idle'), 1400);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-col">
|
||||
@@ -146,21 +163,38 @@ export function RpgEntryWorldDetailView({
|
||||
: '仅自己可见'}
|
||||
</span>
|
||||
{publicWorkCode ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={copyPublicWorkCode}
|
||||
className="platform-pill platform-pill--neutral flex items-center gap-1 px-3"
|
||||
aria-label={`复制作品号 ${publicWorkCode}`}
|
||||
title="复制作品号"
|
||||
>
|
||||
<span>作品号 {publicWorkCode}</span>
|
||||
<Copy className="h-3 w-3" />
|
||||
{copyState !== 'idle' ? (
|
||||
<span className="text-xs">
|
||||
{copyState === 'copied' ? '已复制' : '复制失败'}
|
||||
</span>
|
||||
) : null}
|
||||
</button>
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={copyPublicWorkCode}
|
||||
className="platform-pill platform-pill--neutral flex items-center gap-1 px-3"
|
||||
aria-label={`复制作品号 ${publicWorkCode}`}
|
||||
title="复制作品号"
|
||||
>
|
||||
<span>作品号 {publicWorkCode}</span>
|
||||
<Copy className="h-3 w-3" />
|
||||
{copyState !== 'idle' ? (
|
||||
<span className="text-xs">
|
||||
{copyState === 'copied' ? '已复制' : '复制失败'}
|
||||
</span>
|
||||
) : null}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={sharePublicWork}
|
||||
className="platform-pill platform-pill--neutral flex items-center gap-1 px-3"
|
||||
aria-label={`分享作品 ${entry.worldName}`}
|
||||
title="分享作品"
|
||||
>
|
||||
<Share2 className="h-3 w-3" />
|
||||
<span>分享作品</span>
|
||||
{shareState !== 'idle' ? (
|
||||
<span className="text-xs">
|
||||
{shareState === 'copied' ? '已复制' : '复制失败'}
|
||||
</span>
|
||||
) : null}
|
||||
</button>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="mt-4 text-3xl font-black text-white">
|
||||
|
||||
@@ -9,6 +9,11 @@ import type {
|
||||
CustomWorldLibraryEntry,
|
||||
PlatformBrowseHistoryWriteEntry,
|
||||
} from '../../../packages/shared/src/contracts/runtime';
|
||||
import {
|
||||
buildPublicWorkDetailPath,
|
||||
pushAppHistoryPath,
|
||||
} from '../../routing/appPageRoutes';
|
||||
import { ApiClientError } from '../../services/apiClient';
|
||||
import {
|
||||
deleteRpgEntryWorldProfile,
|
||||
getRpgEntryWorldGalleryDetail,
|
||||
@@ -16,7 +21,6 @@ import {
|
||||
publishRpgEntryWorldProfile,
|
||||
unpublishRpgEntryWorldProfile,
|
||||
} from '../../services/rpg-entry/rpgEntryLibraryClient';
|
||||
import { ApiClientError } from '../../services/apiClient';
|
||||
import type { CustomWorldProfile } from '../../types';
|
||||
import {
|
||||
normalizeRpgEntryAgentBackedProfile,
|
||||
@@ -167,6 +171,9 @@ export function useRpgEntryLibraryDetail(
|
||||
setSelectedDetailEntry(entry);
|
||||
setDetailError(null);
|
||||
setSelectionStage('detail');
|
||||
if (entry.publicWorkCode?.trim()) {
|
||||
pushAppHistoryPath(buildPublicWorkDetailPath(entry.publicWorkCode));
|
||||
}
|
||||
},
|
||||
[appendBrowseHistoryEntry, setSelectedDetailEntry, setSelectionStage],
|
||||
);
|
||||
@@ -183,6 +190,11 @@ export function useRpgEntryLibraryDetail(
|
||||
entry.profileId,
|
||||
);
|
||||
setSelectedDetailEntry(detailEntry);
|
||||
if (detailEntry.publicWorkCode?.trim()) {
|
||||
pushAppHistoryPath(
|
||||
buildPublicWorkDetailPath(detailEntry.publicWorkCode),
|
||||
);
|
||||
}
|
||||
void appendBrowseHistoryEntry({
|
||||
ownerUserId: detailEntry.ownerUserId,
|
||||
profileId: detailEntry.profileId,
|
||||
|
||||
Reference in New Issue
Block a user