summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor/onboarding.tsx
diff options
context:
space:
mode:
authorMaze <167211895+mazeincoding@users.noreply.github.com>2026-01-31 00:20:04 +0100
committerGitHub <noreply@github.com>2026-01-31 00:20:04 +0100
commit7bf0984698e43b9aa2dd879f65951318ed410390 (patch)
treeccf2cd590a47d550152afc07129cb55c71a9ec35 /apps/web/src/components/editor/onboarding.tsx
parent0173db99448d1c4c6415dcf15272e17551b48b04 (diff)
downloadopencut-7bf0984698e43b9aa2dd879f65951318ed410390.tar.gz
codebase overhaul (#697)
Diffstat (limited to 'apps/web/src/components/editor/onboarding.tsx')
-rw-r--r--apps/web/src/components/editor/onboarding.tsx227
1 files changed, 113 insertions, 114 deletions
diff --git a/apps/web/src/components/editor/onboarding.tsx b/apps/web/src/components/editor/onboarding.tsx
index 8ee60690..c60c2de5 100644
--- a/apps/web/src/components/editor/onboarding.tsx
+++ b/apps/web/src/components/editor/onboarding.tsx
@@ -1,138 +1,137 @@
"use client";
-import { Dialog, DialogContent, DialogTitle } from "../ui/dialog";
-import { Button } from "../ui/button";
import { ArrowRightIcon } from "lucide-react";
-import { useState, useEffect } from "react";
+import { useState } from "react";
import ReactMarkdown from "react-markdown";
+import { SOCIAL_LINKS } from "@/constants/site-constants";
+import { useLocalStorage } from "@/hooks/storage/use-local-storage";
+import { Button } from "../ui/button";
+import { Dialog, DialogBody, DialogContent, DialogTitle } from "../ui/dialog";
export function Onboarding() {
- const [step, setStep] = useState(0);
- const [isOpen, setIsOpen] = useState(false);
+ const [step, setStep] = useState(0);
+ const [hasSeenOnboarding, setHasSeenOnboarding] = useLocalStorage({
+ key: "hasSeenOnboarding",
+ defaultValue: false,
+ });
- useEffect(() => {
- const hasSeenOnboarding = localStorage.getItem("hasSeenOnboarding");
- if (!hasSeenOnboarding) {
- setIsOpen(true);
- }
- }, []);
+ const isOpen = !hasSeenOnboarding;
- const handleNext = () => {
- setStep(step + 1);
- };
+ const handleNext = () => {
+ setStep(step + 1);
+ };
- const handleClose = () => {
- setIsOpen(false);
- localStorage.setItem("hasSeenOnboarding", "true");
- };
+ const handleClose = () => {
+ setHasSeenOnboarding({ value: true });
+ };
- const getStepTitle = () => {
- switch (step) {
- case 0:
- return "Welcome to OpenCut Beta! 🎉";
- case 1:
- return "⚠️ This is a super early beta!";
- case 2:
- return "🦋 Have fun testing!";
- default:
- return "OpenCut Onboarding";
- }
- };
+ const getStepTitle = () => {
+ switch (step) {
+ case 0:
+ return "Welcome to OpenCut Beta! 🎉";
+ case 1:
+ return "⚠️ This is a super early beta!";
+ case 2:
+ return "🦋 Have fun testing!";
+ default:
+ return "OpenCut Onboarding";
+ }
+ };
- const renderStepContent = () => {
- switch (step) {
- case 0:
- return (
- <div className="space-y-5">
- <div className="space-y-3">
- <Title title="Welcome to OpenCut Beta! 🎉" />
- <Description description="You're among the first to try OpenCut - the fully open source CapCut alternative." />
- </div>
- <NextButton onClick={handleNext}>Next</NextButton>
- </div>
- );
- case 1:
- return (
- <div className="space-y-5">
- <div className="space-y-3">
- <Title title={getStepTitle()} />
- <Description description="OpenCut started just one month ago. There's still a ton of things to do to make this editor amazing." />
- <Description description="A lot of features are still missing, like the export functionality. We're working hard to build them out!" />
- <Description description="If you're curious, check out our roadmap [here](https://opencut.app/roadmap)" />
- </div>
- <NextButton onClick={handleNext}>Next</NextButton>
- </div>
- );
- case 2:
- return (
- <div className="space-y-5">
- <div className="space-y-3">
- <Title title={getStepTitle()} />
- <Description description="Join our [Discord](https://discord.gg/zmR9N35cjK), chat with cool people and share feedback to help make OpenCut the best editor ever." />
- </div>
- <NextButton onClick={handleClose}>Finish</NextButton>
- </div>
- );
- default:
- return null;
- }
- };
+ const renderStepContent = () => {
+ switch (step) {
+ case 0:
+ return (
+ <div className="space-y-5">
+ <div className="space-y-3">
+ <Title title="Welcome to OpenCut Beta! 🎉" />
+ <Description description="You're among the first to try OpenCut - the fully open source CapCut alternative." />
+ </div>
+ <NextButton onClick={handleNext}>Next</NextButton>
+ </div>
+ );
+ case 1:
+ return (
+ <div className="space-y-5">
+ <div className="space-y-3">
+ <Title title={getStepTitle()} />
+ <Description description="OpenCut started just one month ago. There's still a ton of things to do to make this editor amazing." />
+ <Description description="A lot of features are still missing, like the export functionality. We're working hard to build them out!" />
+ <Description description="If you're curious, check out our roadmap [here](https://opencut.app/roadmap)" />
+ </div>
+ <NextButton onClick={handleNext}>Next</NextButton>
+ </div>
+ );
+ case 2:
+ return (
+ <div className="space-y-5">
+ <div className="space-y-3">
+ <Title title={getStepTitle()} />
+ <Description
+ description={`Join our [Discord](${SOCIAL_LINKS.discord}), chat with cool people and share feedback to help make OpenCut the best editor ever.`}
+ />
+ </div>
+ <NextButton onClick={handleClose}>Finish</NextButton>
+ </div>
+ );
+ default:
+ return null;
+ }
+ };
- return (
- <Dialog open={isOpen} onOpenChange={handleClose}>
- <DialogContent className="sm:max-w-[425px] !outline-none pt-2">
- <DialogTitle>
- <span className="sr-only">{getStepTitle()}</span>
- </DialogTitle>
- {renderStepContent()}
- </DialogContent>
- </Dialog>
- );
+ return (
+ <Dialog open={isOpen} onOpenChange={handleClose}>
+ <DialogContent className="sm:max-w-[425px]">
+ <DialogTitle>
+ <span className="sr-only">{getStepTitle()}</span>
+ </DialogTitle>
+ <DialogBody>
+ {renderStepContent()}
+ </DialogBody>
+ </DialogContent>
+ </Dialog>
+ );
}
function Title({ title }: { title: string }) {
- return <h2 className="text-lg md:text-xl font-bold">{title}</h2>;
-}
-
-function Subtitle({ subtitle }: { subtitle: string }) {
- return <h3 className="text-lg font-medium">{subtitle}</h3>;
+ return <h2 className="text-lg font-bold md:text-xl">{title}</h2>;
}
function Description({ description }: { description: string }) {
- return (
- <div className="text-muted-foreground">
- <ReactMarkdown
- components={{
- p: ({ children }) => <p className="mb-0">{children}</p>,
- a: ({ href, children }) => (
- <a
- href={href}
- target="_blank"
- rel="noopener noreferrer"
- className="text-foreground hover:text-foreground/80 underline"
- >
- {children}
- </a>
- ),
- }}
- >
- {description}
- </ReactMarkdown>
- </div>
- );
+ return (
+ <div className="text-muted-foreground">
+ <ReactMarkdown
+ components={{
+ p: ({ children }) => <p className="mb-0">{children}</p>,
+ a: ({ href, children }) => (
+ <a
+ href={href}
+ target="_blank"
+ rel="noopener noreferrer"
+ className="text-foreground hover:text-foreground/80 underline"
+ >
+ {children}
+ </a>
+ ),
+ }}
+ >
+ {description}
+ </ReactMarkdown>
+ </div>
+ );
}
function NextButton({
- children,
- onClick,
+ children,
+ onClick,
}: {
- children: React.ReactNode;
- onClick: () => void;
+ children: React.ReactNode;
+ onClick: () => void;
}) {
- return (
- <Button onClick={onClick} variant="default" className="w-full">
- {children}
- <ArrowRightIcon className="w-4 h-4" />
- </Button>
- );
+ return (
+ <Button onClick={onClick} variant="default" className="w-full">
+ {children}
+ <ArrowRightIcon className="size-4" />
+ </Button>
+ );
}