Spaces:
Running
Running
Ryan Christian D. Deniega commited on
Commit Β·
ed3755c
1
Parent(s): 7ba7fea
fix(frontend): block social URL preview + better error card for social URLs
Browse files
frontend/src/pages/VerifyPage.jsx
CHANGED
|
@@ -271,6 +271,8 @@ export default function VerifyPage() {
|
|
| 271 |
useEffect(() => {
|
| 272 |
if (tab !== 'url' || !input.trim()) { setUrlPreview(null); setUrlPreviewLoading(false); return }
|
| 273 |
try { new URL(input.trim()) } catch { setUrlPreview(null); setUrlPreviewLoading(false); return }
|
|
|
|
|
|
|
| 274 |
setUrlPreviewLoading(true)
|
| 275 |
const timer = setTimeout(async () => {
|
| 276 |
try {
|
|
@@ -287,9 +289,23 @@ export default function VerifyPage() {
|
|
| 287 |
|
| 288 |
const canSubmit = !loading && (tab === 'text' || tab === 'url' ? input.trim() : file)
|
| 289 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 290 |
async function handleSubmit(e) {
|
| 291 |
e.preventDefault()
|
| 292 |
if (!canSubmit) return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
/* Capture what the user submitted before any state resets */
|
| 294 |
const previewUrl = (tab === 'image' || tab === 'video') && file
|
| 295 |
? URL.createObjectURL(file)
|
|
@@ -598,18 +614,29 @@ export default function VerifyPage() {
|
|
| 598 |
{error && (
|
| 599 |
<div id={errorId} role="alert"
|
| 600 |
className="card p-4 flex items-start gap-2"
|
| 601 |
-
style={{ borderColor: 'rgba(220,38,38,0.4)' }}>
|
| 602 |
-
<AlertCircle size={15} style={{ color: '#f87171', marginTop: 1, flexShrink: 0 }} aria-hidden="true" />
|
| 603 |
<div>
|
| 604 |
-
<p className="text-sm font-semibold" style={{ color: '#f87171', fontFamily: 'var(--font-display)' }}>
|
| 605 |
-
Verification failed
|
| 606 |
-
</p>
|
| 607 |
-
<p className="text-xs mt-0.5" style={{ color: 'var(--text-secondary)', fontFamily: 'var(--font-body)' }}>
|
| 608 |
-
{error}
|
| 609 |
-
{/failed to fetch|network|ERR_/i.test(error) && (
|
| 610 |
-
<> β Make sure the backend is running at <code>localhost:8000</code>.</>
|
| 611 |
-
)}
|
| 612 |
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 613 |
</div>
|
| 614 |
</div>
|
| 615 |
)}
|
|
|
|
| 271 |
useEffect(() => {
|
| 272 |
if (tab !== 'url' || !input.trim()) { setUrlPreview(null); setUrlPreviewLoading(false); return }
|
| 273 |
try { new URL(input.trim()) } catch { setUrlPreview(null); setUrlPreviewLoading(false); return }
|
| 274 |
+
// Don't attempt to preview social media URLs β they're login-protected
|
| 275 |
+
if (isSocialUrl(input.trim())) { setUrlPreview(null); setUrlPreviewLoading(false); return }
|
| 276 |
setUrlPreviewLoading(true)
|
| 277 |
const timer = setTimeout(async () => {
|
| 278 |
try {
|
|
|
|
| 289 |
|
| 290 |
const canSubmit = !loading && (tab === 'text' || tab === 'url' ? input.trim() : file)
|
| 291 |
|
| 292 |
+
function isSocialUrl(s) {
|
| 293 |
+
try {
|
| 294 |
+
const h = new URL(s).hostname
|
| 295 |
+
return h.includes('facebook.com') || h.includes('x.com') || h.includes('twitter.com')
|
| 296 |
+
} catch { return false }
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
async function handleSubmit(e) {
|
| 300 |
e.preventDefault()
|
| 301 |
if (!canSubmit) return
|
| 302 |
+
|
| 303 |
+
/* Block social media URLs β backend can't scrape them */
|
| 304 |
+
if (tab === 'url' && isSocialUrl(input)) {
|
| 305 |
+
setError('Facebook, X, and Twitter URLs cannot be scraped β the page is login-protected.\n\nInstead: copy the post\'s text/caption and paste it into the Text tab.')
|
| 306 |
+
return
|
| 307 |
+
}
|
| 308 |
+
|
| 309 |
/* Capture what the user submitted before any state resets */
|
| 310 |
const previewUrl = (tab === 'image' || tab === 'video') && file
|
| 311 |
? URL.createObjectURL(file)
|
|
|
|
| 614 |
{error && (
|
| 615 |
<div id={errorId} role="alert"
|
| 616 |
className="card p-4 flex items-start gap-2"
|
| 617 |
+
style={{ borderColor: isSocialUrl(input) ? 'rgba(220,150,38,0.4)' : 'rgba(220,38,38,0.4)' }}>
|
| 618 |
+
<AlertCircle size={15} style={{ color: isSocialUrl(input) ? '#fb923c' : '#f87171', marginTop: 1, flexShrink: 0 }} aria-hidden="true" />
|
| 619 |
<div>
|
| 620 |
+
<p className="text-sm font-semibold" style={{ color: isSocialUrl(input) ? '#fb923c' : '#f87171', fontFamily: 'var(--font-display)' }}>
|
| 621 |
+
{isSocialUrl(input) ? 'Social media URLs are not supported' : 'Verification failed'}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 622 |
</p>
|
| 623 |
+
{isSocialUrl(input) ? (
|
| 624 |
+
<>
|
| 625 |
+
<p className="text-xs mt-1" style={{ color: 'var(--text-secondary)', fontFamily: 'var(--font-body)' }}>
|
| 626 |
+
Facebook, X, and Twitter block server-side scraping β the page requires a login.
|
| 627 |
+
</p>
|
| 628 |
+
<p className="text-xs mt-1.5 font-semibold" style={{ color: 'var(--text-primary)', fontFamily: 'var(--font-body)' }}>
|
| 629 |
+
Instead: copy the post caption/text and paste it into the <strong>Text</strong> tab.
|
| 630 |
+
</p>
|
| 631 |
+
</>
|
| 632 |
+
) : (
|
| 633 |
+
<p className="text-xs mt-0.5" style={{ color: 'var(--text-secondary)', fontFamily: 'var(--font-body)' }}>
|
| 634 |
+
{error}
|
| 635 |
+
{/failed to fetch|network|ERR_/i.test(error) && (
|
| 636 |
+
<> β Make sure the backend is running at <code>localhost:8000</code>.</>
|
| 637 |
+
)}
|
| 638 |
+
</p>
|
| 639 |
+
)}
|
| 640 |
</div>
|
| 641 |
</div>
|
| 642 |
)}
|