diff --git a/src/App.flow.test.jsx b/src/App.flow.test.jsx index 5346822..396ab37 100644 --- a/src/App.flow.test.jsx +++ b/src/App.flow.test.jsx @@ -350,12 +350,14 @@ describe('Opening a read-only outline link', () => { stubServer({ user: null }); window.history.replaceState({}, '', '/?outline=abc123'); render(); - const frame = await screen.findByTitle('Shared outline'); - expect(frame.getAttribute('srcdoc')).toContain('Appoint elders'); + expect(await screen.findByRole('heading', { name: 'Titus' })).toBeInTheDocument(); + expect(screen.getByText('Appoint elders')).toBeInTheDocument(); expect(screen.queryByRole('button', { name: /sign in/i })).not.toBeInTheDocument(); - await userEvent.setup().selectOptions(screen.getByRole('combobox', { name: 'Outline' }), 'c1'); - expect(screen.getByTitle('Shared outline').getAttribute('srcdoc')).toContain('Greeting'); - expect(screen.getByTitle('Shared outline').getAttribute('srcdoc')).toContain('Titus — Titus 1:1–4'); + expect(screen.queryByRole('textbox')).not.toBeInTheDocument(); // nothing to edit + await userEvent.setup().click(screen.getByRole('button', { name: 'Titus 1:1–4' })); + expect(await screen.findByRole('heading', { name: 'Titus — Titus 1:1–4' })).toBeInTheDocument(); + expect(screen.getByText('Greeting')).toBeInTheDocument(); + expect(screen.queryByText('Appoint elders')).not.toBeInTheDocument(); }); test('a turned-off or wrong link says so', async () => { diff --git a/src/components/OutlineDocument.jsx b/src/components/OutlineDocument.jsx new file mode 100644 index 0000000..ab4f7a3 --- /dev/null +++ b/src/components/OutlineDocument.jsx @@ -0,0 +1,55 @@ +// An outline laid out for reading (not editing): numbered points, small type tags, notes, verse text. Used +// by the read-only share page. Styled to match the printed / PDF page. +import { labelFor } from '../utils/outlineLabels.js'; +import { kindLabel } from '../utils/outlineLabels.js'; + +function Points({ nodes, depth }) { + return ( + + ); +} + +export default function OutlineDocument({ title, meta, tree }) { + return ( +
+

Outline

+

{title}

+ {meta &&

{meta}

} +
+ {tree.length > 0 ? :

Nothing in this outline yet.

} +

Bible Study Project

+
+ ); +} diff --git a/src/components/SharedOutlineView.jsx b/src/components/SharedOutlineView.jsx index 39f474c..e161353 100644 --- a/src/components/SharedOutlineView.jsx +++ b/src/components/SharedOutlineView.jsx @@ -1,9 +1,17 @@ -// What someone sees when they open a read-only outline link: the outline(s), nothing editable, and a -// way to print or save it. +// What someone sees when they open a read-only outline link: a calm page with the outline, a switcher +// when there is more than one, and Print / PDF. Nothing is editable. import { useEffect, useState } from 'react'; import { getSharedOutline } from '../syncService.js'; -import { buildOutlineHtml } from '../utils/bibleUtils.jsx'; +import { themeVars } from './reader/readerThemes.js'; +import OutlineDocument from './OutlineDocument.jsx'; import { printOutline } from '../utils/outlineExport.js'; +import { flatten } from '../utils/outline.js'; + +const pill = (active) => `shrink-0 rounded-full px-3.5 py-1.5 text-sm transition ${active ? 'bg-[var(--r-text)] text-[var(--r-bg)]' : 'text-[var(--r-sub)] hover:bg-black/5'}`; + +function Frame({ children }) { + return
{children}
; +} export default function SharedOutlineView({ token }) { const [data, setData] = useState(null); @@ -23,40 +31,40 @@ export default function SharedOutlineView({ token }) { if (error) { return ( -
-
-

Bible Study Project

-

{error}

+ +
+
+

Bible Study Project

+

{error}

+
-
+ ); } - if (!data) return

Loading outline…

; + if (!data) return

Loading outline…

; const current = data.outlines.find((o) => o.scope === scope) ?? data.outlines[0]; const title = current && current.scope !== 'study' ? `${data.title} — ${current.label}` : data.title; + const minutes = current ? flatten(current.tree).reduce((sum, { node }) => sum + (Number.isFinite(node.minutes) ? node.minutes : 0), 0) : 0; + const meta = current ? [current.scope === 'study' ? data.translation : `${current.label} · ${data.translation}`, minutes > 0 ? `About ${minutes} minute${minutes === 1 ? '' : 's'}` : ''].filter(Boolean).join(' · ') : ''; const outline = current ? { title, subtitle: current.scope === 'study' ? data.translation : `${current.label} · ${data.translation}`, tree: current.tree } : null; return ( -
-
- 🔗 Read-only outline — go to Bible Study Project - - {data.outlines.length > 1 && ( - - )} - {outline && } - -
- {outline ? ( -