Skip to content

Commit 9723c9f

Browse files
committed
more goodness
1 parent 69d1fae commit 9723c9f

File tree

6 files changed

+103
-40
lines changed

6 files changed

+103
-40
lines changed

app.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,8 @@ export default defineConfig({
1111
},
1212
server: {
1313
preset: "vercel",
14+
prerender: {
15+
routes: ["/", "/pricing"],
16+
},
1417
},
1518
});

app/routes/__root.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,25 @@ export const Route = createRootRoute({
5050
links: [
5151
{ rel: "stylesheet", href: globalCss },
5252
{ rel: "stylesheet", href: clerkCss },
53+
{
54+
rel: "apple-touch-icon",
55+
sizes: "180x180",
56+
href: "/apple-touch-icon.png",
57+
},
58+
{
59+
rel: "icon",
60+
type: "image/png",
61+
sizes: "32x32",
62+
href: "/favicon-32x32.png",
63+
},
64+
{
65+
rel: "icon",
66+
type: "image/png",
67+
sizes: "16x16",
68+
href: "/favicon-16x16.png",
69+
},
70+
{ rel: "manifest", href: "/site.webmanifest", color: "#fffff" },
71+
{ rel: "icon", href: "/favicon.ico" },
5372
],
5473
}),
5574
component: RootComponent,

app/routes/p.$videoId.tsx

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,60 @@
1-
import { createFileRoute, Link, redirect } from "@tanstack/react-router";
2-
import { queryClient } from "./__root";
3-
import { videoQueryOptions } from "../lib/query-utils";
4-
import { useQuery } from "@tanstack/react-query";
5-
import { ViewIncrementer } from "../components/view-incrementer";
6-
import {
7-
EyeIcon,
8-
Loader2Icon,
9-
SquareArrowOutUpRightIcon,
10-
VideoIcon,
11-
} from "lucide-react";
121
import { Button } from "@/components/ui/button";
2+
import { Card, CardContent } from "@/components/ui/card";
133
import { useUser } from "@clerk/tanstack-start";
4+
import { getAuth } from "@clerk/tanstack-start/server";
5+
import { Separator } from "@radix-ui/react-dropdown-menu";
6+
import {
7+
Link,
8+
createFileRoute,
9+
notFound,
10+
redirect,
11+
} from "@tanstack/react-router";
12+
import { createServerFn } from "@tanstack/start";
13+
import { getWebRequest } from "@tanstack/start/server";
1414
import { MediaPlayer, MediaProvider, Poster } from "@vidstack/react";
1515
import {
16-
defaultLayoutIcons,
1716
DefaultVideoLayout,
17+
defaultLayoutIcons,
1818
} from "@vidstack/react/player/layouts/default";
19-
import { Card, CardContent } from "@/components/ui/card";
20-
import { WordyDate } from "../components/wordy-date";
21-
import { AuthorInfo } from "../components/author-info";
22-
import { Separator } from "@radix-ui/react-dropdown-menu";
23-
24-
import themeCss from "@vidstack/react/player/styles/default/theme.css?url";
2519
import audioCss from "@vidstack/react/player/styles/default/layouts/audio.css?url";
2620
import videoCss from "@vidstack/react/player/styles/default/layouts/video.css?url";
21+
import themeCss from "@vidstack/react/player/styles/default/theme.css?url";
22+
import {
23+
EyeIcon,
24+
Loader2Icon,
25+
SquareArrowOutUpRightIcon,
26+
VideoIcon,
27+
} from "lucide-react";
28+
import { z } from "zod";
29+
import { AuthorInfo } from "../components/author-info";
30+
import { ViewIncrementer } from "../components/view-incrementer";
31+
import { WordyDate } from "../components/wordy-date";
32+
import { getVideoDataServerFn } from "../server-fns/video-player";
33+
34+
const fetchVideoData = createServerFn({ method: "POST" })
35+
.validator(z.object({ videoId: z.string() }))
36+
.handler(async ({ data }) => {
37+
const video = await getVideoDataServerFn({
38+
data: { videoId: data.videoId },
39+
});
40+
41+
if (video.videoData.isPrivate) {
42+
const { userId } = await getAuth(getWebRequest()!);
43+
44+
if (userId !== video.videoData.authorId) {
45+
throw notFound();
46+
}
47+
}
48+
49+
return video;
50+
});
2751

2852
export const Route = createFileRoute("/p/$videoId")({
2953
component: RouteComponent,
3054
loader: ({ params }) => {
31-
if (!params.videoId) {
32-
throw redirect({ to: "/" });
33-
}
34-
35-
queryClient.prefetchQuery(videoQueryOptions(params.videoId));
55+
return fetchVideoData({ data: { videoId: params.videoId } });
3656
},
57+
ssr: true,
3758
head: () => ({
3859
links: [
3960
{ rel: "stylesheet", href: themeCss },
@@ -45,16 +66,11 @@ export const Route = createFileRoute("/p/$videoId")({
4566

4667
function RouteComponent() {
4768
const { videoId } = Route.useParams();
48-
49-
const { data } = useQuery(videoQueryOptions(videoId));
69+
const video = Route.useLoaderData();
5070

5171
const { user } = useUser();
5272

53-
if (!data) {
54-
return null;
55-
}
56-
57-
const { videoData, videoSources } = data;
73+
const { videoData, videoSources } = video;
5874

5975
const isViewerAuthor = user?.id === videoData.authorId;
6076

@@ -100,18 +116,18 @@ function RouteComponent() {
100116
streamType="on-demand"
101117
playsInline
102118
title={videoData.title}
103-
poster={data.largeThumbnailUrl ?? undefined}
119+
poster={video.largeThumbnailUrl ?? undefined}
104120
duration={videoData.videoLengthSeconds ?? undefined}
105121
storage="player"
106122
>
107123
<MediaProvider>
108-
{data.largeThumbnailUrl !== null && (
109-
<Poster className="vds-poster" src={data.largeThumbnailUrl} />
124+
{video.largeThumbnailUrl !== null && (
125+
<Poster className="vds-poster" src={video.largeThumbnailUrl} />
110126
)}
111127
</MediaProvider>
112128
<DefaultVideoLayout
113129
icons={defaultLayoutIcons}
114-
thumbnails={data.storyboard}
130+
thumbnails={video.storyboard}
115131
/>
116132
</MediaPlayer>
117133
<div className="flex flex-col gap-4 min-w-96 w-96 grow">
@@ -120,7 +136,7 @@ function RouteComponent() {
120136
<h1 className="text-2xl font-bold">{videoData.title}</h1>
121137
<div className="flex flex-col md:flex-row items-start md:items-center justify-between text-sm text-muted-foreground">
122138
<span>
123-
Uploaded on <WordyDate timestamp={data.videoCreatedAt} />
139+
Uploaded on <WordyDate timestamp={video.videoCreatedAt} />
124140
</span>
125141
<span className="flex items-center gap-1">
126142
<EyeIcon className="w-4 h-4" />

app/routes/videos.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import TopNav from "../components/top-nav";
2-
import { createFileRoute } from "@tanstack/react-router";
2+
import { createFileRoute, redirect } from "@tanstack/react-router";
33
import { usageDataQueryOptions, videosQueryOptions } from "../lib/query-utils";
44
import { DeleteVideoDialog } from "../components/dialogs/delete-video-dialog";
55
import { EditVideoDialog } from "../components/dialogs/edit-video-dialog";
@@ -12,17 +12,29 @@ import { UploadButton } from "../components/upload-button";
1212
import { UploadingVideosContainer } from "../components/uploading-videos-container";
1313
import { VideosBoard } from "../components/videos-board";
1414
import { useQuery } from "@tanstack/react-query";
15+
import { fetchClerkAuth } from "@/server-fns/clerk";
1516

1617
export const Route = createFileRoute("/videos")({
1718
component: RouteComponent,
19+
beforeLoad: async () => {
20+
const { userId } = await fetchClerkAuth();
21+
22+
if (!userId) {
23+
throw redirect({ to: "/sign-in/$" });
24+
}
25+
26+
return {
27+
userId,
28+
};
29+
},
1830
loader: () => {
19-
queryClient.ensureQueryData(videosQueryOptions);
20-
queryClient.ensureQueryData(usageDataQueryOptions);
31+
queryClient.prefetchQuery(videosQueryOptions);
32+
queryClient.prefetchQuery(usageDataQueryOptions);
2133
},
2234
});
2335

2436
function RouteComponent() {
25-
const { data: videoData, isLoading } = useQuery(videosQueryOptions);
37+
const { data: videoData } = useQuery(videosQueryOptions);
2638
const { data: usageData } = useQuery(usageDataQueryOptions);
2739

2840
return (

app/server-fns/clerk.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { getAuth } from "@clerk/tanstack-start/server";
2+
import { createServerFn } from "@tanstack/start";
3+
import { getWebRequest } from "@tanstack/start/server";
4+
5+
export const fetchClerkAuth = createServerFn({ method: "GET" }).handler(
6+
async () => {
7+
const { userId } = await getAuth(getWebRequest()!);
8+
9+
return {
10+
userId,
11+
};
12+
}
13+
);

biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"noExplicitAny": "warn"
2626
},
2727
"style": {
28-
"noNonNullAssertion": "warn"
28+
"noNonNullAssertion": "off"
2929
},
3030
"correctness": {
3131
"noChildrenProp": "off"

0 commit comments

Comments
 (0)