Files
Genarrative/src/components/platform-entry/PlatformProfileRewardCodeRedeemModal.tsx
T
kdletters e8c2e8d532 接入填码原生剪贴板读取
邀请码填写和兑换码弹窗在宿主声明 clipboard.readText 时显示粘贴入口

粘贴动作只读取宿主纯文本并填入现有受控输入框,不自动提交或伪造兑换成功

补充填码粘贴回归测试和 profile 剪贴板 helper

更新 Expo/Tauri 宿主壳方案、HostBridge 协议和共享决策记录
2026-06-18 06:48:46 +08:00

117 lines
3.1 KiB
TypeScript

import { ClipboardPaste } from 'lucide-react';
import { PlatformActionButton } from '../common/PlatformActionButton';
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
import { PlatformTextField } from '../common/PlatformTextField';
import {
canPasteFromNativeHostClipboard,
readNativeHostClipboardText,
} from './platformProfileHostClipboard';
import { PlatformProfileModalShell } from './PlatformProfileModalShell';
export type PlatformProfileRewardCodeRedeemModalProps = {
value: string;
isSubmitting: boolean;
error: string | null;
success: string | null;
onChange: (value: string) => void;
onSubmit: () => void;
onClose: () => void;
};
/**
* 个人中心兑换码弹窗。
* 保持原有输入、回车提交、禁用态和反馈消息语义不变。
*/
export function PlatformProfileRewardCodeRedeemModal({
value,
isSubmitting,
error,
success,
onChange,
onSubmit,
onClose,
}: PlatformProfileRewardCodeRedeemModalProps) {
const canPasteRewardCode = canPasteFromNativeHostClipboard();
const pasteRewardCode = () => {
void readNativeHostClipboardText().then((text) => {
if (text) {
onChange(text);
}
});
};
return (
<PlatformProfileModalShell
title="兑换码"
onClose={onClose}
closeLabel="关闭兑换码"
panelClassName="platform-recharge-modal !max-w-sm rounded-[1.4rem]"
bodyClassName="space-y-3 px-5 py-5"
footerClassName="px-5 pb-5 pt-0"
footer={
<PlatformActionButton
surface="profile"
fullWidth
size="md"
className="disabled:opacity-50"
onClick={onSubmit}
disabled={isSubmitting || !value.trim()}
>
{isSubmitting ? '兑换中' : '兑换'}
</PlatformActionButton>
}
>
<PlatformTextField
value={value}
onChange={(event) => onChange(event.target.value)}
onKeyDown={(event) => {
if (event.key === 'Enter') {
onSubmit();
}
}}
size="sm"
density="roomy"
className="uppercase tracking-normal"
placeholder="输入兑换码"
aria-label="兑换码"
autoFocus
/>
{canPasteRewardCode ? (
<PlatformActionButton
type="button"
surface="profile"
tone="ghost"
size="xs"
shape="pill"
fullWidth
onClick={pasteRewardCode}
>
<ClipboardPaste className="h-3.5 w-3.5" />
粘贴
</PlatformActionButton>
) : null}
{error ? (
<PlatformStatusMessage
tone="error"
surface="profile"
size="xs"
className="rounded-2xl font-semibold"
>
{error}
</PlatformStatusMessage>
) : null}
{success ? (
<PlatformStatusMessage
tone="success"
surface="profile"
size="xs"
className="rounded-2xl font-semibold"
>
{success}
</PlatformStatusMessage>
) : null}
</PlatformProfileModalShell>
);
}