Skip to content

Commit 05799db

Browse files
os-zhuangclaude
andauthored
fix(share): correct public share link URL + ShareDialog audiences (#1784)
* fix(share): align ShareDialog audiences to framework contract Offer only link_only + signed_in (the audiences needing no extra input and allowed by ai_conversations); drop workspace/email_allowlist which the framework ShareLinkService rejects. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(share): point the public share URL at the console-mounted route SharedRecordPage lives under the console SPA basename (/_console/s/:token), so ShareDialog's default ${origin}/s/:token produced links that 404'd for recipients. AiChatPage now passes publicBaseUrl derived from the injected <base href> (matching App.tsx resolveBasename), so copied links resolve. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 5976ba3 commit 05799db

2 files changed

Lines changed: 24 additions & 3 deletions

File tree

packages/app-shell/src/console/ai/AiChatPage.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,23 @@ export function AiChatPage({ apiBase: apiBaseProp, defaultAgent: defaultAgentPro
496496
[apiBase],
497497
);
498498

499+
// Public share-link landing base. SharedRecordPage lives UNDER the console
500+
// SPA basename (e.g. `/_console/s/:token`), so the ShareDialog default of
501+
// `${origin}/s/:token` 404s for recipients. Derive the base from the SPA's
502+
// BASE_URL so the copyable link points at the actually-served route.
503+
const publicShareBase = useMemo(() => {
504+
if (typeof window === 'undefined' || typeof document === 'undefined') return undefined;
505+
// Mirror the console's own basename resolution (App.tsx resolveBasename):
506+
// the published SPA uses a relative Vite base, so the mount path is carried
507+
// by the injected `<base href>` tag, NOT import.meta.env.BASE_URL.
508+
let base = '';
509+
try {
510+
const href = document.querySelector('base')?.getAttribute('href');
511+
if (href) base = new URL(href, window.location.origin).pathname.replace(/\/+$/, '');
512+
} catch { /* no <base> → root-mounted SPA */ }
513+
return `${window.location.origin}${base}/s`;
514+
}, []);
515+
499516
// New-conversation race guard. On an IN-SPA `/ai?new=1` navigation the
500517
// URL-mirroring effect below fires in the SAME commit as the hook's effect,
501518
// with this render's (stale) `conversationId` still in its closure — the
@@ -631,6 +648,7 @@ export function AiChatPage({ apiBase: apiBaseProp, defaultAgent: defaultAgentPro
631648
recordId={conversationId}
632649
recordLabel="this conversation"
633650
apiBase={restApiBase}
651+
publicBaseUrl={publicShareBase}
634652
/>
635653
)}
636654
<div className="flex min-h-0 flex-1 w-full bg-muted/20">

packages/components/src/share/ShareDialog.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import { Badge } from '../ui/badge';
4141
import { cn } from '../lib/utils';
4242

4343
export type ShareLinkPermission = 'view' | 'comment' | 'edit';
44-
export type ShareLinkAudience = 'link_only' | 'signed_in' | 'workspace' | 'email_allowlist';
44+
export type ShareLinkAudience = 'public' | 'link_only' | 'signed_in' | 'email';
4545

4646
export interface ShareLink {
4747
id: string;
@@ -106,11 +106,14 @@ const EXPIRY_OPTIONS: { label: string; value: number | null }[] = [
106106
{ label: 'Never', value: null },
107107
];
108108

109+
// Audience values match the framework's ShareLinkAudience contract
110+
// (@objectstack/spec). Only the two that need no extra input are offered;
111+
// 'email' (allowlist) / 'public' require UI not yet built. The target
112+
// object's `publicSharing.allowedAudiences` further constrains this — the
113+
// server returns 422 AUDIENCE_NOT_ALLOWED if a value is not permitted.
109114
const AUDIENCE_OPTIONS: { value: ShareLinkAudience; label: string; help: string }[] = [
110115
{ value: 'link_only', label: 'Anyone with the link', help: 'No sign-in required' },
111116
{ value: 'signed_in', label: 'Signed-in users', help: 'Must be logged in to view' },
112-
{ value: 'workspace', label: 'Workspace members', help: 'Same tenant only' },
113-
{ value: 'email_allowlist', label: 'Specific emails', help: 'Restricted to listed addresses' },
114117
];
115118

116119
function buildPublicUrl(base: string | undefined, token: string): string {

0 commit comments

Comments
 (0)