diff --git a/src/hooks/useAppVersion.ts b/src/hooks/useAppVersion.ts new file mode 100644 index 0000000..ede62b2 --- /dev/null +++ b/src/hooks/useAppVersion.ts @@ -0,0 +1,20 @@ +import { useState, useEffect } from "react"; +import { IpcClient } from "@/ipc/ipc_client"; + +export function useAppVersion() { + const [appVersion, setAppVersion] = useState(null); + + useEffect(() => { + const fetchVersion = async () => { + try { + const version = await IpcClient.getInstance().getAppVersion(); + setAppVersion(version); + } catch (error) { + setAppVersion(null); + } + }; + fetchVersion(); + }, []); + + return appVersion; +} diff --git a/src/lib/schemas.ts b/src/lib/schemas.ts index 2cc9287..b5d6d32 100644 --- a/src/lib/schemas.ts +++ b/src/lib/schemas.ts @@ -117,6 +117,7 @@ export const UserSettingsSchema = z.object({ enableDyadPro: z.boolean().optional(), dyadProBudget: DyadProBudgetSchema.optional(), experiments: ExperimentsSchema.optional(), + lastShownReleaseNotesVersion: z.string().optional(), // DEPRECATED. runtimeMode: RuntimeModeSchema.optional(), }); diff --git a/src/pages/home.tsx b/src/pages/home.tsx index 08b4a4d..ea8a684 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -14,6 +14,16 @@ import { HomeChatInput } from "@/components/chat/HomeChatInput"; import { usePostHog } from "posthog-js/react"; import { PrivacyBanner } from "@/components/TelemetryBanner"; import { INSPIRATION_PROMPTS } from "@/prompts/inspiration_prompts"; +import { useAppVersion } from "@/hooks/useAppVersion"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { useTheme } from "@/contexts/ThemeContext"; +import { Button } from "@/components/ui/button"; +import { ExternalLink } from "lucide-react"; export default function HomePage() { const [inputValue, setInputValue] = useAtom(homeChatInputValueAtom); @@ -21,11 +31,40 @@ export default function HomePage() { const search = useSearch({ from: "/" }); const setSelectedAppId = useSetAtom(selectedAppIdAtom); const { refreshApps } = useLoadApps(); - const { settings, isAnyProviderSetup } = useSettings(); + const { settings, updateSettings } = useSettings(); const setIsPreviewOpen = useSetAtom(isPreviewOpenAtom); const [isLoading, setIsLoading] = useState(false); const { streamMessage } = useStreamChat({ hasChatId: false }); const posthog = usePostHog(); + const appVersion = useAppVersion(); + const [releaseNotesOpen, setReleaseNotesOpen] = useState(false); + const [releaseUrl, setReleaseUrl] = useState(""); + const { theme } = useTheme(); + + useEffect(() => { + const updateLastVersionLaunched = async () => { + if ( + appVersion && + appVersion.match(/^\d+\.\d+\.\d+$/) && + settings && + settings.lastShownReleaseNotesVersion !== appVersion + ) { + await updateSettings({ + lastShownReleaseNotesVersion: appVersion, + }); + + // Check if release notes exist for this version + const url = `https://www.dyad.sh/docs/releases/${appVersion}`; + const exists = await checkPageExists(url); + if (exists) { + setReleaseUrl(url + "?hideHeader=true&theme=" + theme); + setReleaseNotesOpen(true); + } + } + }; + updateLastVersionLaunched(); + }, [appVersion, settings, updateSettings]); + // Get the appId from search params const appId = search.appId ? Number(search.appId) : null; @@ -165,6 +204,51 @@ export default function HomePage() { + + {/* Release Notes Dialog */} + + + + What's new in v{appVersion}? + + +
+ {releaseUrl && ( +
+