Skip to content

Commit 9021819

Browse files
author
Rajat
committed
PDF download button in community posts
1 parent fd9a109 commit 9021819

File tree

2 files changed

+114
-15
lines changed

2 files changed

+114
-15
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
import { getMedia } from "@/services/medialit";
3+
import { Constants } from "@courselit/common-models";
4+
5+
export async function GET(
6+
request: NextRequest,
7+
{ params }: { params: Promise<{ mediaId: string }> },
8+
) {
9+
const mediaId = (await params).mediaId;
10+
11+
if (!mediaId) {
12+
return new NextResponse("Missing mediaId parameter", { status: 400 });
13+
}
14+
15+
try {
16+
const media = await getMedia(mediaId);
17+
18+
if (!media) {
19+
return new NextResponse("Media not found", { status: 404 });
20+
}
21+
22+
if (media.access !== Constants.MediaAccessType.PUBLIC) {
23+
return new NextResponse("Media not found", { status: 404 });
24+
}
25+
26+
if (!media.file) {
27+
return new NextResponse("Media file URL not available", {
28+
status: 404,
29+
});
30+
}
31+
32+
const response = await fetch(media.file);
33+
34+
if (!response.ok) {
35+
throw new Error(`Failed to fetch file: ${response.statusText}`);
36+
}
37+
38+
const headers = new Headers();
39+
headers.set(
40+
"Content-Type",
41+
response.headers.get("Content-Type") ||
42+
media.mimeType ||
43+
"application/octet-stream",
44+
);
45+
46+
const fileName = media.originalFileName || "file";
47+
headers.set(
48+
"Content-Disposition",
49+
`attachment; filename="${fileName}"`,
50+
);
51+
52+
return new NextResponse(response.body, {
53+
status: 200,
54+
headers,
55+
});
56+
} catch (error) {
57+
console.error("Error downloading file:", error);
58+
return new NextResponse("Error downloading file", { status: 500 });
59+
}
60+
}

apps/web/components/community/index.tsx

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
FlagTriangleRight,
2626
Maximize2,
2727
ArrowLeft,
28+
Download,
2829
} from "lucide-react";
2930
import {
3031
Dialog,
@@ -103,7 +104,8 @@ export function CommunityForum({
103104
() => categories.filter((x) => x !== "All"),
104105
[categories],
105106
);
106-
const [fullscreenImage, setFullscreenImage] = useState<string | null>(null);
107+
const [fullscreenMedia, setFullscreenMedia] =
108+
useState<CommunityMedia | null>(null);
107109
const [page, setPage] = useState(1);
108110
const [totalPosts, setTotalPosts] = useState(0);
109111
const [postToDelete, setPostToDelete] = useState<CommunityPost | null>(
@@ -650,7 +652,7 @@ export function CommunityForum({
650652
type="button"
651653
onClick={(e) => {
652654
e.stopPropagation();
653-
setFullscreenImage(media.media!.file!);
655+
setFullscreenMedia(media);
654656
}}
655657
className="absolute top-2 right-2 rounded-md bg-black/60 text-white p-1.5 opacity-0 group-hover:opacity-100 transition-opacity hover:bg-black/80"
656658
aria-label="View full screen"
@@ -733,10 +735,38 @@ export function CommunityForum({
733735
if (options && options.renderActualFile) {
734736
// embed pdf
735737
return (
736-
<iframe
737-
src={media.media?.file}
738-
className="w-full h-48"
739-
></iframe>
738+
<div
739+
className="relative group w-full h-48"
740+
onContextMenu={(e) => e.preventDefault()}
741+
>
742+
<div className="absolute inset-0 z-10" />
743+
<iframe
744+
src={`${media.media?.file}#toolbar=0&view=FitH`}
745+
className="w-full h-full pointer-events-none"
746+
onContextMenu={(e) => e.preventDefault()}
747+
></iframe>
748+
<div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity z-20">
749+
<a
750+
href={`/api/media/${encodeURIComponent(media.media!.mediaId)}`}
751+
onClick={(e) => e.stopPropagation()}
752+
className="rounded-md bg-black/60 text-white p-1.5 hover:bg-black/80"
753+
aria-label="Download"
754+
>
755+
<Download className="h-4 w-4" />
756+
</a>
757+
<button
758+
type="button"
759+
onClick={(e) => {
760+
e.stopPropagation();
761+
setFullscreenMedia(media);
762+
}}
763+
className="rounded-md bg-black/60 text-white p-1.5 hover:bg-black/80"
764+
aria-label="View full screen"
765+
>
766+
<Maximize2 className="h-4 w-4" />
767+
</button>
768+
</div>
769+
</div>
740770
);
741771
}
742772
return (
@@ -1376,7 +1406,7 @@ export function CommunityForum({
13761406
onOpenChange={(open) => {
13771407
if (!open) {
13781408
setOpenPostId(null);
1379-
setFullscreenImage(null);
1409+
setFullscreenMedia(null);
13801410
}
13811411
}}
13821412
>
@@ -1387,25 +1417,34 @@ export function CommunityForum({
13871417
<VisuallyHidden>
13881418
<DialogTitle>Post&apos; content</DialogTitle>
13891419
</VisuallyHidden>
1390-
{openPost && fullscreenImage ? (
1391-
<div className="flex flex-col items-center gap-4">
1420+
{openPost && fullscreenMedia ? (
1421+
<div className="flex flex-col items-center gap-4 w-full">
13921422
<div className="flex w-full justify-start">
13931423
<button
13941424
type="button"
13951425
onClick={() =>
1396-
setFullscreenImage(null)
1426+
setFullscreenMedia(null)
13971427
}
13981428
className="rounded-md bg-muted text-muted-foreground p-1.5 hover:bg-accent transition-colors"
13991429
aria-label="Back to post"
14001430
>
14011431
<ArrowLeft className="h-5 w-5" />
14021432
</button>
14031433
</div>
1404-
<img
1405-
src={fullscreenImage}
1406-
alt="Full size preview"
1407-
className="max-w-full max-h-[65vh] object-contain rounded-md"
1408-
/>
1434+
{fullscreenMedia.type === "pdf" ? (
1435+
<div className="w-full h-[70vh] relative">
1436+
<iframe
1437+
src={`${fullscreenMedia.media?.file}#toolbar=0&view=FitH`}
1438+
className="w-full h-full rounded-md"
1439+
/>
1440+
</div>
1441+
) : (
1442+
<img
1443+
src={fullscreenMedia.media?.file}
1444+
alt="Full size preview"
1445+
className="max-w-full max-h-[65vh] object-contain rounded-md"
1446+
/>
1447+
)}
14091448
</div>
14101449
) : openPost ? (
14111450
<div className="grid gap-4">

0 commit comments

Comments
 (0)