diff --git a/app/content/page.tsx b/app/content/page.tsx new file mode 100644 index 0000000..a986c02 --- /dev/null +++ b/app/content/page.tsx @@ -0,0 +1,19 @@ +import { ContentContent } from "@/components/content-content"; +import { YOUTUBE_VIDEO_LINKS } from "@/data/youtube-videos"; +import { getPdfNotes, getPodcastCollections } from "@/lib/content-page"; + +export default async function ContentPage() { + const [{ yazilim, masaBasi }, pdfFiles] = await Promise.all([ + getPodcastCollections(), + getPdfNotes(), + ]); + + return ( + + ); +} diff --git a/components/content-content.tsx b/components/content-content.tsx new file mode 100644 index 0000000..d784dea --- /dev/null +++ b/components/content-content.tsx @@ -0,0 +1,371 @@ +"use client"; + +import { useEffect, useMemo, useRef, useState } from "react"; +import Link from "next/link"; +import ReactMarkdown from "react-markdown"; +import remarkGfm from "remark-gfm"; +import { Icon } from "@iconify/react"; +import { Badge, Button, Card, Typography } from "poyraz-ui/atoms"; +import { Modal, ModalContent, ModalTitle, Sheet, SheetContent, SheetTitle } from "poyraz-ui/molecules"; +import type { PodcastEpisode } from "@/data/content-types"; + +type ContentContentProps = { + yazilimEpisodes: PodcastEpisode[]; + masaBasiEpisodes: PodcastEpisode[]; + youtubeLinks: readonly string[]; + pdfFiles: string[]; +}; + +function PdfFirstPagePreview({ src, title }: { src: string; title: string }) { + const canvasRef = useRef(null); + const [failed, setFailed] = useState(false); + + useEffect(() => { + let cancelled = false; + let loadingTask: { promise: Promise; destroy?: () => void } | null = null; + + const render = async () => { + try { + const pdfjs = await import("pdfjs-dist"); + const lib = pdfjs as unknown as { + version: string; + getDocument: (src: string) => { promise: Promise; destroy?: () => void }; + GlobalWorkerOptions: { workerSrc: string }; + }; + + lib.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${lib.version}/build/pdf.worker.min.mjs`; + loadingTask = lib.getDocument(src); + + const pdf = (await loadingTask.promise) as { + getPage: (page: number) => Promise<{ + getViewport: (opts: { scale: number }) => { width: number; height: number }; + render: (opts: { + canvasContext: CanvasRenderingContext2D; + viewport: { width: number; height: number }; + }) => { promise: Promise }; + }>; + }; + + const page = await pdf.getPage(1); + const viewport = page.getViewport({ scale: 1.2 }); + const canvas = canvasRef.current; + if (!canvas || cancelled) return; + + const context = canvas.getContext("2d"); + if (!context) return; + + const ratio = window.devicePixelRatio || 1; + canvas.width = Math.floor(viewport.width * ratio); + canvas.height = Math.floor(viewport.height * ratio); + canvas.style.width = `${viewport.width}px`; + canvas.style.height = `${viewport.height}px`; + + context.setTransform(ratio, 0, 0, ratio, 0, 0); + await page.render({ canvasContext: context, viewport }).promise; + } catch { + if (!cancelled) { + setFailed(true); + } + } + }; + + void render(); + + return () => { + cancelled = true; + if (loadingTask?.destroy) { + loadingTask.destroy(); + } + }; + }, [src]); + + if (failed) { + return ( +
+ + Preview unavailable + +
+ ); + } + + return ( +
+ +
+ ); +} + +function getYoutubeEmbedUrl(link: string) { + try { + const url = new URL(link); + const videoId = url.searchParams.get("v"); + return videoId ? `https://www.youtube.com/embed/${videoId}` : null; + } catch { + return null; + } +} + +function PodcastColumn({ + title, + subtitle, + episodes, + onOpenEpisode, +}: { + title: string; + subtitle: string; + episodes: PodcastEpisode[]; + onOpenEpisode: (episode: PodcastEpisode) => void; +}) { + return ( + + + {title} + + + {subtitle} + + +
+ {episodes.slice(0, 3).map((episode) => ( + + ))} +
+
+ ); +} + +export function ContentContent({ + yazilimEpisodes, + masaBasiEpisodes, + youtubeLinks, + pdfFiles, +}: ContentContentProps) { + const [selectedEpisode, setSelectedEpisode] = useState(null); + const [sheetOpen, setSheetOpen] = useState(false); + const [pdfModalOpen, setPdfModalOpen] = useState(false); + const [activePdfIndex, setActivePdfIndex] = useState(0); + + const activePdf = pdfFiles[activePdfIndex] ?? null; + const canGoPrev = activePdfIndex > 0; + const canGoNext = activePdfIndex < pdfFiles.length - 1; + + const embeddedVideos = useMemo( + () => + youtubeLinks + .map((link) => ({ link, embedUrl: getYoutubeEmbedUrl(link) })) + .slice(0, 3), + [youtubeLinks], + ); + + const openEpisode = (episode: PodcastEpisode) => { + setSelectedEpisode(episode); + setSheetOpen(true); + }; + + const openPdfModal = (index: number) => { + setActivePdfIndex(index); + setPdfModalOpen(true); + }; + + return ( +
+
+ + +
+ +
+ + Latest YouTube Videos + +
+ {embeddedVideos.map((item) => ( + + {item.embedUrl ? ( +
+