e8c2e8d532
邀请码填写和兑换码弹窗在宿主声明 clipboard.readText 时显示粘贴入口 粘贴动作只读取宿主纯文本并填入现有受控输入框,不自动提交或伪造兑换成功 补充填码粘贴回归测试和 profile 剪贴板 helper 更新 Expo/Tauri 宿主壳方案、HostBridge 协议和共享决策记录
117 lines
3.1 KiB
TypeScript
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>
|
|
);
|
|
}
|