"use client" import { useCallback, useEffect, useState } from "react" import { usePathname } from "next/navigation" import { Phone, Users, X as XIcon } from "lucide-react" import { useLobbyside } from "@lobbyside/react" import { useAuth } from "@lib/auth-context" import { cn } from "@lib/utils" import { dmSans125ClassName } from "@/lib/fonts" import { analytics } from "@/lib/analytics" const STORAGE_KEY = "sm_next_app_research_cta_dismissed_v1" const BOOK_CALL_HREF = "https://cal.com/supermemory/growth" const LOBBYSIDE_WIDGET_ID = "e385c52f-4dd3-4fb2-81eb-da3a78059014" function ResearchCtaHeroGraphic({ avatarUrl, hostName, }: { avatarUrl?: string hostName?: string }) { return (
× {avatarUrl ? ( {hostName ) : (
)}
) } export function NextAppResearchCta() { const pathname = usePathname() const [mounted, setMounted] = useState(false) const [dismissed, setDismissed] = useState(false) const widget = useLobbyside(LOBBYSIDE_WIDGET_ID) const { user, org } = useAuth() useEffect(() => { setMounted(true) setDismissed(localStorage.getItem(STORAGE_KEY) === "1") }, []) const handleDismiss = useCallback((e: React.MouseEvent) => { e.stopPropagation() localStorage.setItem(STORAGE_KEY, "1") setDismissed(true) analytics.nextAppResearchCtaDismissed() }, []) const handleJoinCall = useCallback(async () => { if (widget.status !== "online" || widget.isQueueFull) return analytics.nextAppResearchCtaLobbysideCallClicked() // Open the tab synchronously so Safari/iOS keep the user-activation // gesture. We redirect it once joinCall() resolves, or fall back to // the book-a-call URL if the host goes offline / queue fills / the // request errors between render and click. const pendingTab = window.open("", "_blank") const navigate = (url: string) => { if (pendingTab && !pendingTab.closed) { pendingTab.location.href = url } else { window.open(url, "_blank", "noopener,noreferrer") } } try { const visitor: Record = {} if (user?.email) visitor.email = user.email if (user?.name) visitor.name = user.name if (org?.name) visitor.company = org.name const github = (user as { github?: unknown } | null)?.github if (typeof github === "string" && github) visitor.github = github const joinArgs = Object.keys(visitor).length > 0 ? { visitor } : undefined const { entryUrl } = await widget.joinCall(joinArgs) navigate(entryUrl) } catch (err) { console.error("[Lobbyside] joinCall failed", err) navigate(BOOK_CALL_HREF) } }, [widget, user, org]) const handleCardKeyDown = useCallback( (e: React.KeyboardEvent) => { if (e.target !== e.currentTarget) return if (e.key === "Enter" || e.key === " ") { e.preventDefault() handleJoinCall() } }, [handleJoinCall], ) const handleBookClick = useCallback(() => { analytics.nextAppResearchCtaBookCallClicked() }, []) if ( !mounted || dismissed || pathname.startsWith("/onboarding") || widget.status === "loading" ) { return null } const cardBaseClasses = cn( "fixed z-[45] bottom-4 left-4 min-w-[280px] max-w-[min(calc(100vw-2rem),22.5rem)]", "rounded-xl border border-white/[0.08] bg-[#0D121A]/95 backdrop-blur-md", "shadow-[0_8px_32px_rgba(0,0,0,0.35)] p-3.5", ) if (widget.status !== "online" || widget.isQueueFull) { return (

Be part of the next supermemory app

Share what you want next. We’d love a quick call.

) } return (

{widget.hostName}

{widget.hostTitle ? (

{widget.hostTitle}

) : null}
) }