a tool for shared writing and social publishing
1import { AtUri } from "@atproto/syntax";
2import { PubLeafletPublication } from "lexicons/api";
3import { isProductionDomain } from "src/utils/isProductionDeployment";
4import { Json } from "supabase/database.types";
5import {
6 normalizePublicationRecord,
7 isLeafletPublication,
8 hasLeafletContent,
9 type NormalizedDocument,
10 type NormalizedPublication,
11} from "src/utils/normalizeRecords";
12
13type PublicationInput =
14 | { uri: string; record: Json | NormalizedPublication | null }
15 | { uri: string; record: unknown };
16
17/**
18 * Gets the public URL for a publication.
19 * Works with both pub.leaflet.publication and site.standard.publication records.
20 */
21export function getPublicationURL(pub: PublicationInput): string {
22 const normalized = normalizePublicationRecord(pub.record);
23
24 // If we have a normalized record with a URL (site.standard format), use it
25 if (normalized?.url && isProductionDomain()) {
26 return normalized.url;
27 }
28
29 // Fall back to checking raw record for legacy base_path
30 if (
31 isLeafletPublication(pub.record) &&
32 pub.record.base_path &&
33 isProductionDomain()
34 ) {
35 return `https://${pub.record.base_path}`;
36 }
37
38 return getBasePublicationURL(pub);
39}
40
41export function getBasePublicationURL(pub: PublicationInput): string {
42 const normalized = normalizePublicationRecord(pub.record);
43 const aturi = new AtUri(pub.uri);
44
45 //use rkey, fallback to name
46 const name = aturi.rkey || normalized?.name;
47 return `/lish/${aturi.host}/${encodeURIComponent(name || "")}`;
48}
49
50/**
51 * Gets the full URL for a document.
52 * Always appends the document's path property.
53 * For non-leaflet documents (content.$type !== "pub.leaflet.content"),
54 * always uses the full publication site URL, not internal /lish/ URLs.
55 */
56export function getDocumentURL(
57 doc: NormalizedDocument,
58 docUri: string,
59 publication?: PublicationInput | NormalizedPublication | null,
60): string {
61 let path = doc.path || "/" + new AtUri(docUri).rkey;
62 if (path[0] !== "/") path = "/" + path;
63 const aturi = new AtUri(docUri);
64
65 const isNormalized =
66 !!publication &&
67 (publication as NormalizedPublication).$type ===
68 "site.standard.publication";
69 const normPub = isNormalized
70 ? (publication as NormalizedPublication)
71 : publication
72 ? normalizePublicationRecord((publication as PublicationInput).record)
73 : null;
74 const pubInput = isNormalized
75 ? null
76 : (publication as PublicationInput | null);
77
78 // Non-leaflet documents always use the full publication site URL
79 if (doc.content && !hasLeafletContent(doc) && normPub?.url) {
80 return normPub.url + path;
81 }
82
83 // For leaflet documents, use getPublicationURL (may return /lish/ internal paths)
84 if (pubInput) {
85 return getPublicationURL(pubInput) + path;
86 }
87
88 // When we only have a normalized publication, use its URL directly
89 if (normPub?.url) {
90 return normPub.url + path;
91 }
92
93 // Standalone document fallback
94 return `/p/${aturi.host}${path}`;
95}