···5import {
6 normalizePublicationRecord,
7 isLeafletPublication,
008 type NormalizedPublication,
9} from "src/utils/normalizeRecords";
10···44 const name = aturi.rkey || normalized?.name;
45 return `/lish/${aturi.host}/${encodeURIComponent(name || "")}`;
46}
0000000000000000000000000000000000000000000
···5import {
6 normalizePublicationRecord,
7 isLeafletPublication,
8+ hasLeafletContent,
9+ type NormalizedDocument,
10 type NormalizedPublication,
11} from "src/utils/normalizeRecords";
12···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+ */
56+export function getDocumentURL(
57+ doc: NormalizedDocument,
58+ docUri: string,
59+ publication?: PublicationInput | NormalizedPublication | null,
60+): string {
61+ const path = doc.path || "/" + new AtUri(docUri).rkey;
62+ const aturi = new AtUri(docUri);
63+64+ const isNormalized =
65+ !!publication &&
66+ (publication as NormalizedPublication).$type === "site.standard.publication";
67+ const normPub = isNormalized
68+ ? (publication as NormalizedPublication)
69+ : publication
70+ ? normalizePublicationRecord((publication as PublicationInput).record)
71+ : null;
72+ const pubInput = isNormalized ? null : (publication as PublicationInput | null);
73+74+ // Non-leaflet documents always use the full publication site URL
75+ if (doc.content && !hasLeafletContent(doc) && normPub?.url) {
76+ return normPub.url + path;
77+ }
78+79+ // For leaflet documents, use getPublicationURL (may return /lish/ internal paths)
80+ if (pubInput) {
81+ return getPublicationURL(pubInput) + path;
82+ }
83+84+ // When we only have a normalized publication, use its URL directly
85+ if (normPub?.url) {
86+ return normPub.url + path;
87+ }
88+89+ // Standalone document fallback
90+ return `/p/${aturi.host}${path}`;
91+}
+21-7
components/PageSWRDataProvider.tsx
···8import type { GetLeafletDataReturnType } from "app/api/rpc/[command]/get_leaflet_data";
9import { createContext, useContext, useMemo } from "react";
10import { getPublicationMetadataFromLeafletData } from "src/utils/getPublicationMetadataFromLeafletData";
11-import { getPublicationURL } from "app/lish/createPub/getPublicationURL";
12import { AtUri } from "@atproto/syntax";
13import {
14 normalizeDocumentRecord,
···119 // Compute the full post URL for sharing
120 let postShareLink: string | undefined;
121 if (publishedInPublication?.publications && publishedInPublication.documents) {
122- // Published in a publication - use publication URL + document rkey
123- const docUri = new AtUri(publishedInPublication.documents.uri);
124- postShareLink = `${getPublicationURL(publishedInPublication.publications)}/${docUri.rkey}`;
00000000125 } else if (publishedStandalone?.document) {
126- // Standalone published post - use /p/{did}/{rkey} format
127- const docUri = new AtUri(publishedStandalone.document);
128- postShareLink = `/p/${docUri.host}/${docUri.rkey}`;
000000129 }
130131 return {
···8import type { GetLeafletDataReturnType } from "app/api/rpc/[command]/get_leaflet_data";
9import { createContext, useContext, useMemo } from "react";
10import { getPublicationMetadataFromLeafletData } from "src/utils/getPublicationMetadataFromLeafletData";
11+import { getPublicationURL, getDocumentURL } from "app/lish/createPub/getPublicationURL";
12import { AtUri } from "@atproto/syntax";
13import {
14 normalizeDocumentRecord,
···119 // Compute the full post URL for sharing
120 let postShareLink: string | undefined;
121 if (publishedInPublication?.publications && publishedInPublication.documents) {
122+ const normalizedDoc = normalizeDocumentRecord(
123+ publishedInPublication.documents.data,
124+ publishedInPublication.documents.uri,
125+ );
126+ if (normalizedDoc) {
127+ postShareLink = getDocumentURL(
128+ normalizedDoc,
129+ publishedInPublication.documents.uri,
130+ publishedInPublication.publications,
131+ );
132+ }
133 } else if (publishedStandalone?.document) {
134+ const normalizedDoc = publishedStandalone.documents
135+ ? normalizeDocumentRecord(publishedStandalone.documents.data, publishedStandalone.document)
136+ : null;
137+ if (normalizedDoc) {
138+ postShareLink = getDocumentURL(normalizedDoc, publishedStandalone.document);
139+ } else {
140+ const docUri = new AtUri(publishedStandalone.document);
141+ postShareLink = `/p/${docUri.host}/${docUri.rkey}`;
142+ }
143 }
144145 return {
+2-3
components/PostListing.tsx
···18import { InteractionPreview } from "./InteractionsPreview";
19import { useLocalizedDate } from "src/hooks/useLocalizedDate";
20import { mergePreferences } from "src/utils/mergePreferences";
02122export const PostListing = (props: Post) => {
23 let pubRecord = props.publication?.pubRecord as
···60 let tags = (postRecord?.tags as string[] | undefined) || [];
6162 // For standalone posts, link directly to the document
63- let postHref = props.publication
64- ? `${props.publication.href}/${postUri.rkey}`
65- : `/p/${postUri.host}/${postUri.rkey}`;
6667 return (
68 <BaseThemeProvider {...theme} local>
···18import { InteractionPreview } from "./InteractionsPreview";
19import { useLocalizedDate } from "src/hooks/useLocalizedDate";
20import { mergePreferences } from "src/utils/mergePreferences";
21+import { getDocumentURL } from "app/lish/createPub/getPublicationURL";
2223export const PostListing = (props: Post) => {
24 let pubRecord = props.publication?.pubRecord as
···61 let tags = (postRecord?.tags as string[] | undefined) || [];
6263 // For standalone posts, link directly to the document
64+ let postHref = getDocumentURL(postRecord, props.documents.uri, pubRecord);
006566 return (
67 <BaseThemeProvider {...theme} local>