Show onboarding banner to link to YT video & do not show release note on first-time (#1490)
<!-- This is an auto-generated description by cubic. --> ## Summary by cubic Adds an onboarding banner in Setup that links to a 3-minute YouTube intro and dismisses after click. Also avoids showing release notes on a user’s first launch. - **New Features** - Added OnboardingBanner component with YouTube thumbnail and play icon. - Opens the video via IpcClient and hides the banner after click. - Integrated into SetupBanner with local visibility state. - **Bug Fixes** - Don’t show release notes on first run to prevent spam. <!-- End of auto-generated description by cubic. --> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds a dismissible onboarding banner linking to a YouTube intro and changes release notes to appear only on app updates, not first run. > > - **UI/Onboarding**: > - **`src/components/home/OnboardingBanner.tsx`**: New banner component with YouTube thumbnail/play icon; opens intro video via `IpcClient.openExternalUrl`; hides itself on click. > - **`src/components/SetupBanner.tsx`**: Integrates `OnboardingBanner` with `isOnboardingVisible` state; tweaks header typography (`font-medium`). > - **Release Notes Behavior**: > - **`src/pages/home.tsx`**: Only shows release notes when upgrading (checks existing `settings.lastShownReleaseNotesVersion` before opening modal); preserves themed URL handling. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 832e70f87b3b6a7e8b8b921fe8c6ceb15198cce0. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
This commit is contained in:
@@ -30,6 +30,7 @@ import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders";
|
|||||||
import { useScrollAndNavigateTo } from "@/hooks/useScrollAndNavigateTo";
|
import { useScrollAndNavigateTo } from "@/hooks/useScrollAndNavigateTo";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import logo from "../../assets/logo.svg";
|
import logo from "../../assets/logo.svg";
|
||||||
|
import { OnboardingBanner } from "./home/OnboardingBanner";
|
||||||
|
|
||||||
type NodeInstallStep =
|
type NodeInstallStep =
|
||||||
| "install"
|
| "install"
|
||||||
@@ -40,6 +41,7 @@ type NodeInstallStep =
|
|||||||
export function SetupBanner() {
|
export function SetupBanner() {
|
||||||
const posthog = usePostHog();
|
const posthog = usePostHog();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [isOnboardingVisible, setIsOnboardingVisible] = useState(true);
|
||||||
const { isAnyProviderSetup, isLoading: loading } =
|
const { isAnyProviderSetup, isLoading: loading } =
|
||||||
useLanguageModelProviders();
|
useLanguageModelProviders();
|
||||||
const [nodeSystemInfo, setNodeSystemInfo] = useState<NodeSystemInfo | null>(
|
const [nodeSystemInfo, setNodeSystemInfo] = useState<NodeSystemInfo | null>(
|
||||||
@@ -147,7 +149,13 @@ export function SetupBanner() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className="text-xl text-zinc-700 dark:text-zinc-300 p-4">Setup Dyad</p>
|
<p className="text-xl font-medium text-zinc-700 dark:text-zinc-300 p-4">
|
||||||
|
Setup Dyad
|
||||||
|
</p>
|
||||||
|
<OnboardingBanner
|
||||||
|
isVisible={isOnboardingVisible}
|
||||||
|
setIsVisible={setIsOnboardingVisible}
|
||||||
|
/>
|
||||||
<div className={bannerClasses}>
|
<div className={bannerClasses}>
|
||||||
<Accordion
|
<Accordion
|
||||||
type="multiple"
|
type="multiple"
|
||||||
|
|||||||
56
src/components/home/OnboardingBanner.tsx
Normal file
56
src/components/home/OnboardingBanner.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { IpcClient } from "@/ipc/ipc_client";
|
||||||
|
import { Play } from "lucide-react";
|
||||||
|
|
||||||
|
export const OnboardingBanner = ({
|
||||||
|
isVisible,
|
||||||
|
setIsVisible,
|
||||||
|
}: {
|
||||||
|
isVisible: boolean;
|
||||||
|
setIsVisible: (isVisible: boolean) => void;
|
||||||
|
}) => {
|
||||||
|
if (!isVisible) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
// <div className="fixed top-0 left-0 right-0 z-50 flex justify-center mt-2">
|
||||||
|
<div className="max-w-xl w-full mx-4 relative mb-4">
|
||||||
|
<a
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
IpcClient.getInstance().openExternalUrl(
|
||||||
|
"https://www.youtube.com/watch?v=rgdNoHLaRN4",
|
||||||
|
);
|
||||||
|
setIsVisible(false);
|
||||||
|
}}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="cursor-pointer block bg-(--background-lightest) border border-border rounded-lg shadow-lg hover:bg-accent transition-colors"
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="relative p-2">
|
||||||
|
<img
|
||||||
|
src="https://img.youtube.com/vi/rgdNoHLaRN4/maxresdefault.jpg"
|
||||||
|
alt="Get started with Dyad in 3 minutes"
|
||||||
|
className="w-28 h-16 object-cover rounded-md"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
|
<div className="w-10 h-10 bg-background rounded-full flex items-center justify-center shadow-md">
|
||||||
|
<Play size={20} className="text-foreground ml-0.5" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 px-4 py-3">
|
||||||
|
<div className="text-foreground">
|
||||||
|
<p className="font-semibold text-base">
|
||||||
|
Get started with Dyad in 3 minutes
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Start building your app for free
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
// </div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -62,9 +62,15 @@ export default function HomePage() {
|
|||||||
settings &&
|
settings &&
|
||||||
settings.lastShownReleaseNotesVersion !== appVersion
|
settings.lastShownReleaseNotesVersion !== appVersion
|
||||||
) {
|
) {
|
||||||
|
const shouldShowReleaseNotes = !!settings.lastShownReleaseNotesVersion;
|
||||||
await updateSettings({
|
await updateSettings({
|
||||||
lastShownReleaseNotesVersion: appVersion,
|
lastShownReleaseNotesVersion: appVersion,
|
||||||
});
|
});
|
||||||
|
// It feels spammy to show release notes if it's
|
||||||
|
// the users very first time.
|
||||||
|
if (!shouldShowReleaseNotes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await IpcClient.getInstance().doesReleaseNoteExist({
|
const result = await IpcClient.getInstance().doesReleaseNoteExist({
|
||||||
|
|||||||
Reference in New Issue
Block a user