Skip to content

Commit 2317c64

Browse files
committed
feat: Check for custom domain in share page, around dash, etc
1 parent 8fc1071 commit 2317c64

File tree

4 files changed

+193
-51
lines changed

4 files changed

+193
-51
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { db } from "@cap/database";
2+
import { videos, sharedVideos, spaces } from "@cap/database/schema";
3+
import { eq, and } from "drizzle-orm";
4+
import { NextRequest } from "next/server";
5+
6+
export async function GET(request: NextRequest) {
7+
const { searchParams } = request.nextUrl;
8+
const videoId = searchParams.get("videoId");
9+
10+
if (!videoId) {
11+
return Response.json({ error: "Video ID is required" }, { status: 400 });
12+
}
13+
14+
try {
15+
// First, get the video to find the owner or shared space
16+
const video = await db
17+
.select({
18+
id: videos.id,
19+
ownerId: videos.ownerId,
20+
})
21+
.from(videos)
22+
.where(eq(videos.id, videoId))
23+
.limit(1);
24+
25+
if (video.length === 0) {
26+
return Response.json({ error: "Video not found" }, { status: 404 });
27+
}
28+
29+
const videoData = video[0];
30+
if (!videoData || !videoData.ownerId) {
31+
return Response.json({ error: "Invalid video data" }, { status: 500 });
32+
}
33+
34+
// Check if the video is shared with a space
35+
const sharedVideo = await db
36+
.select({
37+
spaceId: sharedVideos.spaceId,
38+
})
39+
.from(sharedVideos)
40+
.where(eq(sharedVideos.videoId, videoId))
41+
.limit(1);
42+
43+
let spaceId = null;
44+
if (sharedVideo.length > 0 && sharedVideo[0] && sharedVideo[0].spaceId) {
45+
spaceId = sharedVideo[0].spaceId;
46+
}
47+
48+
// If we have a space ID, get the space's custom domain
49+
if (spaceId) {
50+
const space = await db
51+
.select({
52+
customDomain: spaces.customDomain,
53+
domainVerified: spaces.domainVerified,
54+
})
55+
.from(spaces)
56+
.where(eq(spaces.id, spaceId))
57+
.limit(1);
58+
59+
if (space.length > 0 && space[0] && space[0].customDomain) {
60+
return Response.json({
61+
customDomain: space[0].customDomain,
62+
domainVerified: space[0].domainVerified || false,
63+
});
64+
}
65+
}
66+
67+
// If no shared space or no custom domain, check the owner's space
68+
const ownerSpaces = await db
69+
.select({
70+
customDomain: spaces.customDomain,
71+
domainVerified: spaces.domainVerified,
72+
})
73+
.from(spaces)
74+
.where(eq(spaces.ownerId, videoData.ownerId))
75+
.limit(1);
76+
77+
if (ownerSpaces.length > 0 && ownerSpaces[0] && ownerSpaces[0].customDomain) {
78+
return Response.json({
79+
customDomain: ownerSpaces[0].customDomain,
80+
domainVerified: ownerSpaces[0].domainVerified || false,
81+
});
82+
}
83+
84+
// No custom domain found
85+
return Response.json({
86+
customDomain: null,
87+
domainVerified: false,
88+
});
89+
} catch (error) {
90+
console.error("Error fetching domain info:", error);
91+
return Response.json({ error: "Internal server error" }, { status: 500 });
92+
}
93+
}

apps/web/app/s/[videoId]/Share.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ interface ShareProps {
3838
comments: number;
3939
reactions: number;
4040
};
41+
customDomain: string | null;
42+
domainVerified: boolean;
4143
}
4244

4345
export const Share: React.FC<ShareProps> = ({
@@ -46,6 +48,8 @@ export const Share: React.FC<ShareProps> = ({
4648
comments,
4749
individualFiles,
4850
initialAnalytics,
51+
customDomain,
52+
domainVerified,
4953
}) => {
5054
const [analytics, setAnalytics] = useState(initialAnalytics);
5155

@@ -94,6 +98,8 @@ export const Share: React.FC<ShareProps> = ({
9498
data={data}
9599
user={user}
96100
individualFiles={individualFiles}
101+
customDomain={customDomain}
102+
domainVerified={domainVerified}
97103
/>
98104

99105
<div className="mt-4">

apps/web/app/s/[videoId]/_components/ShareHeader.tsx

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,17 @@ export const ShareHeader = ({
1414
data,
1515
user,
1616
individualFiles,
17+
customDomain,
18+
domainVerified,
1719
}: {
1820
data: typeof videos.$inferSelect;
1921
user: typeof userSelectProps | null;
2022
individualFiles?: {
2123
fileName: string;
2224
url: string;
2325
}[];
26+
customDomain: string | null;
27+
domainVerified: boolean;
2428
}) => {
2529
const { push, refresh } = useRouter();
2630
const [isEditing, setIsEditing] = useState(false);
@@ -82,6 +86,22 @@ export const ShareHeader = ({
8286
}
8387
};
8488

89+
const getVideoLink = () => {
90+
return customDomain && domainVerified
91+
? `https://${customDomain}/s/${data.id}`
92+
: clientEnv.NEXT_PUBLIC_IS_CAP && NODE_ENV === "production"
93+
? `https://cap.link/${data.id}`
94+
: `${clientEnv.NEXT_PUBLIC_WEB_URL}/s/${data.id}`;
95+
};
96+
97+
const getDisplayLink = () => {
98+
return customDomain && domainVerified
99+
? `${customDomain}/s/${data.id}`
100+
: clientEnv.NEXT_PUBLIC_IS_CAP && NODE_ENV === "production"
101+
? `cap.link/${data.id}`
102+
: `${clientEnv.NEXT_PUBLIC_WEB_URL}/s/${data.id}`;
103+
};
104+
85105
return (
86106
<>
87107
<div>
@@ -144,24 +164,11 @@ export const ShareHeader = ({
144164
variant="gray"
145165
className="hover:bg-gray-300"
146166
onClick={() => {
147-
if (
148-
clientEnv.NEXT_PUBLIC_IS_CAP &&
149-
NODE_ENV === "production"
150-
) {
151-
navigator.clipboard.writeText(
152-
`https://cap.link/${data.id}`
153-
);
154-
} else {
155-
navigator.clipboard.writeText(
156-
`${clientEnv.NEXT_PUBLIC_WEB_URL}/s/${data.id}`
157-
);
158-
}
167+
navigator.clipboard.writeText(getVideoLink());
159168
toast.success("Link copied to clipboard!");
160169
}}
161170
>
162-
{clientEnv.NEXT_PUBLIC_IS_CAP && NODE_ENV === "production"
163-
? `cap.link/${data.id}`
164-
: `${clientEnv.NEXT_PUBLIC_WEB_URL}/s/${data.id}`}
171+
{getDisplayLink()}
165172
<Copy className="ml-2 h-4 w-4" />
166173
</Button>
167174
{user !== null && (

apps/web/app/s/[videoId]/page.tsx

Lines changed: 72 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -252,48 +252,82 @@ export default async function ShareVideoPage(props: Props) {
252252
);
253253
}
254254

255-
let individualFiles: {
256-
fileName: string;
257-
url: string;
258-
}[] = [];
255+
console.log("[ShareVideoPage] Fetching individual files for video:", videoId);
256+
const individualFiles = await fetch(
257+
`${clientEnv.NEXT_PUBLIC_WEB_URL}/api/video/individual-files?videoId=${videoId}`,
258+
{
259+
method: "GET",
260+
credentials: "include",
261+
cache: "no-store",
262+
}
263+
).then((res) => res.json());
264+
265+
console.log("[ShareVideoPage] Fetching analytics for video:", videoId);
266+
const analyticsResponse = await fetch(
267+
`${clientEnv.NEXT_PUBLIC_WEB_URL}/api/video/analytics?videoId=${videoId}`,
268+
{
269+
method: "GET",
270+
credentials: "include",
271+
cache: "no-store",
272+
}
273+
);
259274

260-
if (video?.source.type === "desktopMP4") {
261-
console.log(
262-
"[ShareVideoPage] Fetching individual files for desktop MP4 video:",
263-
videoId
264-
);
265-
const res = await fetch(
266-
`${clientEnv.NEXT_PUBLIC_WEB_URL}/api/video/individual?videoId=${videoId}&userId=${video.ownerId}`,
267-
{
268-
method: "GET",
269-
credentials: "include",
270-
cache: "no-store",
271-
}
272-
);
275+
const analyticsData = await analyticsResponse.json();
276+
const initialAnalytics = {
277+
views: analyticsData.count || 0,
278+
comments: commentsQuery.filter((c) => c.type === "text").length,
279+
reactions: commentsQuery.filter((c) => c.type === "emoji").length,
280+
};
273281

274-
const data = await res.json();
275-
individualFiles = data.files;
276-
}
282+
// Fetch custom domain information
283+
let customDomain: string | null = null;
284+
let domainVerified = false;
277285

278-
console.log("[ShareVideoPage] Rendering Share component for video:", videoId);
286+
// Check if the video is shared with a space
287+
if (video.sharedSpace?.spaceId) {
288+
const spaceData = await db
289+
.select({
290+
customDomain: spaces.customDomain,
291+
domainVerified: spaces.domainVerified,
292+
})
293+
.from(spaces)
294+
.where(eq(spaces.id, video.sharedSpace.spaceId))
295+
.limit(1);
279296

280-
const analyticsData = await db
281-
.select({
282-
id: videos.id,
283-
totalComments: sql<number>`COUNT(DISTINCT CASE WHEN ${comments.type} = 'text' THEN ${comments.id} END)`,
284-
totalReactions: sql<number>`COUNT(DISTINCT CASE WHEN ${comments.type} = 'emoji' THEN ${comments.id} END)`,
285-
})
286-
.from(videos)
287-
.leftJoin(comments, eq(videos.id, comments.videoId))
288-
.where(eq(videos.id, videoId))
289-
.groupBy(videos.id);
297+
if (spaceData.length > 0 && spaceData[0] && spaceData[0].customDomain) {
298+
customDomain = spaceData[0].customDomain;
299+
// Handle domainVerified which could be a Date or boolean
300+
if (spaceData[0].domainVerified !== null) {
301+
domainVerified = true; // If it exists (not null), consider it verified
302+
}
303+
}
304+
}
290305

291-
const initialAnalytics = {
292-
views: 0,
293-
comments: analyticsData[0]?.totalComments || 0,
294-
reactions: analyticsData[0]?.totalReactions || 0,
295-
};
306+
// If no custom domain from shared space, check the owner's space
307+
if (!customDomain && video.ownerId) {
308+
const ownerSpaces = await db
309+
.select({
310+
customDomain: spaces.customDomain,
311+
domainVerified: spaces.domainVerified,
312+
})
313+
.from(spaces)
314+
.where(eq(spaces.ownerId, video.ownerId))
315+
.limit(1);
316+
317+
if (
318+
ownerSpaces.length > 0 &&
319+
ownerSpaces[0] &&
320+
ownerSpaces[0].customDomain
321+
) {
322+
customDomain = ownerSpaces[0].customDomain;
323+
// Handle domainVerified which could be a Date or boolean
324+
if (ownerSpaces[0].domainVerified !== null) {
325+
domainVerified = true; // If it exists (not null), consider it verified
326+
}
327+
}
328+
}
296329

330+
// Get space members if the video is shared with a space
297331
const membersList = video.sharedSpace?.spaceId
298332
? await db
299333
.select({
@@ -316,6 +350,8 @@ export default async function ShareVideoPage(props: Props) {
316350
comments={commentsQuery}
317351
individualFiles={individualFiles}
318352
initialAnalytics={initialAnalytics}
353+
customDomain={customDomain}
354+
domainVerified={domainVerified}
319355
/>
320356
);
321357
}

0 commit comments

Comments
 (0)