import Link from "next/link"; import { useLeafletPublicationData } from "components/PageSWRDataProvider"; import { useRef, useState } from "react"; import { useReplicache } from "src/replicache"; import { AsyncValueAutosizeTextarea } from "components/utils/AutosizeTextarea"; import { Separator } from "components/Layout"; import { AtUri } from "@atproto/syntax"; import { getBasePublicationURL, getPublicationURL, } from "app/lish/createPub/getPublicationURL"; import { useSubscribe } from "src/replicache/useSubscribe"; import { useEntitySetContext } from "components/EntitySetProvider"; import { timeAgo } from "src/utils/timeAgo"; import { CommentTiny } from "components/Icons/CommentTiny"; import { QuoteTiny } from "components/Icons/QuoteTiny"; import { TagTiny } from "components/Icons/TagTiny"; import { Popover } from "components/Popover"; import { TagSelector } from "components/Tags"; import { useIdentityData } from "components/IdentityProvider"; import { PostHeaderLayout } from "app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader"; import { Backdater } from "./Backdater"; import { RecommendTinyEmpty } from "components/Icons/RecommendTiny"; import { mergePreferences } from "src/utils/mergePreferences"; export const PublicationMetadata = (props: { noInteractions?: boolean }) => { let { rep } = useReplicache(); let { data: pub, normalizedDocument, normalizedPublication, } = useLeafletPublicationData(); let { identity } = useIdentityData(); let title = useSubscribe(rep, (tx) => tx.get("publication_title")); let description = useSubscribe(rep, (tx) => tx.get("publication_description"), ); let postPreferences = useSubscribe(rep, (tx) => tx.get<{ showComments?: boolean; showMentions?: boolean; showRecommends?: boolean; } | null>("post_preferences"), ); let merged = mergePreferences( postPreferences || undefined, normalizedPublication?.preferences, ); let publishedAt = normalizedDocument?.publishedAt; if (!pub) return null; if (typeof title !== "string") { title = pub?.title || ""; } if (typeof description !== "string") { description = pub?.description || ""; } let tags = true; return ( {pub.publications && ( {pub.publications?.name} )}
DRAFT
} postTitle={ { await rep?.mutate.updatePublicationDraft({ title: newTitle, description, }); }} placeholder="Untitled" /> } postDescription={ { await rep?.mutate.updatePublicationDraft({ title, description: newDescription, }); }} /> } postInfo={ <> {pub.doc ? (

Published{" "} {publishedAt && ( )}

View
) : (

Draft

)} {!props.noInteractions && (
{merged.showRecommends !== false && (
)} {merged.showMentions !== false && (
)} {merged.showComments !== false && (
)} {tags && ( <> {merged.showRecommends !== false || merged.showMentions !== false || merged.showComments !== false ? ( ) : null} )}
)} } /> ); }; export const TextField = ({ value, onChange, className, placeholder, }: { value: string; onChange: (v: string) => Promise; className: string; placeholder: string; }) => { let { undoManager } = useReplicache(); let actionTimeout = useRef(null); let { permissions } = useEntitySetContext(); let previousSelection = useRef(null); let ref = useRef(null); return ( { let start = e.currentTarget.selectionStart, end = e.currentTarget.selectionEnd; previousSelection.current = { start, end }; }} className={className} value={value} onBlur={async () => { if (actionTimeout.current) { undoManager.endGroup(); window.clearTimeout(actionTimeout.current); actionTimeout.current = null; } }} onChange={async (e) => { let newValue = e.currentTarget.value; let oldValue = value; let start = e.currentTarget.selectionStart, end = e.currentTarget.selectionEnd; await onChange(e.currentTarget.value); if (actionTimeout.current) { window.clearTimeout(actionTimeout.current); } else { undoManager.startGroup(); } actionTimeout.current = window.setTimeout(() => { undoManager.endGroup(); actionTimeout.current = null; }, 200); let previousStart = previousSelection.current?.start || null, previousEnd = previousSelection.current?.end || null; undoManager.add({ redo: async () => { await onChange(newValue); ref.current?.setSelectionRange(start, end); ref.current?.focus(); }, undo: async () => { await onChange(oldValue); ref.current?.setSelectionRange(previousStart, previousEnd); ref.current?.focus(); }, }); }} placeholder={placeholder} /> ); }; export const PublicationMetadataPreview = () => { let { data: pub, normalizedDocument } = useLeafletPublicationData(); let publishedAt = normalizedDocument?.publishedAt; if (!pub) return null; return ( {pub.publications?.name} } postTitle={pub.title} postDescription={pub.description} postInfo={ pub.doc ? (

Published {publishedAt && timeAgo(publishedAt)}

) : (

Draft

) } /> ); }; export const AddTags = () => { let { data: pub, normalizedDocument } = useLeafletPublicationData(); let { rep } = useReplicache(); // Get tags from Replicache local state or published document let replicacheTags = useSubscribe(rep, (tx) => tx.get("publication_tags"), ); // Determine which tags to use - prioritize Replicache state let tags: string[] = []; if (Array.isArray(replicacheTags)) { tags = replicacheTags; } else if ( normalizedDocument?.tags && Array.isArray(normalizedDocument.tags) ) { tags = normalizedDocument.tags as string[]; } // Update tags in replicache local state const handleTagsChange = async (newTags: string[]) => { // Store tags in replicache for next publish/update await rep?.mutate.updatePublicationDraft({ tags: newTags, }); }; return ( {" "} {tags.length > 0 ? `${tags.length} Tag${tags.length === 1 ? "" : "s"}` : "Add Tags"} } > ); };