Skip to content

Commit a95e79d

Browse files
committed
fixing hydration issue because of using process.env.
1 parent 04c48a7 commit a95e79d

File tree

12 files changed

+556
-100
lines changed

12 files changed

+556
-100
lines changed

Diff for: TODO.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@
22

33
- if a user has purcahsed a course, show a message in the /purchase page so they don't get confused if they happen to navigate to that page on accident and do not show the buy now button or pricing there. instead show a button which will redirect them to the learn/$first-slug (use the slug hook)
44
- ability to download the entire video as a zip
5+
- if on last segment, show a "complete course" button and navigate to a certificate page
6+
- after clicking the Next Video button, I need to invalidate the queryClient cache for the "progress" entries
7+
- refactor the approach to instead just fetch the array of progress entries instead of doing the left join stuff
8+
- rename progress to completed_segments

Diff for: app/db/schema.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
serial,
88
text,
99
timestamp,
10+
uniqueIndex,
1011
} from "drizzle-orm/pg-core";
1112

1213
const PREFIX = "app";
@@ -84,12 +85,17 @@ export const progress = tableCreator(
8485
userId: serial("userId")
8586
.notNull()
8687
.references(() => users.id, { onDelete: "cascade" }),
87-
segmentId: serial("segmentId"),
88+
segmentId: serial("segmentId").references(() => segments.id, {
89+
onDelete: "cascade",
90+
}),
8891
createdAt: timestamp("created_at").notNull().defaultNow(),
8992
},
90-
(table) => [
91-
index("progress_user_id_segment_id_idx").on(table.userId, table.segmentId),
92-
]
93+
(table) => ({
94+
uniqueConstraint: uniqueIndex("progress_user_segment_unique_idx").on(
95+
table.userId,
96+
table.segmentId
97+
),
98+
})
9399
);
94100

95101
export const segmentsRelations = relations(segments, ({ many }) => ({

Diff for: app/db/seed.ts

+1-89
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,8 @@
11
import "dotenv/config";
22

3-
import { database } from "./index";
4-
import { accounts, profiles, users } from "~/db/schema";
5-
import { courses, segments } from "./schema";
6-
7-
async function main() {
8-
const [user] = await database
9-
.insert(users)
10-
.values({
11-
12-
emailVerified: undefined,
13-
})
14-
.onConflictDoNothing()
15-
.returning();
16-
17-
const [account] = await database
18-
.insert(accounts)
19-
.values({
20-
googleId: undefined,
21-
userId: user.id,
22-
})
23-
.onConflictDoNothing()
24-
.returning();
25-
26-
const [profile] = await database
27-
.insert(profiles)
28-
.values({
29-
userId: user.id,
30-
displayName: "Test User",
31-
})
32-
.onConflictDoNothing()
33-
.returning();
34-
}
3+
async function main() {}
354

365
async function seed() {
37-
// Create a test user
38-
const [user] = await database
39-
.insert(users)
40-
.values({
41-
42-
})
43-
.returning();
44-
45-
// Create a course
46-
const [course] = await database
47-
.insert(courses)
48-
.values({
49-
userId: user.id,
50-
title: "Introduction to TypeScript",
51-
category: "Programming",
52-
description: "Learn the basics of TypeScript",
53-
})
54-
.returning();
55-
56-
// Create 5 segments for the course
57-
const segmentsData = [
58-
{
59-
courseId: course.id,
60-
title: "TypeScript Basics",
61-
content:
62-
"Learn the basics of TypeScript including types, interfaces, and classes.",
63-
order: 1,
64-
},
65-
{
66-
courseId: course.id,
67-
title: "Advanced Types",
68-
content:
69-
"Explore advanced TypeScript types like unions, intersections, and generics.",
70-
order: 2,
71-
},
72-
{
73-
courseId: course.id,
74-
title: "TypeScript with React",
75-
content: "Learn how to use TypeScript with React components and hooks.",
76-
order: 3,
77-
},
78-
{
79-
courseId: course.id,
80-
title: "State Management",
81-
content: "Implement state management in TypeScript applications.",
82-
order: 4,
83-
},
84-
{
85-
courseId: course.id,
86-
title: "Testing TypeScript Code",
87-
content: "Learn how to write tests for TypeScript applications.",
88-
order: 5,
89-
},
90-
];
91-
92-
await database.insert(segments).values(segmentsData);
93-
946
console.log("Database seeded!");
957
}
968

Diff for: app/lib/auth.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { createMiddleware } from "@tanstack/start";
22
import { validateRequest } from "~/utils/auth";
33
import { redirect } from "@tanstack/react-router";
44
import { type User } from "~/db/schema";
5+
import { env } from "~/utils/env";
56

67
export function isAdmin(user: User | null) {
7-
return user?.email === process.env.ADMIN_EMAIL;
8+
return user?.email === env.ADMIN_EMAIL;
89
}
910

1011
export const authenticatedMiddleware = createMiddleware().server(

Diff for: app/routes/api/stripe/webhook.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { createAPIFileRoute } from "@tanstack/start/api";
22
import { stripe } from "~/lib/stripe";
33
import { updateUserToPremiumUseCase } from "~/use-cases/users";
4+
import { env } from "~/utils/env";
45

5-
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
6+
const webhookSecret = env.STRIPE_WEBHOOK_SECRET!;
67

78
export const APIRoute = createAPIFileRoute("/api/stripe/webhook")({
89
POST: async ({ request }) => {

Diff for: app/routes/learn/$slug/_layout.index.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -315,14 +315,17 @@ function ViewSegment({
315315
</div>
316316
)}
317317

318-
{nextSegment && isLoggedIn && (
318+
{isLoggedIn && (
319319
<div className="flex justify-end">
320320
<Button
321321
onClick={async () => {
322322
await markedAsWatchedFn({
323323
data: { segmentId: currentSegmentId },
324324
});
325-
setCurrentSegmentId(nextSegment.id);
325+
326+
if (nextSegment) {
327+
setCurrentSegmentId(nextSegment.id);
328+
}
326329
}}
327330
>
328331
Next Video <ArrowRight className="ml-2 h-4 w-4" />

Diff for: app/utils/env.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ export const env = {
88
STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY!,
99
STRIPE_PRICE_ID: process.env.STRIPE_PRICE_ID!,
1010
STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET!,
11+
ADMIN_EMAIL: process.env.ADMIN_EMAIL!,
1112
};

Diff for: app/utils/session.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { type UserId } from "~/use-cases/types";
22
import { createSession, generateSessionToken, validateRequest } from "./auth";
33
import { AuthenticationError } from "~/use-cases/errors";
44
import { getCookie, setCookie } from "vinxi/http";
5+
import { env } from "./env";
56

67
const SESSION_COOKIE_NAME = "session";
78

@@ -12,7 +13,7 @@ export async function setSessionTokenCookie(
1213
setCookie(SESSION_COOKIE_NAME, token, {
1314
httpOnly: true,
1415
sameSite: "lax",
15-
secure: process.env.NODE_ENV === "production",
16+
secure: env.NODE_ENV === "production",
1617
expires: expiresAt,
1718
path: "/",
1819
});
@@ -22,7 +23,7 @@ export async function deleteSessionTokenCookie(): Promise<void> {
2223
setCookie(SESSION_COOKIE_NAME, "", {
2324
httpOnly: true,
2425
sameSite: "lax",
25-
secure: process.env.NODE_ENV === "production",
26+
secure: env.NODE_ENV === "production",
2627
maxAge: 0,
2728
path: "/",
2829
});

Diff for: drizzle/0006_right_sunfire.sql

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
DROP INDEX "progress_user_id_segment_id_idx";--> statement-breakpoint
2+
ALTER TABLE "app_progress" ADD CONSTRAINT "app_progress_segmentId_app_segment_id_fk" FOREIGN KEY ("segmentId") REFERENCES "public"."app_segment"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
3+
CREATE UNIQUE INDEX "progress_user_segment_unique_idx" ON "app_progress" USING btree ("userId","segmentId");

0 commit comments

Comments
 (0)