fix: 完善作品号复制与详情返回
This commit is contained in:
@@ -64,3 +64,35 @@ test('shows and copies puzzle public work code in detail view', async () => {
|
||||
|
||||
expect(writeText).toHaveBeenCalledWith('PZ-EPUBLIC1');
|
||||
});
|
||||
|
||||
test('falls back to legacy selection copy when clipboard api rejects', async () => {
|
||||
const user = userEvent.setup();
|
||||
const writeText = vi.fn(async () => {
|
||||
throw new Error('clipboard denied');
|
||||
});
|
||||
const execCommand = vi.fn(() => true);
|
||||
Object.defineProperty(navigator, 'clipboard', {
|
||||
configurable: true,
|
||||
value: { writeText },
|
||||
});
|
||||
Object.defineProperty(document, 'execCommand', {
|
||||
configurable: true,
|
||||
value: execCommand,
|
||||
});
|
||||
|
||||
render(
|
||||
<PuzzleGalleryDetailView
|
||||
item={detailItem}
|
||||
onBack={vi.fn()}
|
||||
onStartGame={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole('button', { name: '复制作品号 PZ-EPUBLIC1' }),
|
||||
);
|
||||
|
||||
expect(writeText).toHaveBeenCalledWith('PZ-EPUBLIC1');
|
||||
expect(execCommand).toHaveBeenCalledWith('copy');
|
||||
expect(await screen.findByText('已复制')).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { ArrowLeft, Copy, Pencil, Play, UserRound } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
|
||||
import { copyTextToClipboard } from '../../services/clipboard';
|
||||
import { buildPuzzlePublicWorkCode } from '../../services/publicWorkCode';
|
||||
import { ResolvedAssetImage } from '../ResolvedAssetImage';
|
||||
|
||||
@@ -13,14 +15,6 @@ type PuzzleGalleryDetailViewProps = {
|
||||
onStartGame: () => void;
|
||||
};
|
||||
|
||||
function copyText(value: string) {
|
||||
if (typeof navigator === 'undefined' || !navigator.clipboard?.writeText) {
|
||||
return;
|
||||
}
|
||||
|
||||
void navigator.clipboard.writeText(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼图广场详情页。
|
||||
* 展示最小信息并提供进入游戏动作,不扩展评论、收藏等非本轮需求。
|
||||
@@ -34,6 +28,15 @@ export function PuzzleGalleryDetailView({
|
||||
onStartGame,
|
||||
}: PuzzleGalleryDetailViewProps) {
|
||||
const publicWorkCode = buildPuzzlePublicWorkCode(item.profileId);
|
||||
const [copyState, setCopyState] = useState<'idle' | 'copied' | 'failed'>(
|
||||
'idle',
|
||||
);
|
||||
const copyPublicWorkCode = () => {
|
||||
void copyTextToClipboard(publicWorkCode).then((copied) => {
|
||||
setCopyState(copied ? 'copied' : 'failed');
|
||||
window.setTimeout(() => setCopyState('idle'), 1400);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex h-full min-h-0 w-full max-w-5xl flex-col gap-3 overflow-hidden px-1 sm:px-0">
|
||||
@@ -42,6 +45,7 @@ export function PuzzleGalleryDetailView({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onBack}
|
||||
aria-label="返回"
|
||||
className="inline-flex h-10 w-10 items-center justify-center rounded-full border border-white/16 bg-white/10 text-white/84"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
@@ -82,7 +86,7 @@ export function PuzzleGalleryDetailView({
|
||||
<span>{item.playCount} 次游玩</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => copyText(publicWorkCode)}
|
||||
onClick={copyPublicWorkCode}
|
||||
className="inline-flex max-w-full items-center gap-1.5 rounded-full border border-white/14 bg-white/10 px-3 py-1 text-sm text-amber-50/86"
|
||||
aria-label={`复制作品号 ${publicWorkCode}`}
|
||||
title="复制作品号"
|
||||
@@ -90,6 +94,11 @@ export function PuzzleGalleryDetailView({
|
||||
<span className="shrink-0">作品号</span>
|
||||
<span className="min-w-0 truncate">{publicWorkCode}</span>
|
||||
<Copy className="h-3.5 w-3.5 shrink-0" />
|
||||
{copyState !== 'idle' ? (
|
||||
<span className="shrink-0 text-xs">
|
||||
{copyState === 'copied' ? '已复制' : '复制失败'}
|
||||
</span>
|
||||
) : null}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user