Something went wrong. Try again.
a tool for shared writing and social publishing
Something went wrong. Try again.
6.0 kB · 191 lines
TSX
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192import { useUIState } from "src/useUIState";import { BlockLayout, BlockProps } from "./Block";import { useMemo } from "react";import { AsyncValueInput } from "components/Input";import { focusElement } from "src/utils/focusElement";import { useEntitySetContext } from "components/EntitySetProvider";import { useEntity, useReplicache } from "src/replicache";import { v7 } from "uuid";import { elementId } from "src/utils/elementId";import { CloseTiny } from "components/Icons/CloseTiny";import { useLeafletPublicationData } from "components/PageSWRDataProvider";import { PubLeafletBlocksPoll, PubLeafletPagesLinearDocument,} from "lexicons/api";import { getDocumentPages } from "src/utils/normalizeRecords";import { ids } from "lexicons/api/lexicons";
/** * PublicationPollBlock is used for editing polls in publication documents. * It allows adding/editing options when the poll hasn't been published yet, * but disables adding new options once the poll record exists (indicated by pollUri). */export const PublicationPollBlock = ( props: BlockProps & { areYouSure?: boolean; setAreYouSure?: (value: boolean) => void; },) => { let { data: publicationData, normalizedDocument } = useLeafletPublicationData(); let isSelected = useUIState((s) => s.selectedBlocks.find((b) => b.value === props.entityID), ); // Check if this poll has been published in a publication document const isPublished = useMemo(() => { if (!normalizedDocument) return false;
const pages = getDocumentPages(normalizedDocument); if (!pages) return false;
// Search through all pages and blocks to find if this poll entity has been published for (const page of pages) { if (page.$type === "pub.leaflet.pages.linearDocument") { const linearPage = page as PubLeafletPagesLinearDocument.Main; for (const blockWrapper of linearPage.blocks || []) { if (blockWrapper.block?.$type === ids.PubLeafletBlocksPoll) { const pollBlock = blockWrapper.block as PubLeafletBlocksPoll.Main; // Check if this poll's rkey matches our entity ID const rkey = pollBlock.pollRef.uri.split("/").pop(); if (rkey === props.entityID) { return true; } } } } } return false; }, [normalizedDocument, props.entityID]);
return ( <BlockLayout className="poll flex flex-col gap-2" hasBackground={"accent"} isSelected={!!isSelected} areYouSure={props.areYouSure} setAreYouSure={props.setAreYouSure} > <EditPollForPublication entityID={props.entityID} isPublished={isPublished} /> </BlockLayout> );};
const EditPollForPublication = (props: { entityID: string; isPublished: boolean;}) => { let pollOptions = useEntity(props.entityID, "poll/options"); let { rep } = useReplicache(); let permission_set = useEntitySetContext();
return ( <> {props.isPublished && ( <div className="text-sm italic text-tertiary"> This poll has been published. You can't edit the options. </div> )}
{pollOptions.length === 0 && !props.isPublished && ( <div className="text-center italic text-tertiary text-sm"> no options yet... </div> )}
{pollOptions.map((p) => ( <EditPollOptionForPublication key={p.id} entityID={p.data.value} pollEntity={props.entityID} disabled={props.isPublished} canDelete={!props.isPublished} /> ))}
{!props.isPublished && permission_set.permissions.write && ( <button className="pollAddOption w-fit flex gap-2 items-center justify-start text-sm text-accent-contrast" onClick={async () => { let pollOptionEntity = v7(); await rep?.mutate.addPollOption({ pollEntity: props.entityID, pollOptionEntity, pollOptionName: "", permission_set: permission_set.set, factID: v7(), });
focusElement( document.getElementById( elementId.block(props.entityID).pollInput(pollOptionEntity), ) as HTMLInputElement | null, ); }} > Add an Option </button> )} </> );};
const EditPollOptionForPublication = (props: { entityID: string; pollEntity: string; disabled: boolean; canDelete: boolean;}) => { let { rep } = useReplicache(); let { permissions } = useEntitySetContext(); let optionName = useEntity(props.entityID, "poll-option/name")?.data.value;
return ( <div className="flex gap-2 items-center"> <AsyncValueInput id={elementId.block(props.pollEntity).pollInput(props.entityID)} type="text" className="pollOptionInput w-full input-with-border" placeholder="Option here..." disabled={props.disabled || !permissions.write} value={optionName || ""} onChange={async (e) => { await rep?.mutate.assertFact([ { entity: props.entityID, attribute: "poll-option/name", data: { type: "string", value: e.currentTarget.value }, }, ]); }} onKeyDown={(e) => { if ( props.canDelete && e.key === "Backspace" && !e.currentTarget.value ) { e.preventDefault(); rep?.mutate.removePollOption({ optionEntity: props.entityID }); } }} />
{permissions.write && props.canDelete && ( <button tabIndex={-1} className="text-accent-contrast" onMouseDown={async () => { await rep?.mutate.removePollOption({ optionEntity: props.entityID, }); }} > <CloseTiny /> </button> )} </div> );};