diff --git a/.env.example b/.env.example index b8cfc8fa..9b9e5af9 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,8 @@ NEXT_PUBLIC_MESH_SDK_KEY= NEXT_PUBLIC_UNIFY_SCRIPT_SRC= NEXT_PUBLIC_UNIFY_API_KEY= NEXT_PUBLIC_RB2B_KEY= +NEXT_PUBLIC_PYLON_APP_ID= +PYLON_IDENTITY_SECRET= # Search mode: 'fumadocs' (default, uses Fumadocs built-in search) or 'rag' (uses RAG endpoint at mcp.superwall.com) SEARCH_MODE=fumadocs diff --git a/content/docs/support.mdx b/content/docs/support.mdx index 3d1f2646..a84e21fa 100644 --- a/content/docs/support.mdx +++ b/content/docs/support.mdx @@ -5,20 +5,33 @@ description: "Need help with anything Superwall-related? We're here to help." ## Resources -We have lots of resources that can help you find the answer to your issue, try looking there first: +We have lots of resources that can help you find the answer to your issue: +- [Ask AI](/ai) for **instant answers** to your questions - Consult our Docs (you're here right now! try searching for your issue) - Search for solutions in our [Help Center](https://support.superwall.com/) - Check our [Status Page](https://status.superwall.com/) for any on-going issues -- [Ask AI](/ai) for instant answers to your questions ## Contact Support -If additional help is required, you can contact Support by logging into your dashboard at [superwall.com](https://superwall.com/login) and using the **💬 Chat button** at the bottom-right corner of the screen. - -**Please provide any relevant details to help us resolve your issue quickly:** -- A detailed description of the issue -- User IDs if the issue is affecting specific users +If additional help is required, you can create a new Support ticket by using the **💬 Support button** in the bottom-right corner of the screen. + + + + + **Please provide all relevant details to help us resolve your issue quickly:** + - A detailed description of the issue + - User IDs if the issue is affecting specific users + - Any and all relevant screenshots, links, logs, etc. + + + + + You will need to [log into your Superwall account](https://superwall.com/login) first + + + + ## Need help faster? Try using [Ask AI](/ai) for instant answers. \ No newline at end of file diff --git a/src/components/GlobalScripts.tsx b/src/components/GlobalScripts.tsx index f4bf79ab..2c315c8e 100644 --- a/src/components/GlobalScripts.tsx +++ b/src/components/GlobalScripts.tsx @@ -15,21 +15,67 @@ export function GlobalScripts({ location }: GlobalScriptsProps) { const unifyScriptSrc = process.env.NEXT_PUBLIC_UNIFY_SCRIPT_SRC; const unifyApiKey = process.env.NEXT_PUBLIC_UNIFY_API_KEY; const rb2bKey = process.env.NEXT_PUBLIC_RB2B_KEY; + const pylonAppId = process.env.NEXT_PUBLIC_PYLON_APP_ID; const scriptContent = ` (async function () { + try { var response = await fetch('/api/auth/session', { credentials: 'include' }); var isLoggedIn = true; + var sessionData = null; if (response && response.ok) { try { var session = await response.json(); isLoggedIn = !!(session && session.isLoggedIn); + sessionData = session; } catch (_e) { - // ignore JSON parse errors; default remains logged in + console.error('Failed to authenticate user' + _e); + return; } } - if (!isLoggedIn) { + else { + console.error('Failed to authenticate user'); + return; + } + + if (isLoggedIn) { + // Load Pylon widget for logged-in users + var pylonAppId = ${toJsStringLiteral(pylonAppId)}; + if (pylonAppId && sessionData.userInfo.email) { + // Load Pylon widget script + (function(){ + var e=window; + var t=document; + var n=function(){n.e(arguments)}; + n.q=[]; + n.e=function(e){n.q.push(e)}; + e.Pylon=n; + var r=function(){ + var e=t.createElement("script"); + e.setAttribute("type","text/javascript"); + e.setAttribute("async","true"); + e.setAttribute("src","https://widget.usepylon.com/widget/" + pylonAppId); + var n=t.getElementsByTagName("script")[0]; + n.parentNode.insertBefore(e,n); + }; + if(t.readyState==="complete"){r()} + else if(e.addEventListener){e.addEventListener("load",r,false)} + })(); + + // Configure Pylon with user data from session + var userEmail = sessionData.userInfo.email; + var userName = sessionData.userInfo.name; + window.pylon = { + chat_settings: { + app_id: pylonAppId, + email: sessionData.userInfo.email, + name: sessionData.userInfo.name, + email_hash: sessionData.emailHash, + } + }; + } + } else { var meshSdkKey = ${toJsStringLiteral(meshSdkKey)}; if (meshSdkKey) { // Avina diff --git a/src/components/LoginStatusContext.tsx b/src/components/LoginStatusContext.tsx new file mode 100644 index 00000000..c7dfe98c --- /dev/null +++ b/src/components/LoginStatusContext.tsx @@ -0,0 +1,139 @@ +'use client'; + +import React, { createContext, useContext, useEffect, useState } from 'react'; +import { LoaderCircle } from 'lucide-react'; + +// Context for sharing login status across components +const LoginStatusContext = createContext<{ + isLoggedIn: boolean | null; + isLoading: boolean; +}>({ + isLoggedIn: null, + isLoading: true, +}); + +// Provider component that checks login status once +export function LoginStatusProvider({ children }: { children: React.ReactNode }) { + const [isLoggedIn, setIsLoggedIn] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + async function checkAuth() { + try { + const response = await fetch('/api/auth/session', { + credentials: 'include' + }); + + if (response && response.ok) { + try { + const session = await response.json(); + setIsLoggedIn(!!(session && session.isLoggedIn)); + } catch (_e) { + console.error('Failed to parse session data', _e); + setIsLoggedIn(false); + } + } else { + setIsLoggedIn(false); + } + } catch (_err) { + // On error, assume not logged in + setIsLoggedIn(false); + } finally { + setIsLoading(false); + } + } + + checkAuth(); + }, []); + + return ( + + {children} + + ); +} + +// Hook to use login status context +function useLoginStatus() { + return useContext(LoginStatusContext); +} + +// Loading component +function LoadingState() { + return ( +

+ + Loading... +

+ ); +} + +// Helper components for MDX children syntax - these handle their own conditional rendering +export function LoggedIn({ children }: { children: React.ReactNode }) { + const { isLoggedIn, isLoading } = useLoginStatus(); + + if (isLoading || !isLoggedIn) { + return null; + } + + return <>{children}; +} +LoggedIn.displayName = 'LoggedIn'; + +export function LoggedOut({ children }: { children: React.ReactNode }) { + const { isLoggedIn, isLoading } = useLoginStatus(); + + if (isLoading || isLoggedIn) { + return null; + } + + return <>{children}; +} +LoggedOut.displayName = 'LoggedOut'; + +// General-purpose component that conditionally renders content based on auth status +interface BasedOnAuthProps { + loggedIn?: React.ReactNode; + loggedOut?: React.ReactNode; + children?: React.ReactNode; // For MDX children syntax +} + +export function BasedOnAuth({ loggedIn, loggedOut, children }: BasedOnAuthProps) { + const { isLoading } = useLoginStatus(); + + // Show loading state while checking + if (isLoading) { + return ; + } + + // If children are provided, render them (LoggedIn/LoggedOut will handle their own conditional rendering) + if (children) { + return <>{children}; + } + + // Fall back to props-based approach + const { isLoggedIn } = useLoginStatus(); + return <>{isLoggedIn ? loggedIn : loggedOut}; +} + +// Component for logged-in users (for use with children syntax) +export function WhenLoggedIn({ children }: { children: React.ReactNode }) { + const { isLoggedIn, isLoading } = useLoginStatus(); + + if (isLoading || !isLoggedIn) { + return null; + } + + return <>{children}; +} + +// Component for logged-out users (for use with children syntax) +export function WhenLoggedOut({ children }: { children: React.ReactNode }) { + const { isLoggedIn, isLoading } = useLoginStatus(); + + if (isLoading || isLoggedIn) { + return null; + } + + return <>{children}; +} diff --git a/src/mdx-components.tsx b/src/mdx-components.tsx index 25064c15..b9c19b6c 100644 --- a/src/mdx-components.tsx +++ b/src/mdx-components.tsx @@ -10,6 +10,7 @@ import { Accordion, Accordions as AccordionGroup } from 'fumadocs-ui/components/ import { ImageZoom as Frame } from 'fumadocs-ui/components/image-zoom' import { SDKContent } from './components/SDKContent' import { GithubInfo as GithubInfoComponent } from 'fumadocs-ui/components/github-info'; +import { WhenLoggedIn, WhenLoggedOut, LoginStatusProvider, BasedOnAuth, LoggedIn, LoggedOut } from './components/LoginStatusContext'; // We'll add custom components here @@ -278,5 +279,11 @@ export function getMDXComponents(components?: MDXComponents): MDXComponents { SDKContent, GithubInfo, ParamTable, + WhenLoggedIn, + WhenLoggedOut, + LoginStatusProvider, + BasedOnAuth, + LoggedIn, + LoggedOut, } as MDXComponents }