diff --git a/src/components/AuthModal.tsx b/src/components/AuthModal.tsx index 1f29912..671f563 100644 --- a/src/components/AuthModal.tsx +++ b/src/components/AuthModal.tsx @@ -14,6 +14,7 @@ import { getSocialLinks } from '@/lib/social-urls'; import { supabase } from '@/lib/supabase'; import { useProfile } from '@/hooks/useProfile'; +import { useXVerification } from '@/hooks/useXVerification'; import { useFriends } from '@/hooks/useFriends'; import { useFriendRequests } from '@/hooks/useFriendRequests'; import type { ETHDenverEvent, NativeAd, UserSearchResult, FriendRequest } from '@/lib/types'; @@ -377,6 +378,7 @@ export function UserMenu({ events, itinerary, onOpenFriends, onSubmitEvent, pend const { friendCount, refreshFriends: localRefreshFriends } = useFriends(); const { config } = useAdminConfig(); const { theme, setTheme } = useTheme(); + const { isXVerified, linkX, unlinkX } = useXVerification(); const refreshFriends = useCallback(async () => { await localRefreshFriends(); @@ -765,18 +767,32 @@ export function UserMenu({ events, itinerary, onOpenFriends, onSubmitEvent, pend -
-
- {'\u{1D54F}'} - setXHandle(e.target.value.replace(/^@/, ''))} - placeholder="X handle" - className="flex-1 bg-transparent text-[var(--theme-text-primary)] text-sm px-2 py-2 focus:outline-none placeholder:text-[var(--theme-text-muted)]" - /> + {/* X Account */} + {profile?.x_verified ? ( +
+ + @{profile.x_handle} + +
-
+ ) : ( + + )}
diff --git a/src/components/CommentSection.tsx b/src/components/CommentSection.tsx index fb3dc61..ac38ced 100644 --- a/src/components/CommentSection.tsx +++ b/src/components/CommentSection.tsx @@ -5,11 +5,13 @@ import { createPortal } from 'react-dom'; import { Send, Trash2, MessageCircle, X } from 'lucide-react'; import { useAuth } from '@/contexts/AuthContext'; import { useEventComments } from '@/hooks/useEventComments'; +import { supabase } from '@/lib/supabase'; import { timeAgo } from '@/lib/time-parse'; import { getDisplayName } from '@/lib/user-display'; import UserAvatar from './UserAvatar'; import ProfileCardModal from './ProfileCardModal'; import { trackCommentExpand, trackCommentAdd, trackCommentDelete, trackCommentVisibilityToggle } from '@/lib/analytics'; +import { useXVerification } from '@/hooks/useXVerification'; interface CommentSectionProps { eventId: string; @@ -19,6 +21,7 @@ interface CommentSectionProps { export function CommentSection({ eventId, commentCount = 0, eventName }: CommentSectionProps) { const { user } = useAuth(); + const { isXVerified, linkX } = useXVerification(); const [expanded, setExpanded] = useState(false); const [isMobile, setIsMobile] = useState(false); const { comments, loading, addComment, deleteComment } = useEventComments( @@ -26,6 +29,10 @@ export function CommentSection({ eventId, commentCount = 0, eventName }: Comment ); const [text, setText] = useState(''); const [visibility, setVisibility] = useState<'public' | 'friends'>('public'); + const [showEmailPrompt, setShowEmailPrompt] = useState(false); + const [email, setEmail] = useState(''); + const [emailLoading, setEmailLoading] = useState(false); + const [emailError, setEmailError] = useState(null); const [selectedProfile, setSelectedProfile] = useState<{ userId: string; displayName?: string | null; @@ -163,8 +170,37 @@ export function CommentSection({ eventId, commentCount = 0, eventName }: Comment ); + // Handle email sign-up then X OAuth link + const handleEmailThenLinkX = async () => { + if (!email.trim()) return; + setEmailLoading(true); + setEmailError(null); + + // Sign up with a random password — auto-confirms since enable_confirmations = false + // Next login they'll use OTP + const { error } = await supabase.auth.signUp({ + email: email.trim(), + password: crypto.randomUUID(), + }); + + if (error) { + // If account exists, they need to sign in properly + if (error.message?.includes('already registered') || error.status === 422) { + setEmailError('Account exists — sign in from the menu first'); + } else { + setEmailError(error.message); + } + setEmailLoading(false); + return; + } + + // Account created + session active, now start X OAuth + setEmailLoading(false); + linkX(); + }; + // Shared input content - const inputContent = user && ( + const inputContent = user && isXVerified ? (
+ ) : user && !isXVerified ? ( + // Logged in but X not verified — go straight to X OAuth + + ) : showEmailPrompt ? ( + // Not logged in — email prompt before X OAuth +
+
+ setEmail(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault(); + handleEmailThenLinkX(); + } + }} + placeholder="Enter your email..." + className="flex-1 bg-[var(--theme-bg-tertiary)]/50 border border-[var(--theme-border-primary)] rounded-lg px-3 py-2 text-sm text-[var(--theme-text-primary)] placeholder-slate-500 outline-none min-w-0" + autoFocus + /> + +
+ {emailError && ( +

{emailError}

+ )} +
+ ) : ( + // Not logged in — show connect CTA + ); // Mobile: render bottom-sheet modal via portal @@ -257,11 +351,9 @@ export function CommentSection({ eventId, commentCount = 0, eventName }: Comment
{/* Sticky input at bottom */} - {user && ( -
- {inputContent} -
- )} +
+ {inputContent} +
, document.body @@ -321,6 +413,7 @@ export function CommentSection({ eventId, commentCount = 0, eventName }: Comment telegramHandle={selectedProfile.telegramHandle} /> )} + setShowAuth(false)} />
); } diff --git a/src/components/EventCard.tsx b/src/components/EventCard.tsx index 1e4c2e9..05d317b 100644 --- a/src/components/EventCard.tsx +++ b/src/components/EventCard.tsx @@ -404,8 +404,7 @@ export const EventCard = memo(function EventCard({ compact={compact} /> )} - {/* Comments disabled until social verification is in place */} - {/* */} + {checkedInFriends && checkedInFriends.length > 0 && (