Outline: share as a read-only link
- Share on the Outline page opens a sheet: create / copy / turn off a read-only link, share the link with the system share sheet, or send the outline as text - The link has its own token (separate from the whole-study share link) and the public endpoint returns only the outlines, rebuilt field by field: no observations, interpretations, applications, scripts or word notes - Opening the link shows the outline(s) with no sign-in, a switcher between the whole-study and per-chunk outlines, and Print / PDF - New outline_share_token column (added automatically on start) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2ca79730f1
commit
d7a1601f80
11 files changed
+444
-10
No files matched your search
@@ -0,0 +1,62 @@
|
||||
// What someone sees when they open a read-only outline link: the outline(s), nothing editable, and a
|
||||
// way to print or save it.
|
||||
import { useEffect, useState } from 'react';
|
||||
import { getSharedOutline } from '../syncService.js';
|
||||
import { buildOutlineHtml } from '../utils/bibleUtils.jsx';
|
||||
import { printOutline } from '../utils/outlineExport.js';
|
||||
|
||||
export default function SharedOutlineView({ token }) {
|
||||
const [data, setData] = useState(null);
|
||||
const [error, setError] = useState('');
|
||||
const [scope, setScope] = useState('study');
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
getSharedOutline(token).then((result) => {
|
||||
if (!active) return;
|
||||
if (result.ok) { setData(result.data); setScope(result.data.outlines[0]?.scope ?? 'study'); } else setError(result.error ?? 'This outline link is invalid or has been turned off.');
|
||||
});
|
||||
return () => { active = false; };
|
||||
}, [token]);
|
||||
|
||||
useEffect(() => { if (data?.title) document.title = `${data.title} — Outline`; }, [data]);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex min-h-dvh items-center justify-center bg-slate-100 px-4">
|
||||
<div className="w-full max-w-sm rounded-3xl bg-white p-8 text-center shadow">
|
||||
<p className="text-sm uppercase tracking-[0.24em] text-slate-400">Bible Study Project</p>
|
||||
<p className="mt-4 text-sm text-rose-600">{error}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (!data) return <div className="flex min-h-dvh items-center justify-center bg-slate-100"><p className="text-sm text-slate-500">Loading outline…</p></div>;
|
||||
|
||||
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 outline = current ? { title, subtitle: current.scope === 'study' ? data.translation : `${current.label} · ${data.translation}`, tree: current.tree } : null;
|
||||
|
||||
return (
|
||||
<div className="min-h-dvh bg-slate-100">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3 border-b border-slate-200 bg-slate-900 px-4 py-3 text-xs text-slate-300">
|
||||
<span>🔗 Read-only outline — <a href="/" className="underline hover:text-white">go to Bible Study Project</a></span>
|
||||
<span className="flex items-center gap-3">
|
||||
{data.outlines.length > 1 && (
|
||||
<label className="flex items-center gap-2">Outline
|
||||
<select value={current?.scope} onChange={(e) => setScope(e.target.value)} aria-label="Outline" className="rounded-md border border-slate-600 bg-slate-800 px-2 py-1 text-slate-100">
|
||||
{data.outlines.map((o) => <option key={o.scope} value={o.scope}>{o.label}</option>)}
|
||||
</select>
|
||||
</label>
|
||||
)}
|
||||
{outline && <button type="button" onClick={() => printOutline(outline)} className="rounded-md border border-slate-500 px-3 py-1 text-slate-100 hover:bg-slate-700">Print / PDF</button>}
|
||||
</span>
|
||||
</div>
|
||||
{outline ? (
|
||||
<iframe title="Shared outline" srcDoc={buildOutlineHtml(outline)} sandbox="allow-popups" className="h-[calc(100dvh-3rem)] w-full border-0 bg-white" />
|
||||
) : (
|
||||
<p className="p-10 text-center text-sm text-slate-500">This study doesn’t have an outline yet.</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user