diff --git a/app/(app)/books/bbd/[chapter]/page.tsx b/app/(app)/(reader)/books/bbd/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/bbd/[chapter]/page.tsx rename to app/(app)/(reader)/books/bbd/[chapter]/page.tsx diff --git a/app/(app)/books/bbd/page.tsx b/app/(app)/(reader)/books/bbd/page.tsx similarity index 100% rename from app/(app)/books/bbd/page.tsx rename to app/(app)/(reader)/books/bbd/page.tsx diff --git a/app/(app)/books/bg/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/bg/[chapter]/[verse]/page.tsx similarity index 98% rename from app/(app)/books/bg/[chapter]/[verse]/page.tsx rename to app/(app)/(reader)/books/bg/[chapter]/[verse]/page.tsx index b702cd83..83ae49c7 100644 --- a/app/(app)/books/bg/[chapter]/[verse]/page.tsx +++ b/app/(app)/(reader)/books/bg/[chapter]/[verse]/page.tsx @@ -4,7 +4,7 @@ import { PageHeader } from "@/components/layout/page-header"; import { FindInPage } from "@/components/layout/find-in-page"; import { notFound } from "next/navigation"; import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api"; -import { ChapterViewer } from "@/app/(app)/books/chapter-viewer"; +import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/layout/mobile-nav"; import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; diff --git a/app/(app)/books/bg/[chapter]/page.tsx b/app/(app)/(reader)/books/bg/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/bg/[chapter]/page.tsx rename to app/(app)/(reader)/books/bg/[chapter]/page.tsx diff --git a/app/(app)/books/bg/page.tsx b/app/(app)/(reader)/books/bg/page.tsx similarity index 100% rename from app/(app)/books/bg/page.tsx rename to app/(app)/(reader)/books/bg/page.tsx diff --git a/app/(app)/books/bk/[chapter]/page.tsx b/app/(app)/(reader)/books/bk/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/bk/[chapter]/page.tsx rename to app/(app)/(reader)/books/bk/[chapter]/page.tsx diff --git a/app/(app)/books/bk/page.tsx b/app/(app)/(reader)/books/bk/page.tsx similarity index 100% rename from app/(app)/books/bk/page.tsx rename to app/(app)/(reader)/books/bk/page.tsx diff --git a/app/(app)/books/books-grid.tsx b/app/(app)/(reader)/books/books-grid.tsx similarity index 100% rename from app/(app)/books/books-grid.tsx rename to app/(app)/(reader)/books/books-grid.tsx diff --git a/app/(app)/books/bs/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/bs/[chapter]/[verse]/page.tsx similarity index 98% rename from app/(app)/books/bs/[chapter]/[verse]/page.tsx rename to app/(app)/(reader)/books/bs/[chapter]/[verse]/page.tsx index 0ed53e90..68ec3f26 100644 --- a/app/(app)/books/bs/[chapter]/[verse]/page.tsx +++ b/app/(app)/(reader)/books/bs/[chapter]/[verse]/page.tsx @@ -12,7 +12,7 @@ import { type VerseIndexEntry, type VerseRecord, } from "@/lib/api"; -import { ChapterViewer } from "@/app/(app)/books/chapter-viewer"; +import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/layout/mobile-nav"; import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; diff --git a/app/(app)/books/bs/[chapter]/page.tsx b/app/(app)/(reader)/books/bs/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/bs/[chapter]/page.tsx rename to app/(app)/(reader)/books/bs/[chapter]/page.tsx diff --git a/app/(app)/books/bs/page.tsx b/app/(app)/(reader)/books/bs/page.tsx similarity index 100% rename from app/(app)/books/bs/page.tsx rename to app/(app)/(reader)/books/bs/page.tsx diff --git a/app/(app)/books/cbhc/[chapter]/page.tsx b/app/(app)/(reader)/books/cbhc/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/cbhc/[chapter]/page.tsx rename to app/(app)/(reader)/books/cbhc/[chapter]/page.tsx diff --git a/app/(app)/books/cbhc/page.tsx b/app/(app)/(reader)/books/cbhc/page.tsx similarity index 100% rename from app/(app)/books/cbhc/page.tsx rename to app/(app)/(reader)/books/cbhc/page.tsx diff --git a/app/(app)/books/cc/[lila]/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/cc/[lila]/[chapter]/[verse]/page.tsx similarity index 98% rename from app/(app)/books/cc/[lila]/[chapter]/[verse]/page.tsx rename to app/(app)/(reader)/books/cc/[lila]/[chapter]/[verse]/page.tsx index b6999bf0..417ab0b0 100644 --- a/app/(app)/books/cc/[lila]/[chapter]/[verse]/page.tsx +++ b/app/(app)/(reader)/books/cc/[lila]/[chapter]/[verse]/page.tsx @@ -6,7 +6,7 @@ import { PageHeader } from "@/components/layout/page-header"; import { FindInPage } from "@/components/layout/find-in-page"; import { notFound } from "next/navigation"; import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api"; -import { ChapterViewer } from "@/app/(app)/books/chapter-viewer"; +import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/layout/mobile-nav"; import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; diff --git a/app/(app)/books/cc/[lila]/[chapter]/page.tsx b/app/(app)/(reader)/books/cc/[lila]/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/cc/[lila]/[chapter]/page.tsx rename to app/(app)/(reader)/books/cc/[lila]/[chapter]/page.tsx diff --git a/app/(app)/books/cc/[lila]/page.tsx b/app/(app)/(reader)/books/cc/[lila]/page.tsx similarity index 100% rename from app/(app)/books/cc/[lila]/page.tsx rename to app/(app)/(reader)/books/cc/[lila]/page.tsx diff --git a/app/(app)/books/cc/page.tsx b/app/(app)/(reader)/books/cc/page.tsx similarity index 100% rename from app/(app)/books/cc/page.tsx rename to app/(app)/(reader)/books/cc/page.tsx diff --git a/app/(app)/books/chapter-viewer.tsx b/app/(app)/(reader)/books/chapter-viewer.tsx similarity index 100% rename from app/(app)/books/chapter-viewer.tsx rename to app/(app)/(reader)/books/chapter-viewer.tsx diff --git a/app/(app)/books/ds/[chapter]/page.tsx b/app/(app)/(reader)/books/ds/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/ds/[chapter]/page.tsx rename to app/(app)/(reader)/books/ds/[chapter]/page.tsx diff --git a/app/(app)/books/ds/page.tsx b/app/(app)/(reader)/books/ds/page.tsx similarity index 100% rename from app/(app)/books/ds/page.tsx rename to app/(app)/(reader)/books/ds/page.tsx diff --git a/app/(app)/books/ej/[chapter]/page.tsx b/app/(app)/(reader)/books/ej/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/ej/[chapter]/page.tsx rename to app/(app)/(reader)/books/ej/[chapter]/page.tsx diff --git a/app/(app)/books/ej/page.tsx b/app/(app)/(reader)/books/ej/page.tsx similarity index 100% rename from app/(app)/books/ej/page.tsx rename to app/(app)/(reader)/books/ej/page.tsx diff --git a/app/(app)/books/ek/[chapter]/page.tsx b/app/(app)/(reader)/books/ek/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/ek/[chapter]/page.tsx rename to app/(app)/(reader)/books/ek/[chapter]/page.tsx diff --git a/app/(app)/books/ek/page.tsx b/app/(app)/(reader)/books/ek/page.tsx similarity index 100% rename from app/(app)/books/ek/page.tsx rename to app/(app)/(reader)/books/ek/page.tsx diff --git a/app/(app)/books/extracted-verse-count.tsx b/app/(app)/(reader)/books/extracted-verse-count.tsx similarity index 100% rename from app/(app)/books/extracted-verse-count.tsx rename to app/(app)/(reader)/books/extracted-verse-count.tsx diff --git a/app/(app)/books/gg/[chapter]/page.tsx b/app/(app)/(reader)/books/gg/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/gg/[chapter]/page.tsx rename to app/(app)/(reader)/books/gg/[chapter]/page.tsx diff --git a/app/(app)/books/gg/page.tsx b/app/(app)/(reader)/books/gg/page.tsx similarity index 100% rename from app/(app)/books/gg/page.tsx rename to app/(app)/(reader)/books/gg/page.tsx diff --git a/app/(app)/books/hkh/[chapter]/page.tsx b/app/(app)/(reader)/books/hkh/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/hkh/[chapter]/page.tsx rename to app/(app)/(reader)/books/hkh/[chapter]/page.tsx diff --git a/app/(app)/books/hkh/page.tsx b/app/(app)/(reader)/books/hkh/page.tsx similarity index 100% rename from app/(app)/books/hkh/page.tsx rename to app/(app)/(reader)/books/hkh/page.tsx diff --git a/app/(app)/books/iso/[mantra]/page.tsx b/app/(app)/(reader)/books/iso/[mantra]/page.tsx similarity index 100% rename from app/(app)/books/iso/[mantra]/page.tsx rename to app/(app)/(reader)/books/iso/[mantra]/page.tsx diff --git a/app/(app)/books/iso/page.tsx b/app/(app)/(reader)/books/iso/page.tsx similarity index 100% rename from app/(app)/books/iso/page.tsx rename to app/(app)/(reader)/books/iso/page.tsx diff --git a/app/(app)/books/kb/[volume]/[slug]/page.tsx b/app/(app)/(reader)/books/kb/[volume]/[slug]/page.tsx similarity index 100% rename from app/(app)/books/kb/[volume]/[slug]/page.tsx rename to app/(app)/(reader)/books/kb/[volume]/[slug]/page.tsx diff --git a/app/(app)/books/kb/[volume]/page.tsx b/app/(app)/(reader)/books/kb/[volume]/page.tsx similarity index 100% rename from app/(app)/books/kb/[volume]/page.tsx rename to app/(app)/(reader)/books/kb/[volume]/page.tsx diff --git a/app/(app)/books/kb/page.tsx b/app/(app)/(reader)/books/kb/page.tsx similarity index 100% rename from app/(app)/books/kb/page.tsx rename to app/(app)/(reader)/books/kb/page.tsx diff --git a/app/(app)/books/lcl/[chapter]/page.tsx b/app/(app)/(reader)/books/lcl/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/lcl/[chapter]/page.tsx rename to app/(app)/(reader)/books/lcl/[chapter]/page.tsx diff --git a/app/(app)/books/lcl/page.tsx b/app/(app)/(reader)/books/lcl/page.tsx similarity index 100% rename from app/(app)/books/lcl/page.tsx rename to app/(app)/(reader)/books/lcl/page.tsx diff --git a/app/(app)/books/lob/[chapter]/page.tsx b/app/(app)/(reader)/books/lob/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/lob/[chapter]/page.tsx rename to app/(app)/(reader)/books/lob/[chapter]/page.tsx diff --git a/app/(app)/books/lob/page.tsx b/app/(app)/(reader)/books/lob/page.tsx similarity index 100% rename from app/(app)/books/lob/page.tsx rename to app/(app)/(reader)/books/lob/page.tsx diff --git a/app/(app)/books/mg/[chapter]/page.tsx b/app/(app)/(reader)/books/mg/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/mg/[chapter]/page.tsx rename to app/(app)/(reader)/books/mg/[chapter]/page.tsx diff --git a/app/(app)/books/mg/page.tsx b/app/(app)/(reader)/books/mg/page.tsx similarity index 100% rename from app/(app)/books/mg/page.tsx rename to app/(app)/(reader)/books/mg/page.tsx diff --git a/app/(app)/books/mm/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/mm/[chapter]/[verse]/page.tsx similarity index 98% rename from app/(app)/books/mm/[chapter]/[verse]/page.tsx rename to app/(app)/(reader)/books/mm/[chapter]/[verse]/page.tsx index 32b3eccc..ff27ea2a 100644 --- a/app/(app)/books/mm/[chapter]/[verse]/page.tsx +++ b/app/(app)/(reader)/books/mm/[chapter]/[verse]/page.tsx @@ -11,7 +11,7 @@ import { type VerseIndexEntry, type VerseRecord, } from "@/lib/api"; -import { ChapterViewer } from "@/app/(app)/books/chapter-viewer"; +import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/layout/mobile-nav"; import type { Metadata } from "next"; import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; diff --git a/app/(app)/books/mm/[chapter]/page.tsx b/app/(app)/(reader)/books/mm/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/mm/[chapter]/page.tsx rename to app/(app)/(reader)/books/mm/[chapter]/page.tsx diff --git a/app/(app)/books/mm/page.tsx b/app/(app)/(reader)/books/mm/page.tsx similarity index 100% rename from app/(app)/books/mm/page.tsx rename to app/(app)/(reader)/books/mm/page.tsx diff --git a/app/(app)/books/mog/[chapter]/page.tsx b/app/(app)/(reader)/books/mog/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/mog/[chapter]/page.tsx rename to app/(app)/(reader)/books/mog/[chapter]/page.tsx diff --git a/app/(app)/books/mog/page.tsx b/app/(app)/(reader)/books/mog/page.tsx similarity index 100% rename from app/(app)/books/mog/page.tsx rename to app/(app)/(reader)/books/mog/page.tsx diff --git a/app/(app)/books/nbs/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/nbs/[chapter]/[verse]/page.tsx similarity index 98% rename from app/(app)/books/nbs/[chapter]/[verse]/page.tsx rename to app/(app)/(reader)/books/nbs/[chapter]/[verse]/page.tsx index 3d136e38..4d0f7ade 100644 --- a/app/(app)/books/nbs/[chapter]/[verse]/page.tsx +++ b/app/(app)/(reader)/books/nbs/[chapter]/[verse]/page.tsx @@ -15,7 +15,7 @@ import { type VerseRecord, } from "@/lib/api"; import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; -import { ChapterViewer } from "@/app/(app)/books/chapter-viewer"; +import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/layout/mobile-nav"; type NbsMeta = { diff --git a/app/(app)/books/nbs/[chapter]/page.tsx b/app/(app)/(reader)/books/nbs/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/nbs/[chapter]/page.tsx rename to app/(app)/(reader)/books/nbs/[chapter]/page.tsx diff --git a/app/(app)/books/nbs/page.tsx b/app/(app)/(reader)/books/nbs/page.tsx similarity index 100% rename from app/(app)/books/nbs/page.tsx rename to app/(app)/(reader)/books/nbs/page.tsx diff --git a/app/(app)/books/nod/[chapter]/page.tsx b/app/(app)/(reader)/books/nod/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/nod/[chapter]/page.tsx rename to app/(app)/(reader)/books/nod/[chapter]/page.tsx diff --git a/app/(app)/books/nod/page.tsx b/app/(app)/(reader)/books/nod/page.tsx similarity index 100% rename from app/(app)/books/nod/page.tsx rename to app/(app)/(reader)/books/nod/page.tsx diff --git a/app/(app)/books/noi/[text]/page.tsx b/app/(app)/(reader)/books/noi/[text]/page.tsx similarity index 100% rename from app/(app)/books/noi/[text]/page.tsx rename to app/(app)/(reader)/books/noi/[text]/page.tsx diff --git a/app/(app)/books/noi/page.tsx b/app/(app)/(reader)/books/noi/page.tsx similarity index 100% rename from app/(app)/books/noi/page.tsx rename to app/(app)/(reader)/books/noi/page.tsx diff --git a/app/(app)/books/ochkm/[chapter]/page.tsx b/app/(app)/(reader)/books/ochkm/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/ochkm/[chapter]/page.tsx rename to app/(app)/(reader)/books/ochkm/[chapter]/page.tsx diff --git a/app/(app)/books/ochkm/page.tsx b/app/(app)/(reader)/books/ochkm/page.tsx similarity index 100% rename from app/(app)/books/ochkm/page.tsx rename to app/(app)/(reader)/books/ochkm/page.tsx diff --git a/app/(app)/books/owk/[chapter]/page.tsx b/app/(app)/(reader)/books/owk/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/owk/[chapter]/page.tsx rename to app/(app)/(reader)/books/owk/[chapter]/page.tsx diff --git a/app/(app)/books/owk/page.tsx b/app/(app)/(reader)/books/owk/page.tsx similarity index 100% rename from app/(app)/books/owk/page.tsx rename to app/(app)/(reader)/books/owk/page.tsx diff --git a/app/(app)/books/page.tsx b/app/(app)/(reader)/books/page.tsx similarity index 100% rename from app/(app)/books/page.tsx rename to app/(app)/(reader)/books/page.tsx diff --git a/app/(app)/books/pk/[chapter]/page.tsx b/app/(app)/(reader)/books/pk/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/pk/[chapter]/page.tsx rename to app/(app)/(reader)/books/pk/[chapter]/page.tsx diff --git a/app/(app)/books/pk/page.tsx b/app/(app)/(reader)/books/pk/page.tsx similarity index 100% rename from app/(app)/books/pk/page.tsx rename to app/(app)/(reader)/books/pk/page.tsx diff --git a/app/(app)/books/poy/[chapter]/page.tsx b/app/(app)/(reader)/books/poy/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/poy/[chapter]/page.tsx rename to app/(app)/(reader)/books/poy/[chapter]/page.tsx diff --git a/app/(app)/books/poy/page.tsx b/app/(app)/(reader)/books/poy/page.tsx similarity index 100% rename from app/(app)/books/poy/page.tsx rename to app/(app)/(reader)/books/poy/page.tsx diff --git a/app/(app)/books/pqpa/[chapter]/page.tsx b/app/(app)/(reader)/books/pqpa/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/pqpa/[chapter]/page.tsx rename to app/(app)/(reader)/books/pqpa/[chapter]/page.tsx diff --git a/app/(app)/books/pqpa/page.tsx b/app/(app)/(reader)/books/pqpa/page.tsx similarity index 100% rename from app/(app)/books/pqpa/page.tsx rename to app/(app)/(reader)/books/pqpa/page.tsx diff --git a/app/(app)/books/rv/[chapter]/page.tsx b/app/(app)/(reader)/books/rv/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/rv/[chapter]/page.tsx rename to app/(app)/(reader)/books/rv/[chapter]/page.tsx diff --git a/app/(app)/books/rv/page.tsx b/app/(app)/(reader)/books/rv/page.tsx similarity index 100% rename from app/(app)/books/rv/page.tsx rename to app/(app)/(reader)/books/rv/page.tsx diff --git a/app/(app)/books/sb/[canto]/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/sb/[canto]/[chapter]/[verse]/page.tsx similarity index 98% rename from app/(app)/books/sb/[canto]/[chapter]/[verse]/page.tsx rename to app/(app)/(reader)/books/sb/[canto]/[chapter]/[verse]/page.tsx index d7c3411a..35cb5136 100644 --- a/app/(app)/books/sb/[canto]/[chapter]/[verse]/page.tsx +++ b/app/(app)/(reader)/books/sb/[canto]/[chapter]/[verse]/page.tsx @@ -6,7 +6,7 @@ import { PageHeader } from "@/components/layout/page-header"; import { FindInPage } from "@/components/layout/find-in-page"; import { notFound } from "next/navigation"; import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api"; -import { ChapterViewer } from "@/app/(app)/books/chapter-viewer"; +import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/layout/mobile-nav"; import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; diff --git a/app/(app)/books/sb/[canto]/[chapter]/page.tsx b/app/(app)/(reader)/books/sb/[canto]/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/sb/[canto]/[chapter]/page.tsx rename to app/(app)/(reader)/books/sb/[canto]/[chapter]/page.tsx diff --git a/app/(app)/books/sb/[canto]/page.tsx b/app/(app)/(reader)/books/sb/[canto]/page.tsx similarity index 100% rename from app/(app)/books/sb/[canto]/page.tsx rename to app/(app)/(reader)/books/sb/[canto]/page.tsx diff --git a/app/(app)/books/sb/page.tsx b/app/(app)/(reader)/books/sb/page.tsx similarity index 100% rename from app/(app)/books/sb/page.tsx rename to app/(app)/(reader)/books/sb/page.tsx diff --git a/app/(app)/books/sb1962/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/sb1962/[chapter]/[verse]/page.tsx similarity index 98% rename from app/(app)/books/sb1962/[chapter]/[verse]/page.tsx rename to app/(app)/(reader)/books/sb1962/[chapter]/[verse]/page.tsx index aaea5cda..6a974c92 100644 --- a/app/(app)/books/sb1962/[chapter]/[verse]/page.tsx +++ b/app/(app)/(reader)/books/sb1962/[chapter]/[verse]/page.tsx @@ -4,7 +4,7 @@ import { PageHeader } from "@/components/layout/page-header"; import { FindInPage } from "@/components/layout/find-in-page"; import { notFound } from "next/navigation"; import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api"; -import { ChapterViewer } from "@/app/(app)/books/chapter-viewer"; +import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer"; import type { MobileNavGroup } from "@/components/layout/mobile-nav"; import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; diff --git a/app/(app)/books/sb1962/[chapter]/page.tsx b/app/(app)/(reader)/books/sb1962/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/sb1962/[chapter]/page.tsx rename to app/(app)/(reader)/books/sb1962/[chapter]/page.tsx diff --git a/app/(app)/books/sb1962/page.tsx b/app/(app)/(reader)/books/sb1962/page.tsx similarity index 100% rename from app/(app)/books/sb1962/page.tsx rename to app/(app)/(reader)/books/sb1962/page.tsx diff --git a/app/(app)/books/ssr/[chapter]/page.tsx b/app/(app)/(reader)/books/ssr/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/ssr/[chapter]/page.tsx rename to app/(app)/(reader)/books/ssr/[chapter]/page.tsx diff --git a/app/(app)/books/ssr/page.tsx b/app/(app)/(reader)/books/ssr/page.tsx similarity index 100% rename from app/(app)/books/ssr/page.tsx rename to app/(app)/(reader)/books/ssr/page.tsx diff --git a/app/(app)/books/sva/[chapter]/page.tsx b/app/(app)/(reader)/books/sva/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/sva/[chapter]/page.tsx rename to app/(app)/(reader)/books/sva/[chapter]/page.tsx diff --git a/app/(app)/books/sva/page.tsx b/app/(app)/(reader)/books/sva/page.tsx similarity index 100% rename from app/(app)/books/sva/page.tsx rename to app/(app)/(reader)/books/sva/page.tsx diff --git a/app/(app)/books/tlc/[chapter]/page.tsx b/app/(app)/(reader)/books/tlc/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/tlc/[chapter]/page.tsx rename to app/(app)/(reader)/books/tlc/[chapter]/page.tsx diff --git a/app/(app)/books/tlc/page.tsx b/app/(app)/(reader)/books/tlc/page.tsx similarity index 100% rename from app/(app)/books/tlc/page.tsx rename to app/(app)/(reader)/books/tlc/page.tsx diff --git a/app/(app)/books/tlk/[chapter]/page.tsx b/app/(app)/(reader)/books/tlk/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/tlk/[chapter]/page.tsx rename to app/(app)/(reader)/books/tlk/[chapter]/page.tsx diff --git a/app/(app)/books/tlk/page.tsx b/app/(app)/(reader)/books/tlk/page.tsx similarity index 100% rename from app/(app)/books/tlk/page.tsx rename to app/(app)/(reader)/books/tlk/page.tsx diff --git a/app/(app)/books/tqk/[chapter]/page.tsx b/app/(app)/(reader)/books/tqk/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/tqk/[chapter]/page.tsx rename to app/(app)/(reader)/books/tqk/[chapter]/page.tsx diff --git a/app/(app)/books/tqk/page.tsx b/app/(app)/(reader)/books/tqk/page.tsx similarity index 100% rename from app/(app)/books/tqk/page.tsx rename to app/(app)/(reader)/books/tqk/page.tsx diff --git a/app/(app)/books/ttp/[chapter]/page.tsx b/app/(app)/(reader)/books/ttp/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/ttp/[chapter]/page.tsx rename to app/(app)/(reader)/books/ttp/[chapter]/page.tsx diff --git a/app/(app)/books/ttp/page.tsx b/app/(app)/(reader)/books/ttp/page.tsx similarity index 100% rename from app/(app)/books/ttp/page.tsx rename to app/(app)/(reader)/books/ttp/page.tsx diff --git a/app/(app)/books/va/[chapter]/page.tsx b/app/(app)/(reader)/books/va/[chapter]/page.tsx similarity index 100% rename from app/(app)/books/va/[chapter]/page.tsx rename to app/(app)/(reader)/books/va/[chapter]/page.tsx diff --git a/app/(app)/books/va/page.tsx b/app/(app)/(reader)/books/va/page.tsx similarity index 100% rename from app/(app)/books/va/page.tsx rename to app/(app)/(reader)/books/va/page.tsx diff --git a/app/(app)/disciples/disciples-browser.tsx b/app/(app)/(reader)/disciples/disciples-browser.tsx similarity index 100% rename from app/(app)/disciples/disciples-browser.tsx rename to app/(app)/(reader)/disciples/disciples-browser.tsx diff --git a/app/(app)/disciples/page.tsx b/app/(app)/(reader)/disciples/page.tsx similarity index 100% rename from app/(app)/disciples/page.tsx rename to app/(app)/(reader)/disciples/page.tsx diff --git a/app/(app)/essays/[slug]/page.tsx b/app/(app)/(reader)/essays/[slug]/page.tsx similarity index 100% rename from app/(app)/essays/[slug]/page.tsx rename to app/(app)/(reader)/essays/[slug]/page.tsx diff --git a/app/(app)/essays/essays-grid.tsx b/app/(app)/(reader)/essays/essays-grid.tsx similarity index 100% rename from app/(app)/essays/essays-grid.tsx rename to app/(app)/(reader)/essays/essays-grid.tsx diff --git a/app/(app)/essays/page.tsx b/app/(app)/(reader)/essays/page.tsx similarity index 100% rename from app/(app)/essays/page.tsx rename to app/(app)/(reader)/essays/page.tsx diff --git a/app/(app)/glossary/[letter]/page.tsx b/app/(app)/(reader)/glossary/[letter]/page.tsx similarity index 100% rename from app/(app)/glossary/[letter]/page.tsx rename to app/(app)/(reader)/glossary/[letter]/page.tsx diff --git a/app/(app)/glossary/glossary-client.tsx b/app/(app)/(reader)/glossary/glossary-client.tsx similarity index 100% rename from app/(app)/glossary/glossary-client.tsx rename to app/(app)/(reader)/glossary/glossary-client.tsx diff --git a/app/(app)/glossary/page.tsx b/app/(app)/(reader)/glossary/page.tsx similarity index 100% rename from app/(app)/glossary/page.tsx rename to app/(app)/(reader)/glossary/page.tsx diff --git a/app/(app)/journals/[slug]/[chapter]/page.tsx b/app/(app)/(reader)/journals/[slug]/[chapter]/page.tsx similarity index 100% rename from app/(app)/journals/[slug]/[chapter]/page.tsx rename to app/(app)/(reader)/journals/[slug]/[chapter]/page.tsx diff --git a/app/(app)/journals/[slug]/page.tsx b/app/(app)/(reader)/journals/[slug]/page.tsx similarity index 100% rename from app/(app)/journals/[slug]/page.tsx rename to app/(app)/(reader)/journals/[slug]/page.tsx diff --git a/app/(app)/journals/page.tsx b/app/(app)/(reader)/journals/page.tsx similarity index 100% rename from app/(app)/journals/page.tsx rename to app/(app)/(reader)/journals/page.tsx diff --git a/app/(app)/(reader)/layout.tsx b/app/(app)/(reader)/layout.tsx new file mode 100644 index 00000000..11c7e92a --- /dev/null +++ b/app/(app)/(reader)/layout.tsx @@ -0,0 +1,37 @@ +import { Suspense } from "react"; +import { TextSelectionToolbar } from "@/components/text-selection-toolbar"; +import { TextHighlighter } from "@/components/text-highlighter"; +import { PageBookmarks } from "@/components/social/page-bookmarks"; + +/** + * Reader layout — the annotation surface for the actual scripture library + * (books, letters, lectures, essays, glossary, journals, magazines, other + * documents, disciples). + * + * The text-selection toolbar and its two companions (the `?hl=` highlighter + * and the persistent in-text bookmark marks) only make sense over long-form + * scripture prose. Mounting them in this nested route group — rather than the + * parent `(app)` layout — keeps them off the home feed, search, profiles, + * settings, downloads, Kautilya, etc., so a stray text selection on chrome + * text no longer pops the toolbar. + * + * This is a route group: it adds NO path segment, so every URL underneath is + * unchanged (`/books/...`, `/letters/...`, …). All the outer `(app)` chrome + * (header, sidebar, footer, bottom nav) still wraps these pages. + */ +export default function ReaderLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <> + {children} + + + + + + + ); +} diff --git a/app/(app)/letters/[slug]/page.tsx b/app/(app)/(reader)/letters/[slug]/page.tsx similarity index 100% rename from app/(app)/letters/[slug]/page.tsx rename to app/(app)/(reader)/letters/[slug]/page.tsx diff --git a/app/(app)/letters/filter-year-browser.tsx b/app/(app)/(reader)/letters/filter-year-browser.tsx similarity index 100% rename from app/(app)/letters/filter-year-browser.tsx rename to app/(app)/(reader)/letters/filter-year-browser.tsx diff --git a/app/(app)/letters/letters-browser.tsx b/app/(app)/(reader)/letters/letters-browser.tsx similarity index 100% rename from app/(app)/letters/letters-browser.tsx rename to app/(app)/(reader)/letters/letters-browser.tsx diff --git a/app/(app)/letters/letters-nav.tsx b/app/(app)/(reader)/letters/letters-nav.tsx similarity index 100% rename from app/(app)/letters/letters-nav.tsx rename to app/(app)/(reader)/letters/letters-nav.tsx diff --git a/app/(app)/letters/location/[name]/page.tsx b/app/(app)/(reader)/letters/location/[name]/page.tsx similarity index 100% rename from app/(app)/letters/location/[name]/page.tsx rename to app/(app)/(reader)/letters/location/[name]/page.tsx diff --git a/app/(app)/letters/page.tsx b/app/(app)/(reader)/letters/page.tsx similarity index 100% rename from app/(app)/letters/page.tsx rename to app/(app)/(reader)/letters/page.tsx diff --git a/app/(app)/letters/recipient/[name]/page.tsx b/app/(app)/(reader)/letters/recipient/[name]/page.tsx similarity index 100% rename from app/(app)/letters/recipient/[name]/page.tsx rename to app/(app)/(reader)/letters/recipient/[name]/page.tsx diff --git a/app/(app)/letters/year/[year]/page.tsx b/app/(app)/(reader)/letters/year/[year]/page.tsx similarity index 100% rename from app/(app)/letters/year/[year]/page.tsx rename to app/(app)/(reader)/letters/year/[year]/page.tsx diff --git a/app/(app)/letters/year/[year]/year-month-browser.tsx b/app/(app)/(reader)/letters/year/[year]/year-month-browser.tsx similarity index 100% rename from app/(app)/letters/year/[year]/year-month-browser.tsx rename to app/(app)/(reader)/letters/year/[year]/year-month-browser.tsx diff --git a/app/(app)/magazines/[slug]/[issue]/page.tsx b/app/(app)/(reader)/magazines/[slug]/[issue]/page.tsx similarity index 100% rename from app/(app)/magazines/[slug]/[issue]/page.tsx rename to app/(app)/(reader)/magazines/[slug]/[issue]/page.tsx diff --git a/app/(app)/magazines/[slug]/page.tsx b/app/(app)/(reader)/magazines/[slug]/page.tsx similarity index 100% rename from app/(app)/magazines/[slug]/page.tsx rename to app/(app)/(reader)/magazines/[slug]/page.tsx diff --git a/app/(app)/magazines/page.tsx b/app/(app)/(reader)/magazines/page.tsx similarity index 100% rename from app/(app)/magazines/page.tsx rename to app/(app)/(reader)/magazines/page.tsx diff --git a/app/(app)/other-documents/[slug]/[chapter]/page.tsx b/app/(app)/(reader)/other-documents/[slug]/[chapter]/page.tsx similarity index 100% rename from app/(app)/other-documents/[slug]/[chapter]/page.tsx rename to app/(app)/(reader)/other-documents/[slug]/[chapter]/page.tsx diff --git a/app/(app)/other-documents/[slug]/page.tsx b/app/(app)/(reader)/other-documents/[slug]/page.tsx similarity index 100% rename from app/(app)/other-documents/[slug]/page.tsx rename to app/(app)/(reader)/other-documents/[slug]/page.tsx diff --git a/app/(app)/other-documents/other-documents-grid.tsx b/app/(app)/(reader)/other-documents/other-documents-grid.tsx similarity index 100% rename from app/(app)/other-documents/other-documents-grid.tsx rename to app/(app)/(reader)/other-documents/other-documents-grid.tsx diff --git a/app/(app)/other-documents/page.tsx b/app/(app)/(reader)/other-documents/page.tsx similarity index 100% rename from app/(app)/other-documents/page.tsx rename to app/(app)/(reader)/other-documents/page.tsx diff --git a/app/(app)/transcriptions/[slug]/page.tsx b/app/(app)/(reader)/transcriptions/[slug]/page.tsx similarity index 100% rename from app/(app)/transcriptions/[slug]/page.tsx rename to app/(app)/(reader)/transcriptions/[slug]/page.tsx diff --git a/app/(app)/transcriptions/[slug]/play-audio-button.tsx b/app/(app)/(reader)/transcriptions/[slug]/play-audio-button.tsx similarity index 100% rename from app/(app)/transcriptions/[slug]/play-audio-button.tsx rename to app/(app)/(reader)/transcriptions/[slug]/play-audio-button.tsx diff --git a/app/(app)/transcriptions/book/[book]/book-browser.tsx b/app/(app)/(reader)/transcriptions/book/[book]/book-browser.tsx similarity index 99% rename from app/(app)/transcriptions/book/[book]/book-browser.tsx rename to app/(app)/(reader)/transcriptions/book/[book]/book-browser.tsx index 1c1e61b3..3a8fbb4f 100644 --- a/app/(app)/transcriptions/book/[book]/book-browser.tsx +++ b/app/(app)/(reader)/transcriptions/book/[book]/book-browser.tsx @@ -8,7 +8,7 @@ import { Badge } from "@/components/ui/badge"; import { buttonVariants } from "@/components/ui/button"; import { cn } from "@/lib/utils"; import { ChevronLeft, ChevronRight, Menu, Calendar, Clock, MapPin } from "lucide-react"; -import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button"; +import { PlayAudioButton } from "@/app/(app)/(reader)/transcriptions/[slug]/play-audio-button"; import { parseChapter, parseVerse } from "@/lib/book-reference"; import { Drawer, diff --git a/app/(app)/transcriptions/book/[book]/page.tsx b/app/(app)/(reader)/transcriptions/book/[book]/page.tsx similarity index 100% rename from app/(app)/transcriptions/book/[book]/page.tsx rename to app/(app)/(reader)/transcriptions/book/[book]/page.tsx diff --git a/app/(app)/transcriptions/page.tsx b/app/(app)/(reader)/transcriptions/page.tsx similarity index 100% rename from app/(app)/transcriptions/page.tsx rename to app/(app)/(reader)/transcriptions/page.tsx diff --git a/app/(app)/transcriptions/place/[place]/page.tsx b/app/(app)/(reader)/transcriptions/place/[place]/page.tsx similarity index 97% rename from app/(app)/transcriptions/place/[place]/page.tsx rename to app/(app)/(reader)/transcriptions/place/[place]/page.tsx index f9624532..e9e5ee0e 100644 --- a/app/(app)/transcriptions/place/[place]/page.tsx +++ b/app/(app)/(reader)/transcriptions/place/[place]/page.tsx @@ -9,7 +9,7 @@ import { PageHeader } from "@/components/layout/page-header"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Calendar, Clock } from "lucide-react"; -import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button"; +import { PlayAudioButton } from "@/app/(app)/(reader)/transcriptions/[slug]/play-audio-button"; export async function generateMetadata({ params, diff --git a/app/(app)/transcriptions/transcriptions-browser.tsx b/app/(app)/(reader)/transcriptions/transcriptions-browser.tsx similarity index 100% rename from app/(app)/transcriptions/transcriptions-browser.tsx rename to app/(app)/(reader)/transcriptions/transcriptions-browser.tsx diff --git a/app/(app)/transcriptions/transcriptions-nav.tsx b/app/(app)/(reader)/transcriptions/transcriptions-nav.tsx similarity index 100% rename from app/(app)/transcriptions/transcriptions-nav.tsx rename to app/(app)/(reader)/transcriptions/transcriptions-nav.tsx diff --git a/app/(app)/transcriptions/type/[type]/page.tsx b/app/(app)/(reader)/transcriptions/type/[type]/page.tsx similarity index 100% rename from app/(app)/transcriptions/type/[type]/page.tsx rename to app/(app)/(reader)/transcriptions/type/[type]/page.tsx diff --git a/app/(app)/transcriptions/type/[type]/type-browser.tsx b/app/(app)/(reader)/transcriptions/type/[type]/type-browser.tsx similarity index 99% rename from app/(app)/transcriptions/type/[type]/type-browser.tsx rename to app/(app)/(reader)/transcriptions/type/[type]/type-browser.tsx index 1288a920..c3fa265b 100644 --- a/app/(app)/transcriptions/type/[type]/type-browser.tsx +++ b/app/(app)/(reader)/transcriptions/type/[type]/type-browser.tsx @@ -14,7 +14,7 @@ import { Clock, MapPin, } from "lucide-react"; -import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button"; +import { PlayAudioButton } from "@/app/(app)/(reader)/transcriptions/[slug]/play-audio-button"; import { Drawer, DrawerClose, diff --git a/app/(app)/transcriptions/year/[year]/page.tsx b/app/(app)/(reader)/transcriptions/year/[year]/page.tsx similarity index 100% rename from app/(app)/transcriptions/year/[year]/page.tsx rename to app/(app)/(reader)/transcriptions/year/[year]/page.tsx diff --git a/app/(app)/transcriptions/year/[year]/year-browser.tsx b/app/(app)/(reader)/transcriptions/year/[year]/year-browser.tsx similarity index 98% rename from app/(app)/transcriptions/year/[year]/year-browser.tsx rename to app/(app)/(reader)/transcriptions/year/[year]/year-browser.tsx index ab9d39a6..11f441e2 100644 --- a/app/(app)/transcriptions/year/[year]/year-browser.tsx +++ b/app/(app)/(reader)/transcriptions/year/[year]/year-browser.tsx @@ -7,7 +7,7 @@ import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; import { Calendar, ChevronRight, Clock, MapPin } from "lucide-react"; -import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button"; +import { PlayAudioButton } from "@/app/(app)/(reader)/transcriptions/[slug]/play-audio-button"; type TranscriptionItem = { slug: string; diff --git a/app/(app)/layout.tsx b/app/(app)/layout.tsx index d00777fa..5bdc4a95 100644 --- a/app/(app)/layout.tsx +++ b/app/(app)/layout.tsx @@ -1,12 +1,8 @@ -import { Suspense } from "react"; import { SiteHeader } from "@/components/layout/site-header"; import { SiteSidebar } from "@/components/layout/site-sidebar"; import { SiteBottomNav } from "@/components/layout/site-bottom-nav"; import { SiteFooter } from "@/components/layout/site-footer"; import { VerifyEmailBanner } from "@/components/auth/verify-email-banner"; -import { TextSelectionToolbar } from "@/components/text-selection-toolbar"; -import { TextHighlighter } from "@/components/text-highlighter"; -import { PageBookmarks } from "@/components/social/page-bookmarks"; import { PushPrompt } from "@/components/push/push-prompt"; /** @@ -17,6 +13,11 @@ import { PushPrompt } from "@/components/push/push-prompt"; * Sibling groups (e.g. `(legal)/`) opt out of this chrome by living in their * own route group with their own layout. Everything in the root layout * (providers, fonts, toaster, floating audio player) still wraps this layer. + * + * The text-selection toolbar and its highlight/bookmark companions live in + * the nested `(reader)/` group instead of here, so they only mount over the + * scripture library and never fire on chrome text (home, search, profile, + * settings, …). */ export default function AppLayout({ children, @@ -33,11 +34,6 @@ export default function AppLayout({ - - - - - ); diff --git a/app/(app)/settings/account/page.tsx b/app/(app)/settings/account/page.tsx new file mode 100644 index 00000000..0305ba10 --- /dev/null +++ b/app/(app)/settings/account/page.tsx @@ -0,0 +1,24 @@ +/** + * /settings/account — connected accounts, email, and password. + * + * Dedicated page for what used to be the "Authentication" section on the old + * /settings screen. All the interactive bits (OAuth link flows, the post-link + * result banner, add-email / change-password modals) live in the client + * AuthenticationSection component. + */ + +import { AuthenticationSection } from "@/components/settings/authentication-section"; +import { SettingsShell } from "@/components/settings/settings-ui"; + +export default function AccountSettingsPage() { + return ( + + + + ); +} diff --git a/app/(app)/settings/legal/page.tsx b/app/(app)/settings/legal/page.tsx new file mode 100644 index 00000000..a482e18b --- /dev/null +++ b/app/(app)/settings/legal/page.tsx @@ -0,0 +1,25 @@ +/** + * /settings/legal — links to the standalone legal documents. + * + * Dedicated page for what used to be the "Legal" section on the old /settings + * screen. Each row hard-links to its own top-level route (/terms, /privacy, + * /data-deletion). + */ + +import { NavList, NavRow, SettingsShell } from "@/components/settings/settings-ui"; + +export default function LegalSettingsPage() { + return ( + + + + + + + + ); +} diff --git a/app/(app)/settings/notifications/page.tsx b/app/(app)/settings/notifications/page.tsx new file mode 100644 index 00000000..32fccb18 --- /dev/null +++ b/app/(app)/settings/notifications/page.tsx @@ -0,0 +1,26 @@ +/** + * /settings/notifications — push opt-in + per-channel notification matrix. + * + * Dedicated page for what used to be the "Notifications" section on the old + * /settings screen. Push goes to every device where the toggle is on. + */ + +import { PushToggle } from "@/components/push/push-toggle"; +import { NotificationMatrix } from "@/components/notifications/notification-matrix"; +import { SettingsShell } from "@/components/settings/settings-ui"; + +export default function NotificationSettingsPage() { + return ( + +
+ + +
+
+ ); +} diff --git a/app/(app)/settings/page.tsx b/app/(app)/settings/page.tsx index 7bc27ba5..511b9fe6 100644 --- a/app/(app)/settings/page.tsx +++ b/app/(app)/settings/page.tsx @@ -1,99 +1,85 @@ "use client"; /** - * /settings — the top-level user-settings screen. + * /settings — the settings hub. * - * Lives outside the /u/me profile layout so it doesn't carry the - * ProfileHeader + tabs that frame the profile views. It sits directly - * under (app)/, which gives it the default site chrome (sidebar, header, - * etc.) without the profile-specific frame on top. + * A plain entry menu: one tappable row per setting type, each routing to its + * own dedicated page. This keeps the top level uncluttered — the actual + * controls live on the sub-pages (`/settings/reading`, `/settings/notifications`, + * `/settings/account`, `/settings/legal`) plus the existing + * `/settings/profile` editor. The "Offline" row is a direct shortcut to the + * device-local download library at `/downloads` (no intermediate page). * - * Sections are stacked plain — no per-section card chrome — so the page - * reads as a single ordered list of preferences rather than a dashboard - * of widgets. To add a new section, drop another
in the - * `
` below. + * Sign out is an action rather than a destination, so it sits on its own + * below the menu. */ -import { Suspense, useEffect, useState } from "react"; -import Link from "next/link"; -import { useRouter, useSearchParams } from "next/navigation"; +import { useState } from "react"; +import { useRouter } from "next/navigation"; import { - AlertCircle, - Check, - ChevronLeft, - ChevronRight, + Bell, + BookOpenText, + DownloadCloud, LogOut, + Scale, + ShieldCheck, + UserRound, } from "lucide-react"; -import { auth, AuthError } from "@/lib/auth"; import { useAuth } from "@/components/auth/auth-provider"; -import { AddEmailModal } from "@/components/auth/add-email-modal"; -import { ChangePasswordModal } from "@/components/auth/change-password-modal"; -import { ReadingSettings } from "@/components/reading-settings"; -import { PushToggle } from "@/components/push/push-toggle"; -import { NotificationMatrix } from "@/components/notifications/notification-matrix"; - -const BFF_BASE = - process.env.NEXT_PUBLIC_BFF_URL || "http://localhost:8080"; +import { + NavList, + NavRow, + SettingsShell, +} from "@/components/settings/settings-ui"; export default function SettingsPage() { return ( -
-
- - - Back to profile - -

- Settings -

-
+ + + } + label="Profile" + description="Name, handle, bio, and avatar." + /> + } + label="Reading preferences" + description="Theme, palette, font, and size — synced across devices." + /> + } + label="Notifications" + description="What reaches you, and on which channels." + /> + } + label="Offline downloads" + description="Manage pages and lectures saved to this device." + /> + } + label="Account & security" + description="Connected accounts, email, and password." + /> + } + label="Legal" + description="Terms, privacy, and data deletion." + /> + -
- -
- -
- - -
- -
-
    - -
-
- -
- {/* Suspense boundary required because LinkResult reads - useSearchParams — Next.js bails out of SSR otherwise. */} - - - - -
- -
- -
- - -
+ + ); } -function SignOutSection() { +function SignOut() { const { logOut } = useAuth(); const router = useRouter(); const [busy, setBusy] = useState(false); @@ -111,372 +97,17 @@ function SignOutSection() { } } - // Renders as a full-width clickable row matching LegalLink — same - // padding, same hover, with LogOut taking the slot ChevronRight uses - // on link rows. The whole row is the click target. return ( -
    -
  • - -
  • -
- ); -} - -function Section({ - title, - description, - children, -}: { - title: string; - description?: string; - children: React.ReactNode; -}) { - return ( -
-
-

- {title} -

- {description ? ( -

{description}

- ) : null} -
-
{children}
-
- ); -} - -// ─── Authentication ──────────────────────────────────────────────────── - -function AuthenticationSection() { - const { user } = useAuth(); - const [pwModalOpen, setPwModalOpen] = useState(false); - const [emailModalOpen, setEmailModalOpen] = useState(false); - const [linkedProviders, setLinkedProviders] = useState | null>( - null, - ); - const next = "/settings"; - - // Fetch linked providers on mount. A null set means "not yet loaded" — we - // render the rows as if nothing is connected until the answer comes back, - // which is fine because the only thing that changes after load is the - // "Connected" pill swapping in for "Connect". - useEffect(() => { - let cancelled = false; - auth - .linkedProviders() - .then(({ providers }) => { - if (!cancelled) setLinkedProviders(new Set(providers)); - }) - .catch(() => { - if (!cancelled) setLinkedProviders(new Set()); - }); - return () => { - cancelled = true; - }; - }, []); - - function startOAuth(provider: "facebook" | "google") { - // /link/start — links the provider to the currently-authenticated - // user (auth cookie travels on the top-level navigation). The login - // page still uses /start for sign-in / sign-up. - const u = new URL( - `${BFF_BASE}/api/v1/auth/oauth/${provider}/link/start`, - ); - u.searchParams.set("next", next); - return u.toString(); - } - - function providerAction(provider: "google" | "facebook") { - if (linkedProviders?.has(provider)) { - return ; - } - return ( - - Connect - - ); - } - - // Three contextual states for the email-related rows: - // • user has no email → "Add your email" row only - // • user has email, NOT verified → "Verify your email" row + - // "Change your password" row - // • user has email, verified → "Change your password" row - const needsEmail = !user?.email; - const needsVerify = !!user?.email && !user.email_verified_at; - - return ( - <> -
    - } - label="Connect your Google account" - action={providerAction("google")} - /> - } - label="Connect your Facebook account" - action={providerAction("facebook")} - /> - {needsEmail ? ( - setEmailModalOpen(true)} - className="text-sm font-medium text-primary hover:underline" - > - Add email - - } - /> - ) : ( - <> - {needsVerify ? : null} - setPwModalOpen(true)} - className="text-sm font-medium text-primary hover:underline" - > - Change - - } - /> - - )} -
- setPwModalOpen(false)} - /> - setEmailModalOpen(false)} - /> - - ); -} - -function VerifyEmailRow({ email }: { email: string }) { - const [busy, setBusy] = useState(false); - const [sent, setSent] = useState(false); - const [error, setError] = useState(null); - - async function onResend() { - setError(null); - setBusy(true); - try { - await auth.resendVerification(); - setSent(true); - } catch (err) { - setError( - err instanceof AuthError - ? err.message - : "Could not send verification email.", - ); - } finally { - setBusy(false); - } - } - - return ( - Sent - ) : ( - - ) - } - /> - ); -} - -function LinkResult() { - const params = useSearchParams(); - const router = useRouter(); - const raw = params.get("link"); - - // Strip the param from the URL so a refresh doesn't re-show the banner. - useEffect(() => { - if (!raw) return; - const next = new URLSearchParams(params.toString()); - next.delete("link"); - const q = next.toString(); - router.replace(q ? `/settings?${q}` : "/settings"); - }, [raw, params, router]); - - if (!raw) return null; - - // Format: ":" — `google:ok`, `facebook:claimed`, etc. - const [provider, status] = raw.split(":", 2); - const name = provider.charAt(0).toUpperCase() + provider.slice(1); - - if (status === "ok") { - return ( -

- - Connected your {name} account. -

- ); - } - if (status === "claimed") { - return ( -

- - That {name} account is already linked to another Prabhupāda Says - account. -

- ); - } - return ( -

- - Couldn't connect your {name} account. Please try again. -

- ); -} - -function ConnectedPill() { - return ( - - - Connected - - ); -} - -// ─── Legal ───────────────────────────────────────────────────────────── - -function LegalSection() { - return ( -
    - - - -
- ); -} - -function LegalLink({ href, label }: { href: string; label: string }) { - return ( -
  • - - - {label} - - - -
  • - ); -} - -// ─── Row helper ──────────────────────────────────────────────────────── - -function GoogleGlyph() { - return ( - - - - - - - ); -} - -function FacebookGlyph() { - return ( - - - - ); -} - -function Row({ - icon, - label, - description, - action, -}: { - icon?: React.ReactNode; - label: string; - description?: string; - action: React.ReactNode; -}) { - return ( -
  • -
    -
    - {icon ? {icon} : null} - {label} -
    - {description ? ( -

    {description}

    - ) : null} -
    -
    {action}
    -
  • + + {busy ? "Signing out…" : "Sign out from this device"} + + + ); } diff --git a/app/(app)/settings/reading/page.tsx b/app/(app)/settings/reading/page.tsx new file mode 100644 index 00000000..9fd8b9e4 --- /dev/null +++ b/app/(app)/settings/reading/page.tsx @@ -0,0 +1,23 @@ +/** + * /settings/reading — theme, palette, font, and size. + * + * A dedicated page for the reading preferences that used to be a section on + * the old monolithic /settings screen. ReadingSettings ("bare" variant) + * owns all the controls and their sync to the server. + */ + +import { ReadingSettings } from "@/components/reading-settings"; +import { SettingsShell } from "@/components/settings/settings-ui"; + +export default function ReadingSettingsPage() { + return ( + + + + ); +} diff --git a/components/settings/authentication-section.tsx b/components/settings/authentication-section.tsx new file mode 100644 index 00000000..0e55f683 --- /dev/null +++ b/components/settings/authentication-section.tsx @@ -0,0 +1,293 @@ +"use client"; + +/** + * AuthenticationSection — the connect-accounts / email / password controls + * for the /settings/account page. + * + * Split out of the old monolithic /settings page so account & security lives + * on its own dedicated screen. OAuth link flows return to /settings/account + * (via `next`), and the post-link banner (LinkResult) reads/clears the + * `?link=` param there. + */ + +import { Suspense, useEffect, useState } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { AlertCircle, Check } from "lucide-react"; +import { auth, AuthError } from "@/lib/auth"; +import { useAuth } from "@/components/auth/auth-provider"; +import { AddEmailModal } from "@/components/auth/add-email-modal"; +import { ChangePasswordModal } from "@/components/auth/change-password-modal"; +import { Row } from "@/components/settings/settings-ui"; + +const BFF_BASE = process.env.NEXT_PUBLIC_BFF_URL || "http://localhost:8080"; + +/** Where OAuth link flows and the result banner return to. */ +const RETURN_TO = "/settings/account"; + +export function AuthenticationSection() { + const { user } = useAuth(); + const [pwModalOpen, setPwModalOpen] = useState(false); + const [emailModalOpen, setEmailModalOpen] = useState(false); + const [linkedProviders, setLinkedProviders] = useState | null>( + null, + ); + + // Fetch linked providers on mount. A null set means "not yet loaded" — we + // render the rows as if nothing is connected until the answer comes back, + // which is fine because the only thing that changes after load is the + // "Connected" pill swapping in for "Connect". + useEffect(() => { + let cancelled = false; + auth + .linkedProviders() + .then(({ providers }) => { + if (!cancelled) setLinkedProviders(new Set(providers)); + }) + .catch(() => { + if (!cancelled) setLinkedProviders(new Set()); + }); + return () => { + cancelled = true; + }; + }, []); + + function startOAuth(provider: "facebook" | "google") { + // /link/start — links the provider to the currently-authenticated + // user (auth cookie travels on the top-level navigation). The login + // page still uses /start for sign-in / sign-up. + const u = new URL(`${BFF_BASE}/api/v1/auth/oauth/${provider}/link/start`); + u.searchParams.set("next", RETURN_TO); + return u.toString(); + } + + function providerAction(provider: "google" | "facebook") { + if (linkedProviders?.has(provider)) { + return ; + } + return ( + + Connect + + ); + } + + // Three contextual states for the email-related rows: + // • user has no email → "Add your email" row only + // • user has email, NOT verified → "Verify your email" row + + // "Change your password" row + // • user has email, verified → "Change your password" row + const needsEmail = !user?.email; + const needsVerify = !!user?.email && !user.email_verified_at; + + return ( + <> + {/* Suspense boundary required because LinkResult reads + useSearchParams — Next.js bails out of SSR otherwise. */} + + + +
      + } + label="Connect your Google account" + action={providerAction("google")} + /> + } + label="Connect your Facebook account" + action={providerAction("facebook")} + /> + {needsEmail ? ( + setEmailModalOpen(true)} + className="text-sm font-medium text-primary hover:underline" + > + Add email + + } + /> + ) : ( + <> + {needsVerify ? : null} + setPwModalOpen(true)} + className="text-sm font-medium text-primary hover:underline" + > + Change + + } + /> + + )} +
    + setPwModalOpen(false)} + /> + setEmailModalOpen(false)} + /> + + ); +} + +function VerifyEmailRow({ email }: { email: string }) { + const [busy, setBusy] = useState(false); + const [sent, setSent] = useState(false); + const [error, setError] = useState(null); + + async function onResend() { + setError(null); + setBusy(true); + try { + await auth.resendVerification(); + setSent(true); + } catch (err) { + setError( + err instanceof AuthError + ? err.message + : "Could not send verification email.", + ); + } finally { + setBusy(false); + } + } + + return ( + Sent + ) : ( + + ) + } + /> + ); +} + +function LinkResult() { + const params = useSearchParams(); + const router = useRouter(); + const raw = params.get("link"); + + // Strip the param from the URL so a refresh doesn't re-show the banner. + useEffect(() => { + if (!raw) return; + const next = new URLSearchParams(params.toString()); + next.delete("link"); + const q = next.toString(); + router.replace(q ? `${RETURN_TO}?${q}` : RETURN_TO); + }, [raw, params, router]); + + if (!raw) return null; + + // Format: ":" — `google:ok`, `facebook:claimed`, etc. + const [provider, status] = raw.split(":", 2); + const name = provider.charAt(0).toUpperCase() + provider.slice(1); + + if (status === "ok") { + return ( +

    + + Connected your {name} account. +

    + ); + } + if (status === "claimed") { + return ( +

    + + That {name} account is already linked to another Prabhupāda Says + account. +

    + ); + } + return ( +

    + + Couldn't connect your {name} account. Please try again. +

    + ); +} + +function ConnectedPill() { + return ( + + + Connected + + ); +} + +function GoogleGlyph() { + return ( + + + + + + + ); +} + +function FacebookGlyph() { + return ( + + + + ); +} diff --git a/components/settings/settings-ui.tsx b/components/settings/settings-ui.tsx new file mode 100644 index 00000000..dba33df0 --- /dev/null +++ b/components/settings/settings-ui.tsx @@ -0,0 +1,159 @@ +/** + * Shared settings chrome — the shell + row primitives used by the settings + * hub (`/settings`) and every dedicated setting page under it + * (`/settings/reading`, `/settings/notifications`, `/settings/account`, + * `/settings/legal`). + * + * All presentational, no hooks — safe to render from server components. The + * hub is a menu of `NavRow`s that route into these pages; each page frames + * its controls with `SettingsShell` (back link + title) so the header reads + * consistently everywhere. `Section` / `Row` are the plain, card-less + * building blocks the individual pages compose. + */ + +import Link from "next/link"; +import { ChevronLeft, ChevronRight } from "lucide-react"; + +/** Page frame: back link + title (+ optional description) over the content. */ +export function SettingsShell({ + backHref, + backLabel, + title, + description, + children, +}: { + backHref: string; + backLabel: string; + title: string; + description?: string; + children: React.ReactNode; +}) { + return ( +
    +
    + + + {backLabel} + +

    + {title} +

    + {description ? ( +

    {description}

    + ) : null} +
    + {children} +
    + ); +} + +/** A grouped, bordered list container — wrap `NavRow`s (hub, legal). */ +export function NavList({ children }: { children: React.ReactNode }) { + return ( +
      + {children} +
    + ); +} + +/** A tappable navigation row: icon + label/description on the left, a + * chevron on the right. The whole row is the link target. */ +export function NavRow({ + href, + icon, + label, + description, +}: { + href: string; + icon?: React.ReactNode; + label: string; + description?: string; +}) { + return ( +
  • + + {icon ? ( + + {icon} + + ) : null} + + + {label} + + {description ? ( + + {description} + + ) : null} + + + +
  • + ); +} + +/** A titled block within a settings page (used by pages that stack more + * than one control group). */ +export function Section({ + title, + description, + children, +}: { + title?: string; + description?: string; + children: React.ReactNode; +}) { + return ( +
    + {title || description ? ( +
    + {title ? ( +

    + {title} +

    + ) : null} + {description ? ( +

    {description}

    + ) : null} +
    + ) : null} +
    {children}
    +
    + ); +} + +/** A label/description + trailing action row (used inside control lists, + * e.g. the account page's connect / verify / change-password rows). */ +export function Row({ + icon, + label, + description, + action, +}: { + icon?: React.ReactNode; + label: string; + description?: string; + action: React.ReactNode; +}) { + return ( +
  • +
    +
    + {icon ? {icon} : null} + {label} +
    + {description ? ( +

    {description}

    + ) : null} +
    +
    {action}
    +
  • + ); +}