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,66 @@
|
||||
// Share an outline: a read-only link anyone can open (no account), or the plain text.
|
||||
import { useState } from 'react';
|
||||
import Sheet from './reader/Sheet.jsx';
|
||||
import { useToast } from './Toasts.jsx';
|
||||
import { useOutlineShareLink } from '../hooks/useOutlineShareLink.js';
|
||||
import { shareOutline } from '../utils/outlineExport.js';
|
||||
|
||||
const btn = 'rounded-xl border border-[var(--r-line)] px-3.5 py-2 text-sm hover:bg-black/5 disabled:opacity-50';
|
||||
|
||||
export default function ShareOutlineSheet({ open, onClose, projectId, outline }) {
|
||||
const { toast } = useToast();
|
||||
const link = useOutlineShareLink(projectId, open);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const copyLink = async () => {
|
||||
try { await navigator.clipboard.writeText(link.url); setCopied(true); window.setTimeout(() => setCopied(false), 2000); } catch { toast('Couldn’t copy. Select the link and copy it.', { tone: 'error' }); }
|
||||
};
|
||||
// The phone/computer share sheet, with the link (falls back to copying it)
|
||||
const shareLink = async () => {
|
||||
if (navigator.share) {
|
||||
try { await navigator.share({ title: `${outline.title} — Outline`, url: link.url }); return; } catch (err) { if (err?.name === 'AbortError') return; }
|
||||
}
|
||||
copyLink();
|
||||
};
|
||||
const shareText = async () => {
|
||||
const result = await shareOutline(outline);
|
||||
if (result === 'copied') toast('Outline copied. Paste it into a message or email.');
|
||||
else if (result === 'failed') toast('Couldn’t share or copy the outline.', { tone: 'error' });
|
||||
};
|
||||
|
||||
return (
|
||||
<Sheet open={open} onClose={onClose} title="Share this outline">
|
||||
<h3 className="text-sm font-medium">Read-only link</h3>
|
||||
<p className="mb-3 mt-1 text-xs text-[var(--r-sub)]">
|
||||
Anyone with the link can read this study’s outlines (the whole-study one and each chunk’s), and print them. They can’t edit anything, and they can’t see your notes or scripts. No account needed.
|
||||
</p>
|
||||
{link.state === 'loading' && <p className="text-sm text-[var(--r-sub)]">Checking…</p>}
|
||||
{link.state === 'unavailable' && (
|
||||
<p className="rounded-xl border border-[var(--r-line)] px-3 py-2 text-sm text-[var(--r-sub)]">Links need your account and a connection to the server. You can still share the text below.</p>
|
||||
)}
|
||||
{link.state === 'ready' && (link.token ? (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<input readOnly aria-label="Outline link" value={link.url} onFocus={(e) => e.target.select()}
|
||||
className="min-w-0 flex-1 truncate rounded-lg border border-[var(--r-line)] bg-[var(--r-bg)] px-3 py-2 text-xs text-[var(--r-sub)]" />
|
||||
<button type="button" onClick={copyLink} className="shrink-0 rounded-lg bg-[var(--r-accent)] px-4 py-2 text-xs font-medium text-[var(--r-on)]">{copied ? 'Copied!' : 'Copy'}</button>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<button type="button" onClick={shareLink} className={btn}>Share link…</button>
|
||||
<button type="button" onClick={link.turnOff} disabled={link.busy} className="text-xs text-[var(--r-danger,#b42318)] hover:underline">{link.busy ? 'Turning off…' : 'Turn off link'}</button>
|
||||
</div>
|
||||
<p className="text-[11px] text-[var(--r-sub)]">People see the version that last synced to your account, and it updates as you edit. Turning the link off stops it working right away.</p>
|
||||
</div>
|
||||
) : (
|
||||
<button type="button" onClick={link.create} disabled={link.busy} className="rounded-xl bg-[var(--r-accent)] px-4 py-2 text-sm font-medium text-[var(--r-on)] hover:opacity-90 disabled:opacity-50">{link.busy ? 'Creating…' : 'Create read-only link'}</button>
|
||||
))}
|
||||
{link.error && <p role="alert" className="mt-2 text-xs text-[var(--r-danger,#b42318)]">{link.error}</p>}
|
||||
|
||||
<div className="mt-5 border-t border-[var(--r-line)] pt-4">
|
||||
<h3 className="text-sm font-medium">Or send the text</h3>
|
||||
<p className="mb-2 mt-1 text-xs text-[var(--r-sub)]">The outline you’re looking at, as plain text, to paste into a message or email.</p>
|
||||
<button type="button" onClick={shareText} className={btn}>Share as text</button>
|
||||
</div>
|
||||
</Sheet>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user