From 34f4d666524e0a7aaa4b1025ff8cd00f874f125d Mon Sep 17 00:00:00 2001 From: Rithish-2914 Date: Mon, 5 Oct 2026 17:09:49 +0530 Subject: [PATCH 1/4] fix: resolve zip download missing extension and posthog cjs constructor error --- src/context/filterContext.tsx | 4 ++-- src/lib/posthog-server.ts | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/context/filterContext.tsx b/src/context/filterContext.tsx index f0cb3fb..0172ce7 100644 --- a/src/context/filterContext.tsx +++ b/src/context/filterContext.tsx @@ -202,7 +202,7 @@ export const FilterProvider: React.FC = ({ const a = document.createElement("a"); a.href = url; - a.download = getDownloadName(searchParams, "subject"); + a.download = `${getDownloadName(searchParams, "subject")}.zip`; document.body.appendChild(a); a.click(); @@ -292,7 +292,7 @@ export const FilterProvider: React.FC = ({ () => Math.ceil( (appliedFilters ? filteredPapers.length : papers.length) / - papersPerPage, + papersPerPage, ), [appliedFilters, filteredPapers.length, papers.length, papersPerPage], ); diff --git a/src/lib/posthog-server.ts b/src/lib/posthog-server.ts index fe4f3e2..481e3ef 100644 --- a/src/lib/posthog-server.ts +++ b/src/lib/posthog-server.ts @@ -1,8 +1,8 @@ -import { PostHog } from "posthog-node"; +import * as posthogModule from "posthog-node"; -let posthogClient: PostHog | null = null; +let posthogClient: any = null; -export function getPostHogClient(): PostHog | null { +export function getPostHogClient(): any { const token = process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN; if (!token) { if (process.env.NODE_ENV !== "production") { @@ -12,7 +12,10 @@ export function getPostHogClient(): PostHog | null { } return null; } - posthogClient ??= new PostHog(token, { + + const PHClass = (posthogModule as any).PostHog || (posthogModule as any).default?.PostHog || posthogModule.default; + + posthogClient ??= new PHClass(token, { host: process.env.NEXT_PUBLIC_POSTHOG_HOST, flushAt: 1, flushInterval: 0, From 48bfb899d528fd1688b765329cc65efc652897f4 Mon Sep 17 00:00:00 2001 From: Rithish-2914 Date: Mon, 5 Oct 2026 20:12:07 +0530 Subject: [PATCH 2/4] feat: Add missing file UX handling for downloads and PDF viewer --- src/components/newPdfViewer.tsx | 47 ++++++++++++++++++++++++++++----- src/lib/posthog-server.ts | 16 ++++++++--- src/lib/utils/download.ts | 4 ++- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/components/newPdfViewer.tsx b/src/components/newPdfViewer.tsx index a3c1973..b767c89 100644 --- a/src/components/newPdfViewer.tsx +++ b/src/components/newPdfViewer.tsx @@ -468,6 +468,19 @@ const [isReadingMode, setIsReadingMode] = useState(false); const [showReadingCoachmark, setShowReadingCoachmark] = useState(false); const viewerRef = useRef(null); + const [urlStatus, setUrlStatus] = useState<"checking" | "ok" | "error">("checking"); + + useEffect(() => { + let isMounted = true; + fetch(url, { method: "HEAD" }) + .then(res => { + if (isMounted) setUrlStatus(res.ok ? "ok" : "error"); + }) + .catch(() => { + if (isMounted) setUrlStatus("error"); + }); + return () => { isMounted = false; }; + }, [url]); const effectiveBackgroundColor = backgroundColor ?? (resolvedTheme === "light" ? "#F3F5FF" : "#070114"); const loaderTextColor = @@ -569,7 +582,19 @@ }), ], [url, name]); - if (isLoading || !engine) { + if (urlStatus === "error") { + return ( +
+ + Paper Not Found + + This paper has been deleted or is missing from the server. + +
+ ); + } + + if (isLoading || !engine || urlStatus === "checking") { return ( } - {({ isLoaded }) => ( + {({ isLoaded, isError }) => ( <>
- + {isError ? ( +
+ + Paper Not Found + + This paper has been deleted or is missing from the server. + +
+ ) : ( + + )}
url.startsWith("http://") ? url.replace("http://", "https://") : url; @@ -23,6 +24,7 @@ export const downloadFile = async ( link.click(); window.URL.revokeObjectURL(link.href); } catch (error) { - console.error("Download failed:", error); + console.error("Download failed (File missing or network error):", error); + toast.error("This paper is no longer available on the server."); } }; \ No newline at end of file From 379e6c7751b109fd9ae3f7312362cf4dfafec6d5 Mon Sep 17 00:00:00 2001 From: Rithish-2914 Date: Tue, 6 Oct 2026 13:42:30 +0530 Subject: [PATCH 3/4] fix: address PR review comments from YOGESH-08 --- src/lib/posthog-server.ts | 2 ++ src/lib/utils/download.ts | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib/posthog-server.ts b/src/lib/posthog-server.ts index ffe513c..d7e0bf0 100644 --- a/src/lib/posthog-server.ts +++ b/src/lib/posthog-server.ts @@ -10,6 +10,8 @@ import type { PostHog } from "posthog-node"; let posthogClient: PostHog | null = null; export function getPostHogClient(): PostHog | null { + if (posthogClient) return posthogClient; + const token = process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN; if (!token) { if (process.env.NODE_ENV !== "production") { diff --git a/src/lib/utils/download.ts b/src/lib/utils/download.ts index a90f13e..5d02d10 100644 --- a/src/lib/utils/download.ts +++ b/src/lib/utils/download.ts @@ -25,6 +25,10 @@ export const downloadFile = async ( window.URL.revokeObjectURL(link.href); } catch (error) { console.error("Download failed (File missing or network error):", error); - toast.error("This paper is no longer available on the server."); + if (typeof navigator !== "undefined" && !navigator.onLine) { + toast.error("You are offline. Please check your internet connection."); + } else { + toast.error("This paper is no longer available on the server."); + } } }; \ No newline at end of file From 7ee69bf9341136d73612f8b8f6bf9fe6223ebaad Mon Sep 17 00:00:00 2001 From: Rithish-2914 Date: Mon, 9 Nov 2026 11:24:04 +0530 Subject: [PATCH 4/4] fix: prevent duplicate filenames from overwriting papers in Download All zip --- src/context/filterContext.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/context/filterContext.tsx b/src/context/filterContext.tsx index 0172ce7..77548ae 100644 --- a/src/context/filterContext.tsx +++ b/src/context/filterContext.tsx @@ -162,6 +162,19 @@ export const FilterProvider: React.FC = ({ ).map((id) => selectedPapers.find((paper) => paper._id === id)) as IPaper[]; let failedCount = 0; + // Different papers can share a generated name (e.g. same subject/exam/slot/year + // from different campuses). JSZip overwrites entries with the same path, so + // append " (1)", " (2)", ... to keep every paper in the zip. + const usedNames = new Map(); + const getUniqueName = (name: string): string => { + const count = usedNames.get(name) ?? 0; + usedNames.set(name, count + 1); + if (count === 0) return name; + const dot = name.lastIndexOf("."); + return dot > 0 + ? `${name.slice(0, dot)} (${count})${name.slice(dot)}` + : `${name} (${count})`; + }; await Promise.all( uniquePapers.map(async (paper) => { @@ -171,7 +184,7 @@ export const FilterProvider: React.FC = ({ throw new Error(`HTTP ${response.status}`); } const blob = await response.blob(); - const filename = generateFileName(paper); + const filename = getUniqueName(generateFileName(paper)); zip.file(filename, blob); } catch (err) { failedCount += 1;