Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/components/HomeIntro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,11 @@ function WavyLine() {
export function HomeIntro({
activeStep,
showIntro,
isMobile = false,
}: {
activeStep: string | null;
showIntro: boolean;
isMobile?: boolean;
}) {
const [open, setOpen] = useState(
activeStep === null ||
Expand Down Expand Up @@ -122,7 +124,7 @@ export function HomeIntro({
front-ends.
</motion.p>
<WavyLine />
<ul className="flex flex-wrap gap-4">
<ul className="flex flex-wrap gap-4 justify-center">
<FeatureItem emoji={"🚀"} title="User actions feel instant">
Updates show immediately while sync happens in the background.
</FeatureItem>
Expand All @@ -143,11 +145,18 @@ export function HomeIntro({
Sounds amazing, right? Learn the basics with this interactive
tutorial in 6-7 minutes!
</p>
{isMobile && (
<p className="max-w-sm text-destructive text-center font-bold text-sm">
This interactive tutorial requires features not available on
mobile browsers. Please visit on a desktop computer.
</p>
)}
</CardContent>
<CardFooter className="flex justify-center items-center flex-col gap-10">
<Button
onClick={startTutorial}
className="bg-primary cursor-pointer"
disabled={isMobile}
>
Get started <ArrowRightIcon />
</Button>
Expand Down
22 changes: 21 additions & 1 deletion src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.

import { Route as rootRouteImport } from './routes/__root'
import { Route as MobileRouteImport } from './routes/mobile'
import { Route as TutorialRouteImport } from './routes/_tutorial'
import { Route as IndexRouteImport } from './routes/index'
import { Route as TutorialDbRouteImport } from './routes/_tutorial._db'
Expand All @@ -17,6 +18,11 @@ import { Route as TutorialDbProjectRootRouteImport } from './routes/_tutorial._d
import { Route as TutorialDbProjectsIndexRouteImport } from './routes/_tutorial._db.projects.index'
import { Route as TutorialDbProjectsProjectIdRouteImport } from './routes/_tutorial._db.projects.$projectId'

const MobileRoute = MobileRouteImport.update({
id: '/mobile',
path: '/mobile',
getParentRoute: () => rootRouteImport,
} as any)
const TutorialRoute = TutorialRouteImport.update({
id: '/_tutorial',
getParentRoute: () => rootRouteImport,
Expand Down Expand Up @@ -54,13 +60,15 @@ const TutorialDbProjectsProjectIdRoute =

export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/mobile': typeof MobileRoute
'/project-root': typeof TutorialDbProjectRootRoute
'/projects': typeof TutorialDbProjectsRouteWithChildren
'/projects/$projectId': typeof TutorialDbProjectsProjectIdRoute
'/projects/': typeof TutorialDbProjectsIndexRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/mobile': typeof MobileRoute
'/project-root': typeof TutorialDbProjectRootRoute
'/projects/$projectId': typeof TutorialDbProjectsProjectIdRoute
'/projects': typeof TutorialDbProjectsIndexRoute
Expand All @@ -69,6 +77,7 @@ export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/_tutorial': typeof TutorialRouteWithChildren
'/mobile': typeof MobileRoute
'/_tutorial/_db': typeof TutorialDbRouteWithChildren
'/_tutorial/_db/project-root': typeof TutorialDbProjectRootRoute
'/_tutorial/_db/projects': typeof TutorialDbProjectsRouteWithChildren
Expand All @@ -79,16 +88,18 @@ export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths:
| '/'
| '/mobile'
| '/project-root'
| '/projects'
| '/projects/$projectId'
| '/projects/'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/project-root' | '/projects/$projectId' | '/projects'
to: '/' | '/mobile' | '/project-root' | '/projects/$projectId' | '/projects'
id:
| '__root__'
| '/'
| '/_tutorial'
| '/mobile'
| '/_tutorial/_db'
| '/_tutorial/_db/project-root'
| '/_tutorial/_db/projects'
Expand All @@ -99,10 +110,18 @@ export interface FileRouteTypes {
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
TutorialRoute: typeof TutorialRouteWithChildren
MobileRoute: typeof MobileRoute
}

declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/mobile': {
id: '/mobile'
path: '/mobile'
fullPath: '/mobile'
preLoaderRoute: typeof MobileRouteImport
parentRoute: typeof rootRouteImport
}
'/_tutorial': {
id: '/_tutorial'
path: ''
Expand Down Expand Up @@ -197,6 +216,7 @@ const TutorialRouteWithChildren = TutorialRoute._addFileChildren(
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
TutorialRoute: TutorialRouteWithChildren,
MobileRoute: MobileRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
Expand Down
16 changes: 16 additions & 0 deletions src/routes/__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ClientOnly,
createRootRouteWithContext,
HeadContent,
redirect,
ScriptOnce,
Scripts,
} from "@tanstack/react-router";
Expand All @@ -16,6 +17,7 @@ import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Toaster } from "@/components/ui/sonner";
import { client, idbName } from "@/db";
import { getIsMobile } from "@/server/functions/getIsMobile";
import { seo } from "@/utils/seo";
import appCss from "../styles.css?url";

Expand All @@ -24,6 +26,20 @@ interface MyRouterContext {
}

export const Route = createRootRouteWithContext<MyRouterContext>()({
beforeLoad: async ({ location }) => {
// Skip redirect if already on /mobile to avoid infinite loop
if (location.pathname === "/mobile") {
return;
}

const isMobile = await getIsMobile();

if (isMobile) {
throw redirect({
to: "/mobile",
});
}
},
head: () => ({
meta: [
{
Expand Down
14 changes: 14 additions & 0 deletions src/routes/mobile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createFileRoute } from "@tanstack/react-router";
import { HomeIntro } from "@/components/HomeIntro";

export const Route = createFileRoute("/mobile")({
component: RouteComponent,
});

function RouteComponent() {
return (
<div className="min-h-screen bg-background">
<HomeIntro activeStep={null} showIntro={true} isMobile={true} />
</div>
);
}
9 changes: 9 additions & 0 deletions src/server/functions/getIsMobile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createServerFn } from "@tanstack/react-start";
import { getRequestHeader } from "@tanstack/react-start/server";

export const getIsMobile = createServerFn().handler(async () => {
const userAgent = getRequestHeader("user-agent") || "";
const mobileRegex =
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
return mobileRegex.test(userAgent);
});