Skip to content

Commit 4fcb82a

Browse files
committed
fix(web): reliable paste on iOS Safari (Add screen + dashboard)
iOS Safari often doesn't fire a textarea's onChange for a long-press 'Paste', so nothing populated. Capture the native onPaste event (read clipboardData directly), make the /add paste box always-visible + controlled, and add an explicit '📋 Paste from clipboard' button (Clipboard API on tap) as a fallback.
1 parent da4959b commit 4fcb82a

2 files changed

Lines changed: 46 additions & 12 deletions

File tree

web/app/add/add-form.tsx

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,31 @@ export function AddForm({ carts, initial }: { carts: CartOpt[]; initial: ParsedS
2828
const [busy, setBusy] = useState(false);
2929
const [cartBusy, setCartBusy] = useState(false);
3030
const [uploading, setUploading] = useState(false);
31+
const [pasteText, setPasteText] = useState("");
3132
const [added, setAdded] = useState<CartOpt | null>(null);
3233
const [newCartTitle, setNewCartTitle] = useState("");
3334

34-
const onPaste = (raw: string) => {
35+
// Apply pasted/typed text → extract link, title, price into the form.
36+
const applyPasted = (raw: string) => {
37+
setPasteText(raw);
3538
const p = parseShare({ text: raw, url: raw });
3639
if (p.productUrl) setOriginalUrl(p.productUrl);
3740
if (p.title) setTitle(p.title);
3841
if (p.priceText) setPriceText(p.priceText);
3942
};
4043

44+
// Explicit clipboard read — the reliable path on iOS Safari, where a
45+
// textarea's onChange often doesn't fire for a long-press "Paste".
46+
const pasteFromClipboard = async () => {
47+
try {
48+
const t = await navigator.clipboard.readText();
49+
if (t.trim()) applyPasted(t);
50+
else toast.error("Clipboard is empty — copy the product link first.");
51+
} catch {
52+
toast.error("Couldn't read the clipboard — paste into the box instead.");
53+
}
54+
};
55+
4156
const onPickPhoto = async (e: React.ChangeEvent<HTMLInputElement>) => {
4257
const file = e.target.files?.[0];
4358
e.target.value = ""; // allow re-picking the same file
@@ -125,17 +140,30 @@ export function AddForm({ carts, initial }: { carts: CartOpt[]; initial: ParsedS
125140

126141
return (
127142
<div className="space-y-4">
128-
{!originalUrl && (
129-
<label className="block">
130-
<span className="block text-sm font-medium mb-1">Paste a product link</span>
131-
<textarea
132-
rows={2}
133-
onChange={(e) => onPaste(e.target.value)}
134-
placeholder={'Paste the link or the whole “Check out this product…” text'}
135-
className={inputCls}
136-
/>
137-
</label>
138-
)}
143+
<div className="block">
144+
<span className="block text-sm font-medium mb-1">Paste a product link</span>
145+
<textarea
146+
rows={2}
147+
value={pasteText}
148+
onChange={(e) => applyPasted(e.target.value)}
149+
onPaste={(e) => {
150+
// Capture the paste directly — iOS Safari doesn't reliably fire
151+
// onChange for a long-press paste. preventDefault so we set the
152+
// value ourselves (no double insert).
153+
e.preventDefault();
154+
applyPasted(e.clipboardData.getData("text"));
155+
}}
156+
placeholder={'Paste the link or the whole “Check out this product…” text'}
157+
className={inputCls}
158+
/>
159+
<button
160+
type="button"
161+
onClick={pasteFromClipboard}
162+
className="mt-2 inline-flex items-center gap-2 rounded-full border border-ink px-4 py-2 text-sm font-medium hover:bg-paper"
163+
>
164+
📋 Paste from clipboard
165+
</button>
166+
</div>
139167

140168
<label className="block">
141169
<span className="block text-sm font-medium mb-1">Cart</span>

web/components/paste-url-preview.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ export function PasteUrlPreview({ onResolved }: PasteUrlPreviewProps) {
123123
rows={2}
124124
value={rawInput}
125125
onChange={(e) => handlePaste(e.target.value)}
126+
onPaste={(e) => {
127+
// iOS Safari doesn't reliably fire onChange for a long-press
128+
// paste — capture the clipboard data directly.
129+
e.preventDefault();
130+
handlePaste(e.clipboardData.getData("text"));
131+
}}
126132
placeholder="Paste here — e.g. “Check out this product… https://www.myntra.com/…”"
127133
className="w-full resize-y rounded-lg border border-rule bg-cream py-3 pl-10 pr-4 text-base leading-relaxed focus:outline-none focus:ring-2 focus:ring-accent focus:border-accent"
128134
/>

0 commit comments

Comments
 (0)