Something went wrong. Try again.
A Bsky-like frontend using the atprotocol natively.
Something went wrong. Try again.
5.1 kB · 140 lines
TSX
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141import { useState, useEffect, useRef } from 'react';import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';import { faFlag, faXmark } from '@fortawesome/free-solid-svg-icons';import { atprotoClient } from '../api/client';import type { PostView } from '../api/types';import './ReportModal.css';
interface ReportModalProps { post: PostView; onClose: () => void;}
const REPORT_REASONS = [ { value: 'com.atproto.moderation.defs#reasonSpam', label: 'Spam' }, { value: 'com.atproto.moderation.defs#reasonViolation', label: 'Violation of server rules' }, { value: 'com.atproto.moderation.defs#reasonMisleading', label: 'Misleading content' }, { value: 'com.atproto.moderation.defs#reasonSexual', label: 'Unwanted sexual content' }, { value: 'com.atproto.moderation.defs#reasonRude', label: 'Anti-social behavior' }, { value: 'com.atproto.moderation.defs#reasonOther', label: 'Other' },] as const;
export default function ReportModal({ post, onClose }: ReportModalProps) { const [reasonType, setReasonType] = useState<string>(''); const [details, setDetails] = useState(''); const [submitting, setSubmitting] = useState(false); const [submitted, setSubmitted] = useState(false); const [error, setError] = useState<string | null>(null); const modalRef = useRef<HTMLDivElement>(null);
useEffect(() => { const prev = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = prev; }; }, []);
useEffect(() => { const handleKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', handleKey); return () => window.removeEventListener('keydown', handleKey); }, [onClose]);
const handleSubmit = async () => { if (!reasonType || submitting) return; setSubmitting(true); setError(null); try { await atprotoClient.createReport({ reasonType, reason: details.trim() || undefined, subject: { $type: 'com.atproto.repo.strongRef', uri: post.uri, cid: post.cid, }, }); setSubmitted(true); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to submit report'); } finally { setSubmitting(false); } };
const handleBackdropClick = (e: React.MouseEvent) => { if (e.target === modalRef.current) onClose(); };
return ( <div className="report-modal-backdrop" ref={modalRef} onClick={handleBackdropClick}> <div className="report-modal"> <div className="report-modal-header"> <h2 className="report-modal-title"> <FontAwesomeIcon icon={faFlag} /> Report Post </h2> <button className="report-modal-close" onClick={onClose} type="button"> <FontAwesomeIcon icon={faXmark} /> </button> </div>
{submitted ? ( <div className="report-modal-success"> <FontAwesomeIcon icon={faFlag} className="report-modal-success-icon" /> <p>Report submitted. Thank you for helping keep the community safe.</p> <button className="report-modal-btn" onClick={onClose} type="button"> Done </button> </div> ) : ( <> <div className="report-modal-body"> <p className="report-modal-description"> Why are you reporting this post? </p> <div className="report-modal-reasons"> {REPORT_REASONS.map((r) => ( <label key={r.value} className={`report-modal-reason${reasonType === r.value ? ' selected' : ''}`} > <input type="radio" name="report-reason" value={r.value} checked={reasonType === r.value} onChange={() => setReasonType(r.value)} /> <span>{r.label}</span> </label> ))} </div> <textarea className="report-modal-details" placeholder="Additional details (optional)" value={details} onChange={(e) => setDetails(e.target.value)} rows={3} /> {error && <div className="report-modal-error">{error}</div>} </div> <div className="report-modal-footer"> <button className="report-modal-cancel" onClick={onClose} type="button"> Cancel </button> <button className="report-modal-btn" onClick={handleSubmit} disabled={!reasonType || submitting} type="button" > {submitting ? 'Submitting…' : 'Submit Report'} </button> </div> </> )} </div> </div> );}