a tool for shared writing and social publishing
1import {
2 Fact,
3 ReplicacheMutators,
4 useEntity,
5 useReplicache,
6} from "src/replicache";
7import { Replicache } from "replicache";
8import { useUIState } from "src/useUIState";
9import { scanIndex } from "src/replicache/utils";
10import { getBlocksWithType } from "src/hooks/queries/useBlocks";
11import { focusBlock } from "src/utils/focusBlock";
12import { ButtonPrimary } from "components/Buttons";
13import { CloseTiny } from "components/Icons/CloseTiny";
14
15export const AreYouSure = (props: {
16 entityID: string[] | string;
17 onClick?: () => void;
18 closeAreYouSure: () => void;
19 type: Fact<"block/type">["data"]["value"] | undefined;
20 compact?: boolean;
21}) => {
22 let entities = [props.entityID].flat();
23 let { rep } = useReplicache();
24
25 return (
26 <div
27 className={`
28 w-full
29 flex items-center justify-center
30 ${
31 !props.compact &&
32 `bg-border-light border-2 border-border rounded-lg
33 ${
34 props.type === "card"
35 ? "h-[104px]"
36 : props.type === "mailbox"
37 ? "h-[92px]"
38 : "h-full"
39 }`
40 }`}
41 >
42 <div
43 className={`flex h-fit justify-center items-center font-bold text-secondary ${props.compact ? "flex-row gap-2 justify-between w-full " : "flex-col py-2 gap-1"}`}
44 >
45 <div className="text-center w-fit">
46 Delete{" "}
47 {entities.length > 1 ? (
48 "Blocks"
49 ) : props.type === "card" ? (
50 <span>Page</span>
51 ) : props.type === "mailbox" ? (
52 <span>Mailbox and Posts</span>
53 ) : (
54 <span>Block</span>
55 )}
56 ?{" "}
57 </div>
58 <div className="flex gap-2">
59 <ButtonPrimary
60 autoFocus
61 compact
62 onClick={async (e) => {
63 e.stopPropagation();
64 if (rep) await deleteBlock(entities, rep);
65 }}
66 >
67 Delete
68 </ButtonPrimary>
69 <button
70 className="text-accent-1"
71 onClick={() => props.closeAreYouSure()}
72 >
73 {props.compact ? (
74 <CloseTiny className="mx-2 shrink-0" />
75 ) : (
76 "Nevermind"
77 )}
78 </button>
79 </div>
80 </div>
81 </div>
82 );
83};
84
85export async function deleteBlock(
86 entities: string[],
87 rep: Replicache<ReplicacheMutators>,
88) {
89 // get what pagess we need to close as a result of deleting this block
90 let pagesToClose = [] as string[];
91 for (let entity of entities) {
92 let [type] = await rep.query((tx) =>
93 scanIndex(tx).eav(entity, "block/type"),
94 );
95 if (type.data.value === "card") {
96 let [childPages] = await rep?.query(
97 (tx) => scanIndex(tx).eav(entity, "block/card") || [],
98 );
99 pagesToClose = [childPages?.data.value];
100 }
101 if (type.data.value === "mailbox") {
102 let [archive] = await rep?.query(
103 (tx) => scanIndex(tx).eav(entity, "mailbox/archive") || [],
104 );
105 let [draft] = await rep?.query(
106 (tx) => scanIndex(tx).eav(entity, "mailbox/draft") || [],
107 );
108 pagesToClose = [archive?.data.value, draft?.data.value];
109 }
110 }
111
112 // the next and previous blocks in the block list
113 // if the focused thing is a page and not a block, return
114 let focusedBlock = useUIState.getState().focusedEntity;
115 let parent =
116 focusedBlock?.entityType === "page"
117 ? focusedBlock.entityID
118 : focusedBlock?.parent;
119
120 if (parent) {
121 let parentType = await rep?.query((tx) =>
122 scanIndex(tx).eav(parent, "page/type"),
123 );
124 if (parentType[0]?.data.value === "canvas") {
125 useUIState
126 .getState()
127 .setFocusedBlock({ entityType: "page", entityID: parent });
128 useUIState.getState().setSelectedBlocks([]);
129 } else {
130 let siblings =
131 (await rep?.query((tx) => getBlocksWithType(tx, parent))) || [];
132
133 let selectedBlocks = useUIState.getState().selectedBlocks;
134 let firstSelected = selectedBlocks[0];
135 let lastSelected = selectedBlocks[entities.length - 1];
136
137 let prevBlock =
138 siblings?.[
139 siblings.findIndex((s) => s.value === firstSelected?.value) - 1
140 ];
141 let prevBlockType = await rep?.query((tx) =>
142 scanIndex(tx).eav(prevBlock?.value, "block/type"),
143 );
144
145 let nextBlock =
146 siblings?.[
147 siblings.findIndex((s) => s.value === lastSelected.value) + 1
148 ];
149 let nextBlockType = await rep?.query((tx) =>
150 scanIndex(tx).eav(nextBlock?.value, "block/type"),
151 );
152
153 if (prevBlock) {
154 useUIState.getState().setSelectedBlock({
155 value: prevBlock.value,
156 parent: prevBlock.parent,
157 });
158
159 focusBlock(
160 {
161 value: prevBlock.value,
162 type: prevBlockType?.[0].data.value,
163 parent: prevBlock.parent,
164 },
165 { type: "end" },
166 );
167 } else {
168 useUIState.getState().setSelectedBlock({
169 value: nextBlock.value,
170 parent: nextBlock.parent,
171 });
172
173 focusBlock(
174 {
175 value: nextBlock.value,
176 type: nextBlockType?.[0]?.data.value,
177 parent: nextBlock.parent,
178 },
179 { type: "start" },
180 );
181 }
182 }
183 }
184
185 pagesToClose.forEach((page) => page && useUIState.getState().closePage(page));
186 await Promise.all(
187 entities.map((entity) =>
188 rep?.mutate.removeBlock({
189 blockEntity: entity,
190 }),
191 ),
192 );
193}