Something went wrong. Try again.
a tool for shared writing and social publishing
Something went wrong. Try again.
5.9 kB · 185 lines
TSX
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186import { useUIState } from "src/useUIState";import { BlockProps } from "./Block";import { useMemo } from "react";import { focusElement, AsyncValueInput } from "components/Input";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, PubLeafletDocument, PubLeafletPagesLinearDocument,} from "lexicons/api";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) => { let { data: publicationData } = 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 (!publicationData?.documents?.data) return false;
const docRecord = publicationData.documents .data as PubLeafletDocument.Record;
// Search through all pages and blocks to find if this poll entity has been published for (const page of docRecord.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; }, [publicationData, props.entityID]);
return ( <div className={`poll flex flex-col gap-2 p-3 w-full ${isSelected ? "block-border-selected " : "block-border"}`} style={{ backgroundColor: "color-mix(in oklab, rgb(var(--accent-1)), rgb(var(--bg-page)) 85%)", }} > <EditPollForPublication entityID={props.entityID} isPublished={isPublished} /> </div> );};
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> );};