refactor: shared Google sign-in hook + remove Android false advertising #4752
1 changed file+33−69
Modifiedlanding/src/App.tsx+33−69View fileUnifiedSplit
@@ -33,6 +33,28 @@ import {
3333 PRICE_LIFETIME,
3434} from "./lib/stripe";
3535
36/** Shared hook — wraps useGoogleLogin with userinfo fetch so every component uses the same flow. */
37function useGoogleSignIn(onSignIn: (u: GoogleUser) => void) {
38 return useGoogleLogin({
39 flow: "implicit",
40 onSuccess: async (response) => {
41 try {
42 const res = await fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
43 headers: { Authorization: `Bearer ${response.access_token}` },
44 });
45 const info = await res.json() as { email: string; name: string; picture: string; sub: string };
46 storeToken(response.access_token);
47 onSignIn({ email: info.email, name: info.name, picture: info.picture, sub: info.sub });
48 } catch {
49 if ("id_token" in response && typeof response.id_token === "string") {
50 const u = parseIdToken(response.id_token);
51 if (u) onSignIn(u);
52 }
53 }
54 },
55 });
56}
57
3658const fadeUp = {
3759 hidden: { opacity: 0, y: 30 },
3860 visible: { opacity: 1, y: 0, transition: { duration: 0.6 } },
@@ -136,24 +158,7 @@ function Navbar({
136158}) {
137159 const [menuOpen, setMenuOpen] = useState(false);
138160
139 const login = useGoogleLogin({
140 flow: "implicit",
141 onSuccess: async (response) => {
142 try {
143 const res = await fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
144 headers: { Authorization: `Bearer ${response.access_token}` },
145 });
146 const info = await res.json() as { email: string; name: string; picture: string; sub: string };
147 storeToken(response.access_token);
148 onSignIn({ email: info.email, name: info.name, picture: info.picture, sub: info.sub });
149 } catch {
150 if ("id_token" in response && typeof response.id_token === "string") {
151 const u = parseIdToken(response.id_token);
152 if (u) onSignIn(u);
153 }
154 }
155 },
156 });
161 const login = useGoogleSignIn(onSignIn);
157162
158163 return (
159164 <nav className="fixed top-0 w-full z-50 border-b border-white/5 bg-[#09090b]/80 backdrop-blur-xl">
@@ -237,19 +242,7 @@ function Navbar({
237242}
238243
239244function Hero({ user, onSignIn }: { user: GoogleUser | null; onSignIn: (u: GoogleUser) => void }) {
240 const login = useGoogleLogin({
241 flow: "implicit",
242 onSuccess: async (response) => {
243 try {
244 const res = await fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
245 headers: { Authorization: `Bearer ${response.access_token}` },
246 });
247 const info = await res.json() as { email: string; name: string; picture: string; sub: string };
248 storeToken(response.access_token);
249 onSignIn({ email: info.email, name: info.name, picture: info.picture, sub: info.sub });
250 } catch {}
251 },
252 });
245 const login = useGoogleSignIn(onSignIn);
253246
254247 return (
255248 <section className="relative pt-32 pb-20 overflow-hidden glow-hero">
@@ -287,7 +280,7 @@ function Hero({ user, onSignIn }: { user: GoogleUser | null; onSignIn: (u: Googl
287280 >
288281 The AI voice dictation tool built for professionals who can't afford mistakes.
289282 Real-time transcription, Claude AI grammar correction, and zero-retention privacy.
290 Works in every app on Mac, Windows, iPhone, and Android.{" "}
283 Works in every app on Mac, Windows, and iPhone.{" "}
291284 <span className="text-white font-medium">
292285 Loved by lawyers, accountants, doctors, and executives.
293286 </span>
@@ -341,9 +334,6 @@ function Hero({ user, onSignIn }: { user: GoogleUser | null; onSignIn: (u: Googl
341334 <span className="flex items-center gap-1.5">
342335 <Smartphone className="h-3.5 w-3.5" /> iOS
343336 </span>
344 <span className="flex items-center gap-1.5">
345 <Smartphone className="h-3.5 w-3.5" /> Android
346 </span>
347337 </motion.div>
348338 </motion.div>
349339
@@ -434,7 +424,7 @@ function TrustBar() {
434424 { value: "95%+", label: "Transcription accuracy" },
435425 { value: "<300ms", label: "Real-time latency" },
436426 { value: "20+", label: "Languages supported" },
437 { value: "4", label: "Platforms: Mac, Win, iOS, Android" },
427 { value: "4", label: "Platforms: Mac, Win, iOS" },
438428 ];
439429 return (
440430 <section className="border-y border-white/5 bg-[#0c0c0f] py-8">
@@ -511,7 +501,7 @@ function Platforms() {
511501 <span className="text-zinc-500">Every device you own.</span>
512502 </motion.h2>
513503 <motion.p variants={fadeUp} className="mt-4 text-zinc-400 max-w-xl mx-auto">
514 Unlike Dragon (Windows-only) or Wispr Flow (Mac/iOS-only), Voxlen works everywhere. Mac, Windows, iPhone, and Android — same account, same vocabulary, same AI.
504 Unlike Dragon (Windows-only) or Wispr Flow (Mac/iOS-only), Voxlen works everywhere. Mac, Windows, and iPhone — same account, same vocabulary, same AI.
515505 </motion.p>
516506 </motion.div>
517507
@@ -713,8 +703,8 @@ function Features() {
713703 },
714704 {
715705 icon: Keyboard,
716 title: "Android Keyboard",
717 description: "Full custom keyboard for Android with Deepgram Nova-3 streaming STT, AI grammar polish, haptic feedback, and dark mode. Replace your stock keyboard with professional-grade dictation.",
706 title: "iOS Keyboard Extension",
707 description: "Custom keyboard for iPhone and iPad — dictate in any app including iMessage, WhatsApp, Mail, and legal practice software. Same Nova-3 accuracy as the desktop app.",
718708 color: "text-green-400",
719709 bg: "bg-green-400/10",
720710 },
@@ -921,21 +911,7 @@ function Comparison() {
921911}
922912
923913function Pricing({ user, onSignIn }: { user: GoogleUser | null; onSignIn: (u: GoogleUser) => void }) {
924 const login = useGoogleLogin({
925 flow: "implicit",
926 onSuccess: async (response) => {
927 try {
928 const res = await fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
929 headers: { Authorization: `Bearer ${response.access_token}` },
930 });
931 const info = await res.json() as { email: string; name: string; picture: string; sub: string };
932 storeToken(response.access_token);
933 const u = { email: info.email, name: info.name, picture: info.picture, sub: info.sub };
934 onSignIn(u);
935 // After sign-in, proceed to checkout (will scroll to download if Stripe not configured)
936 } catch {}
937 },
938 });
914 const login = useGoogleSignIn(onSignIn);
939915
940916 const handleCta = (priceId: string) => {
941917 if (!user) {
@@ -972,7 +948,7 @@ function Pricing({ user, onSignIn }: { user: GoogleUser | null; onSignIn: (u: Go
972948 "Text injection (any app)",
973949 "Voice commands",
974950 "20+ languages",
975 "iOS + Android keyboard",
951 "iOS keyboard extension",
976952 "macOS, Windows, Linux",
977953 "Priority email support",
978954 ],
@@ -1151,7 +1127,7 @@ function FAQ() {
11511127 },
11521128 {
11531129 q: "Is Voxlen a Dragon NaturallySpeaking alternative?",
1154 a: "Yes — and a better one. Voxlen uses Deepgram Nova-3 (more accurate than Dragon's aging engine), costs a fraction of Dragon's $699+ license, works on Mac AND Windows AND iPhone AND Android (Dragon is Windows-only), and includes AI grammar correction that Dragon lacks. No one-time $700 fee, no USB dongle, no outdated acoustic models.",
1130 a: "Yes — and a better one. Voxlen uses Deepgram Nova-3 (more accurate than Dragon's aging engine), costs a fraction of Dragon's $699+ license, works on Mac AND Windows AND iPhone (Dragon is Windows-only), and includes AI grammar correction that Dragon lacks. No one-time $700 fee, no USB dongle, no outdated acoustic models.",
11551131 },
11561132 {
11571133 q: "Does Voxlen work on iPhone and Samsung Android?",
@@ -1297,19 +1273,7 @@ const DOWNLOADS: Record<
12971273function CTA({ user, onSignIn }: { user: GoogleUser | null; onSignIn: (u: GoogleUser) => void }) {
12981274 const [platform, setPlatform] = useState<Platform>("unknown");
12991275
1300 const login = useGoogleLogin({
1301 flow: "implicit",
1302 onSuccess: async (response) => {
1303 try {
1304 const res = await fetch("https://www.googleapis.com/oauth2/v3/userinfo", {
1305 headers: { Authorization: `Bearer ${response.access_token}` },
1306 });
1307 const info = await res.json() as { email: string; name: string; picture: string; sub: string };
1308 storeToken(response.access_token);
1309 onSignIn({ email: info.email, name: info.name, picture: info.picture, sub: info.sub });
1310 } catch {}
1311 },
1312 });
1276 const login = useGoogleSignIn(onSignIn);
13131277 const [liveAssets, setLiveAssets] = useState<ReleaseAsset[] | null>(null);
13141278 const [hasRelease, setHasRelease] = useState<boolean | null>(null);
13151279
13161280
c comment · e edit title · m merge · a approve · r request changes · ? shortcuts