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
60 changes: 60 additions & 0 deletions apps/web/app/api/media/[mediaId]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { NextRequest, NextResponse } from "next/server";
import { getMedia } from "@/services/medialit";
import { Constants } from "@courselit/common-models";

export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ mediaId: string }> },
) {
const mediaId = (await params).mediaId;

if (!mediaId) {
return new NextResponse("Missing mediaId parameter", { status: 400 });
}

try {
const media = await getMedia(mediaId);

if (!media) {
return new NextResponse("Media not found", { status: 404 });
}

if (media.access !== Constants.MediaAccessType.PUBLIC) {
return new NextResponse("Media not found", { status: 404 });
}

if (!media.file) {
return new NextResponse("Media file URL not available", {
status: 404,
});
}

const response = await fetch(media.file);

if (!response.ok) {
throw new Error(`Failed to fetch file: ${response.statusText}`);
}

const headers = new Headers();
headers.set(
"Content-Type",
response.headers.get("Content-Type") ||
media.mimeType ||
"application/octet-stream",
);

const fileName = media.originalFileName || "file";
headers.set(
"Content-Disposition",
`attachment; filename="${fileName}"`,
);

return new NextResponse(response.body, {
status: 200,
headers,
});
} catch (error) {
console.error("Error downloading file:", error);
return new NextResponse("Error downloading file", { status: 500 });
}
}
71 changes: 56 additions & 15 deletions apps/web/components/community/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
FlagTriangleRight,
Maximize2,
ArrowLeft,
Download,
} from "lucide-react";
import {
Dialog,
Expand Down Expand Up @@ -103,7 +104,8 @@ export function CommunityForum({
() => categories.filter((x) => x !== "All"),
[categories],
);
const [fullscreenImage, setFullscreenImage] = useState<string | null>(null);
const [fullscreenMedia, setFullscreenMedia] =
useState<CommunityMedia | null>(null);
const [page, setPage] = useState(1);
const [totalPosts, setTotalPosts] = useState(0);
const [postToDelete, setPostToDelete] = useState<CommunityPost | null>(
Expand Down Expand Up @@ -650,7 +652,7 @@ export function CommunityForum({
type="button"
onClick={(e) => {
e.stopPropagation();
setFullscreenImage(media.media!.file!);
setFullscreenMedia(media);
}}
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"
aria-label="View full screen"
Expand Down Expand Up @@ -733,10 +735,40 @@ export function CommunityForum({
if (options && options.renderActualFile) {
// embed pdf
return (
<iframe
src={media.media?.file}
className="w-full h-48"
></iframe>
<div
className="relative group w-full h-48"
onContextMenu={(e) => e.preventDefault()}
>
<div className="absolute inset-0 z-10" />
<iframe
src={`${media.media?.file}#toolbar=0&view=FitH`}
className="w-full h-full pointer-events-none"
onContextMenu={(e) => e.preventDefault()}
></iframe>
<div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity z-20">
{media.media?.mediaId && (
<a
href={`/api/media/${encodeURIComponent(media.media.mediaId)}`}
onClick={(e) => e.stopPropagation()}
className="rounded-md bg-black/60 text-white p-1.5 hover:bg-black/80"
aria-label="Download"
>
<Download className="h-4 w-4" />
</a>
)}
<button
type="button"
onClick={(e) => {
e.stopPropagation();
setFullscreenMedia(media);
}}
className="rounded-md bg-black/60 text-white p-1.5 hover:bg-black/80"
aria-label="View full screen"
>
<Maximize2 className="h-4 w-4" />
</button>
</div>
</div>
);
}
return (
Expand Down Expand Up @@ -1376,7 +1408,7 @@ export function CommunityForum({
onOpenChange={(open) => {
if (!open) {
setOpenPostId(null);
setFullscreenImage(null);
setFullscreenMedia(null);
}
}}
>
Expand All @@ -1387,25 +1419,34 @@ export function CommunityForum({
<VisuallyHidden>
<DialogTitle>Post&apos; content</DialogTitle>
</VisuallyHidden>
{openPost && fullscreenImage ? (
<div className="flex flex-col items-center gap-4">
{openPost && fullscreenMedia ? (
<div className="flex flex-col items-center gap-4 w-full">
<div className="flex w-full justify-start">
<button
type="button"
onClick={() =>
setFullscreenImage(null)
setFullscreenMedia(null)
}
className="rounded-md bg-muted text-muted-foreground p-1.5 hover:bg-accent transition-colors"
aria-label="Back to post"
>
<ArrowLeft className="h-5 w-5" />
</button>
</div>
<img
src={fullscreenImage}
alt="Full size preview"
className="max-w-full max-h-[65vh] object-contain rounded-md"
/>
{fullscreenMedia.type === "pdf" ? (
<div className="w-full h-[70vh] relative">
<iframe
src={`${fullscreenMedia.media?.file}#toolbar=0&view=FitH`}
className="w-full h-full rounded-md"
/>
</div>
) : (
<img
src={fullscreenMedia.media?.file}
alt="Full size preview"
className="max-w-full max-h-[65vh] object-contain rounded-md"
/>
)}
</div>
) : openPost ? (
<div className="grid gap-4">
Expand Down
Loading