Something went wrong. Try again.
a tool for shared writing and social publishing
Something went wrong. Try again.
6.6 kB · 200 lines
TSX
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201import Link from "next/link";import { useLeafletPublicationData } from "components/PageSWRDataProvider";import { useRef } from "react";import { useReplicache } from "src/replicache";import { AsyncValueAutosizeTextarea } from "components/utils/AutosizeTextarea";import { Separator } from "components/Layout";import { AtUri } from "@atproto/syntax";import { PubLeafletDocument } from "lexicons/api";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 { useIdentityData } from "components/IdentityProvider";export const PublicationMetadata = () => { let { rep } = useReplicache(); let { data: pub } = useLeafletPublicationData(); let { identity } = useIdentityData(); let title = useSubscribe(rep, (tx) => tx.get<string>("publication_title")); let description = useSubscribe(rep, (tx) => tx.get<string>("publication_description"), ); let record = pub?.documents?.data as PubLeafletDocument.Record | null; let publishedAt = record?.publishedAt;
if (!pub || !pub.publications) return null;
if (typeof title !== "string") { title = pub?.title || ""; } if (typeof description !== "string") { description = pub?.description || ""; } return ( <div className={`flex flex-col px-3 sm:px-4 pb-5 sm:pt-3 pt-2`}> <div className="flex gap-2"> <Link href={ identity?.atp_did === pub.publications?.identity_did ? `${getBasePublicationURL(pub.publications)}/dashboard` : getPublicationURL(pub.publications) } className="leafletMetadata text-accent-contrast font-bold hover:no-underline" > {pub.publications?.name} </Link> <div className="font-bold text-tertiary px-1 text-sm flex place-items-center bg-border-light rounded-md "> Editor </div> </div> <TextField className="text-xl font-bold outline-hidden bg-transparent" value={title} onChange={async (newTitle) => { await rep?.mutate.updatePublicationDraft({ title: newTitle, description, }); }} placeholder="Untitled" /> <TextField placeholder="add an optional description..." className="italic text-secondary outline-hidden bg-transparent" value={description} onChange={async (newDescription) => { await rep?.mutate.updatePublicationDraft({ title, description: newDescription, }); }} /> {pub.doc ? ( <div className="flex flex-row items-center gap-2 pt-3"> <p className="text-sm text-tertiary"> Published {publishedAt && timeAgo(publishedAt)} </p> <Separator classname="h-4" /> <Link target="_blank" className="text-sm" href={`${getPublicationURL(pub.publications)}/${new AtUri(pub.doc).rkey}`} > View Post </Link> </div> ) : ( <p className="text-sm text-tertiary pt-2">Draft</p> )} </div> );};
export const TextField = ({ value, onChange, className, placeholder,}: { value: string; onChange: (v: string) => Promise<void>; className: string; placeholder: string;}) => { let { undoManager } = useReplicache(); let actionTimeout = useRef<number | null>(null); let { permissions } = useEntitySetContext(); let previousSelection = useRef<null | { start: number; end: number }>(null); let ref = useRef<HTMLTextAreaElement | null>(null); return ( <AsyncValueAutosizeTextarea ref={ref} disabled={!permissions.write} onSelect={(e) => { 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 } = useLeafletPublicationData(); let record = pub?.documents?.data as PubLeafletDocument.Record | null; let publishedAt = record?.publishedAt;
if (!pub || !pub.publications) return null;
return ( <div className={`flex flex-col px-3 sm:px-4 pb-5 sm:pt-3 pt-2`}> <div className="text-accent-contrast font-bold hover:no-underline"> {pub.publications?.name} </div>
<div className={`text-xl font-bold outline-hidden bg-transparent ${!pub.title && "text-tertiary italic"}`} > {pub.title ? pub.title : "Untitled"} </div> <div className="italic text-secondary outline-hidden bg-transparent"> {pub.description} </div>
{pub.doc ? ( <div className="flex flex-row items-center gap-2 pt-3"> <p className="text-sm text-tertiary"> Published {publishedAt && timeAgo(publishedAt)} </p> </div> ) : ( <p className="text-sm text-tertiary pt-2">Draft</p> )} </div> );};