From 8dd72de35917775668594df061f6915dc15cc950 Mon Sep 17 00:00:00 2001 From: gosangam Date: Fri, 16 Oct 2026 03:25:08 +0530 Subject: [PATCH] quiz link --- app/(app)/quizzes/[id]/quiz-player.tsx | 5 ++ components/layout/copy-page-link-button.tsx | 79 +++++++++++++++++++++ components/layout/page-header.tsx | 59 +-------------- 3 files changed, 86 insertions(+), 57 deletions(-) create mode 100644 components/layout/copy-page-link-button.tsx diff --git a/app/(app)/quizzes/[id]/quiz-player.tsx b/app/(app)/quizzes/[id]/quiz-player.tsx index 7bf71f9c..1f8b2983 100644 --- a/app/(app)/quizzes/[id]/quiz-player.tsx +++ b/app/(app)/quizzes/[id]/quiz-player.tsx @@ -39,6 +39,7 @@ import { type QuizResult, } from "@/lib/quiz"; import { QuizResultView } from "@/components/quiz/quiz-result-view"; +import { CopyPageLinkButton } from "@/components/layout/copy-page-link-button"; import { cn } from "@/lib/utils"; type Stage = @@ -170,6 +171,10 @@ export function QuizPlayer({ id }: { id: string }) { {stage.card.target_title || stage.card.target_path} )} + {/* Shares the quiz itself; the link unfurls with its own OG card. */} +
+ +
)} diff --git a/components/layout/copy-page-link-button.tsx b/components/layout/copy-page-link-button.tsx new file mode 100644 index 00000000..bed29bf9 --- /dev/null +++ b/components/layout/copy-page-link-button.tsx @@ -0,0 +1,79 @@ +"use client"; + +/** + * "Copy link to this page" — copies the full, unshortened absolute URL of + * the active page. Deliberately bypasses useCopyFeedback (which rewrites + * `?hl=` highlight URLs into short /share links); here we want the literal + * page URL. The write is synchronous within the click so a single tap works. + * + * Shared by the PageHeader controls row and pages with their own header + * (e.g. a quiz). + */ + +import { useEffect, useRef, useState } from "react"; +import { Check, Link2 } from "lucide-react"; +import { cn } from "@/lib/utils"; + +export function CopyPageLinkButton({ + label = "Copy link to this page", + className, +}: { + label?: string; + className?: string; +}) { + const [copied, setCopied] = useState(false); + const copyTimerRef = useRef | null>(null); + useEffect(() => { + return () => { + if (copyTimerRef.current) clearTimeout(copyTimerRef.current); + }; + }, []); + + async function copyPageLink() { + const url = window.location.href; + try { + await navigator.clipboard.writeText(url); + } catch { + // Fallback for browsers without the async clipboard API. + const ta = document.createElement("textarea"); + ta.value = url; + ta.style.position = "fixed"; + ta.style.opacity = "0"; + document.body.appendChild(ta); + ta.focus(); + ta.select(); + try { + document.execCommand("copy"); + } catch { + // silent fail + } + document.body.removeChild(ta); + } + setCopied(true); + if (copyTimerRef.current) clearTimeout(copyTimerRef.current); + copyTimerRef.current = setTimeout(() => setCopied(false), 1500); + } + + return ( + + ); +} diff --git a/components/layout/page-header.tsx b/components/layout/page-header.tsx index feb75d12..d79168bd 100644 --- a/components/layout/page-header.tsx +++ b/components/layout/page-header.tsx @@ -25,8 +25,8 @@ import { useEffect, useRef, useState } from "react"; import { useRouter, usePathname } from "next/navigation"; -import { Check, Link2 } from "lucide-react"; import { cn } from "@/lib/utils"; +import { CopyPageLinkButton } from "@/components/layout/copy-page-link-button"; import { SaveOfflineButton } from "@/components/layout/save-offline-button"; import { QuizButton } from "@/components/quiz/quiz-button"; import type { OfflineDescriptor } from "@/lib/offline"; @@ -153,43 +153,6 @@ export function PageHeader({ return () => window.removeEventListener("scroll", onScroll); }, [sticky]); - // "Copy link to this page" — copies the full, unshortened absolute URL of - // the active page. Deliberately bypasses useCopyFeedback (which rewrites - // `?hl=` highlight URLs into short /share links); here we want the literal - // page URL. The write is synchronous within the click so a single tap works. - const [copied, setCopied] = useState(false); - const copyTimerRef = useRef | null>(null); - useEffect(() => { - return () => { - if (copyTimerRef.current) clearTimeout(copyTimerRef.current); - }; - }, []); - - async function copyPageLink() { - const url = window.location.href; - try { - await navigator.clipboard.writeText(url); - } catch { - // Fallback for browsers without the async clipboard API. - const ta = document.createElement("textarea"); - ta.value = url; - ta.style.position = "fixed"; - ta.style.opacity = "0"; - document.body.appendChild(ta); - ta.focus(); - ta.select(); - try { - document.execCommand("copy"); - } catch { - // silent fail - } - document.body.removeChild(ta); - } - setCopied(true); - if (copyTimerRef.current) clearTimeout(copyTimerRef.current); - copyTimerRef.current = setTimeout(() => setCopied(false), 1500); - } - return (
: null} - + {resolvedOffline ? ( ) : null}