diff --git a/web/app/actions.ts b/web/app/actions.ts
index 7431453f..14795825 100644
--- a/web/app/actions.ts
+++ b/web/app/actions.ts
@@ -1,6 +1,8 @@
"use server"
import { authedProcedure } from "@/lib/utils/auth-procedure"
+import { currentUser } from "@clerk/nextjs/server"
+import { get } from "ronin"
import { create } from "ronin"
import { z } from "zod"
import { ZSAError } from "zsa"
@@ -68,3 +70,9 @@ export const storeImage = authedProcedure
throw new ZSAError("ERROR", "Failed to store image")
}
})
+
+export const isExistingUser = async () => {
+ const clerkUser = await currentUser()
+ const roninUser = await get.existingStripeSubscriber.with({ email: clerkUser?.emailAddresses[0].emailAddress })
+ return clerkUser?.emailAddresses[0].emailAddress === roninUser?.email
+}
diff --git a/web/app/layout.tsx b/web/app/layout.tsx
index b9dd669c..785a5550 100644
--- a/web/app/layout.tsx
+++ b/web/app/layout.tsx
@@ -10,6 +10,7 @@ import { DeepLinkProvider } from "@/lib/providers/deep-link-provider"
import { GeistMono, GeistSans } from "./fonts"
import { JazzAndAuth } from "@/lib/providers/jazz-provider"
import { TooltipProvider } from "@/components/ui/tooltip"
+import { LearnAnythingOnboarding } from "@/components/custom/learn-anything-onboarding"
export const metadata: Metadata = {
title: "Learn Anything",
@@ -42,6 +43,7 @@ export default function RootLayout({
{children}
+
diff --git a/web/components/custom/learn-anything-onboarding.tsx b/web/components/custom/learn-anything-onboarding.tsx
new file mode 100644
index 00000000..619d9a5a
--- /dev/null
+++ b/web/components/custom/learn-anything-onboarding.tsx
@@ -0,0 +1,102 @@
+"use client"
+
+import React, { useEffect, useState } from "react"
+import { atom, useAtom } from "jotai"
+import { atomWithStorage } from "jotai/utils"
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle
+} from "@/components/ui/alert-dialog"
+import { isExistingUser } from "@/app/actions"
+import { usePathname } from "next/navigation"
+
+const hasVisitedAtom = atomWithStorage("hasVisitedLearnAnything", false)
+const isDialogOpenAtom = atom(true)
+
+export function LearnAnythingOnboarding() {
+ const pathname = usePathname()
+ const [hasVisited, setHasVisited] = useAtom(hasVisitedAtom)
+ const [isOpen, setIsOpen] = useAtom(isDialogOpenAtom)
+ const [isFetching, setIsFetching] = useState(true)
+ const [isExisting, setIsExisting] = useState(false)
+
+ if (pathname === "/") return null
+
+ useEffect(() => {
+ const loadUser = async () => {
+ try {
+ const existingUser = await isExistingUser()
+ setIsExisting(existingUser)
+ setIsOpen(true)
+ } catch (error) {
+ console.error("Error loading user:", error)
+ } finally {
+ setIsFetching(false)
+ }
+ }
+
+ if (!hasVisited) {
+ loadUser()
+ }
+ }, [hasVisited, setIsOpen])
+
+ const handleClose = () => {
+ setIsOpen(false)
+ setHasVisited(true)
+ }
+
+ if (hasVisited || isFetching) return null
+
+ return (
+
+
+
+
+ Welcome to Learn Anything!
+
+
+
+
+ {isExisting && (
+ <>
+ Existing Customer Notice
+
+ We noticed you are an existing Learn Anything customer. We sincerely apologize for any broken experience
+ you may have encountered on the old website. We've been working hard on this new version, which
+ addresses previous issues and offers more features. As an early customer, you're locked in at the{" "}
+ $3 price for our upcoming pro version. Thank you for your support!
+
+ >
+ )}
+
+ Learn Anything is a learning platform that organizes knowledge in a social way. You can create pages, add
+ links, track learning status of any topic, and more things in the future.
+
+ Try do these quick onboarding steps to get a feel for the product:
+
+ - Create your first page
+ - Add a link to a resource
+ - Update your learning status on a topic
+
+
+ If you have any questions, don't hesitate to reach out. Click on question mark button in the bottom right
+ corner and enter your message.
+
+
+
+
+ Close
+ Get Started
+
+
+
+ )
+}
+
+export default LearnAnythingOnboarding
diff --git a/web/components/ui/alert-dialog.tsx b/web/components/ui/alert-dialog.tsx
new file mode 100644
index 00000000..10a573a4
--- /dev/null
+++ b/web/components/ui/alert-dialog.tsx
@@ -0,0 +1,106 @@
+"use client"
+
+import * as React from "react"
+import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
+
+import { cn } from "@/lib/utils"
+import { buttonVariants } from "@/components/ui/button"
+
+const AlertDialog = AlertDialogPrimitive.Root
+
+const AlertDialogTrigger = AlertDialogPrimitive.Trigger
+
+const AlertDialogPortal = AlertDialogPrimitive.Portal
+
+const AlertDialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
+
+const AlertDialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+))
+AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
+
+const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => (
+
+)
+AlertDialogHeader.displayName = "AlertDialogHeader"
+
+const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => (
+
+)
+AlertDialogFooter.displayName = "AlertDialogFooter"
+
+const AlertDialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
+
+const AlertDialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName
+
+const AlertDialogAction = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
+
+const AlertDialogCancel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
+
+export {
+ AlertDialog,
+ AlertDialogPortal,
+ AlertDialogOverlay,
+ AlertDialogTrigger,
+ AlertDialogContent,
+ AlertDialogHeader,
+ AlertDialogFooter,
+ AlertDialogTitle,
+ AlertDialogDescription,
+ AlertDialogAction,
+ AlertDialogCancel
+}
diff --git a/web/package.json b/web/package.json
index c431e0cc..4aeca69c 100644
--- a/web/package.json
+++ b/web/package.json
@@ -17,6 +17,7 @@
"@nothing-but/utils": "^0.16.0",
"@omit/react-confirm-dialog": "^1.1.5",
"@omit/react-fancy-switch": "^0.1.3",
+ "@radix-ui/react-alert-dialog": "^1.1.1",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-context-menu": "^2.2.1",